mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 09:46:05 -04:00
Merge pull request #2898 from lightpanda-io/js_global_rework
refactor: Rework how v8::Globals are managed
This commit is contained in:
@@ -124,7 +124,7 @@ pub const DispatchDirectOptions = EventManagerBase.DispatchDirectOptions;
|
||||
|
||||
// Direct dispatch for non-DOM targets (Window, XHR, AbortSignal) or DOM nodes with
|
||||
// property handlers. No propagation - just calls the handler and registered listeners.
|
||||
// Handler can be: null, ?js.Function.Global, ?js.Function.Temp, or js.Function
|
||||
// Handler can be: null, ?js.Function.Global or js.Function
|
||||
pub fn dispatchDirect(self: *EventManager, target: *EventTarget, event: *Event, handler: anytype, comptime opts: DispatchDirectOptions) !void {
|
||||
const frame = self.frame;
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ pub const DispatchDirectOptions = struct {
|
||||
|
||||
/// Direct dispatch for non-DOM targets. No propagation - just calls the property
|
||||
/// handler and registered listeners. Caller is responsible for event ref counting.
|
||||
/// Handler can be: null, ?js.Function.Global, ?js.Function.Temp, or js.Function
|
||||
/// Handler can be: null, ?js.Function.Global or js.Function
|
||||
pub fn dispatchDirect(
|
||||
self: *EventManagerBase,
|
||||
arena: Allocator,
|
||||
@@ -359,7 +359,6 @@ fn getFunction(handler: anytype, local: *const js.Local) ?js.Function {
|
||||
}
|
||||
return switch (T) {
|
||||
js.Function => handler,
|
||||
js.Function.Temp => local.toLocal(handler),
|
||||
js.Function.Global => local.toLocal(handler),
|
||||
else => @compileError("handler must be null or \\??js.Function(\\.(Temp|Global))?"),
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const js = @import("js/js.zig");
|
||||
const v8 = js.v8;
|
||||
|
||||
const Frame = @import("Frame.zig");
|
||||
const Session = @import("Session.zig");
|
||||
@@ -81,11 +80,10 @@ identity: js.Identity = .{},
|
||||
// weak-callback safety.
|
||||
finalizer_callbacks: std.AutoHashMapUnmanaged(usize, *js.FinalizerCallback) = .empty,
|
||||
|
||||
// Tracked global v8 objects that need to be released when the Page tears down.
|
||||
globals: std.ArrayList(v8.Global) = .empty,
|
||||
|
||||
// Temporary v8 globals that can be released early. Key is global.data_ptr.
|
||||
temps: std.AutoHashMapUnmanaged(usize, v8.Global) = .empty,
|
||||
// Persisted v8 handles owned by this Page. Handles that outlive the Page are
|
||||
// reset on teardown; handles that can be released early are dropped
|
||||
// individually. See js.GlobalTracker.
|
||||
globals: js.GlobalTracker,
|
||||
|
||||
// Double buffered so that, as we process one list of queued navigations, new
|
||||
// entries are added to the separate buffer. Prevents endless navigation loops
|
||||
@@ -144,6 +142,7 @@ pub fn init(self: *Page, session: *Session, frame_id: u32) !void {
|
||||
.frame = undefined,
|
||||
.frame_arena = frame_arena,
|
||||
.factory = Factory.init(frame_arena),
|
||||
.globals = .init(session.browser.app.allocator),
|
||||
};
|
||||
self.queued_navigation = &self.queued_navigation_1;
|
||||
|
||||
@@ -180,20 +179,7 @@ pub fn deinit(self: *Page) void {
|
||||
self.finalizer_callbacks = .empty;
|
||||
}
|
||||
|
||||
{
|
||||
for (self.globals.items) |*global| {
|
||||
v8.v8__Global__Reset(global);
|
||||
}
|
||||
self.globals = .empty;
|
||||
}
|
||||
|
||||
{
|
||||
var it = self.temps.valueIterator();
|
||||
while (it.next()) |global| {
|
||||
v8.v8__Global__Reset(global);
|
||||
}
|
||||
self.temps = .empty;
|
||||
}
|
||||
self.globals.deinit();
|
||||
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(self.origins.count() == 0);
|
||||
|
||||
@@ -277,14 +277,6 @@ pub fn setOrigin(self: *Context, key: ?[]const u8) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trackGlobal(self: *Context, global: v8.Global) !void {
|
||||
return self.page.globals.append(self.page.frame_arena, global);
|
||||
}
|
||||
|
||||
pub fn trackTemp(self: *Context, global: v8.Global) !void {
|
||||
return self.page.temps.put(self.page.frame_arena, global.data_ptr, global);
|
||||
}
|
||||
|
||||
pub const IdentityResult = struct {
|
||||
value_ptr: *v8.Global,
|
||||
found_existing: bool,
|
||||
@@ -1134,7 +1126,7 @@ fn enqueueMicrotask(self: *Context, callback: anytype) void {
|
||||
// this should be safe (I think). In whatever HandleScope a microtask is enqueued,
|
||||
// PerformCheckpoint should be run. So the v8::Local<v8::Function> should remain
|
||||
// valid. If we have problems with this, a simple solution is to provide a Zig
|
||||
// wrapper for these callbacks which references a js.Function.Temp, on callback
|
||||
// wrapper for these callbacks which references a js.Function, on callback
|
||||
// it executes the function and then releases the global.
|
||||
pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
|
||||
// Use context-specific microtask queue instead of isolate queue
|
||||
|
||||
@@ -227,29 +227,7 @@ pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
|
||||
}
|
||||
|
||||
pub fn persist(self: *const Function) !Global {
|
||||
return self._persist(true);
|
||||
}
|
||||
|
||||
pub fn temp(self: *const Function) !Temp {
|
||||
return self._persist(false);
|
||||
}
|
||||
|
||||
fn _persist(self: *const Function, comptime is_global: bool) !(if (is_global) Global else Temp) {
|
||||
var ctx = self.local.ctx;
|
||||
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
if (comptime is_global) {
|
||||
try ctx.trackGlobal(global);
|
||||
return .{ .handle = global, .temps = {} };
|
||||
}
|
||||
try ctx.trackTemp(global);
|
||||
return .{ .handle = global, .temps = &ctx.page.temps };
|
||||
}
|
||||
|
||||
pub fn tempWithThis(self: *const Function, value: anytype) !Temp {
|
||||
const with_this = try self.withThis(value);
|
||||
return with_this.temp();
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
pub fn persistWithThis(self: *const Function, value: anytype) !Global {
|
||||
@@ -257,41 +235,23 @@ pub fn persistWithThis(self: *const Function, value: anytype) !Global {
|
||||
return with_this.persist();
|
||||
}
|
||||
|
||||
pub const Temp = G(.temp);
|
||||
pub const Global = G(.global);
|
||||
// A cheap, copyable handle to a persisted function. See js.GlobalSlot.
|
||||
pub const Global = struct {
|
||||
slot: *js.GlobalSlot,
|
||||
|
||||
const GlobalType = enum(u8) {
|
||||
temp,
|
||||
global,
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
pub const release = deinit;
|
||||
|
||||
pub fn local(self: Global, l: *const js.Local) Function {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.slot.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isEqual(self: Global, other: Function) bool {
|
||||
return v8.v8__Global__IsEqual(&self.slot.handle, other.handle);
|
||||
}
|
||||
};
|
||||
|
||||
fn G(comptime global_type: GlobalType) type {
|
||||
return struct {
|
||||
handle: v8.Global,
|
||||
temps: if (global_type == .temp) *std.AutoHashMapUnmanaged(usize, v8.Global) else void,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
|
||||
pub fn local(self: *const Self, l: *const js.Local) Function {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isEqual(self: *const Self, other: Function) bool {
|
||||
return v8.v8__Global__IsEqual(&self.handle, other.handle);
|
||||
}
|
||||
|
||||
pub fn release(self: *const Self) void {
|
||||
if (self.temps.fetchRemove(self.handle.data_ptr)) |kv| {
|
||||
var g = kv.value;
|
||||
v8.v8__Global__Reset(&g);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -275,7 +275,10 @@ pub fn mapZigInstanceToJs(self: *const Local, js_obj_handle: ?*const v8.Object,
|
||||
const gop = try ctx.addIdentity(resolved_ptr_id);
|
||||
if (gop.found_existing) {
|
||||
// we've seen this instance before, return the same object
|
||||
return (js.Object.Global{ .handle = gop.value_ptr.* }).local(self);
|
||||
return .{
|
||||
.local = self,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(gop.value_ptr, self.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
const isolate = self.isolate;
|
||||
@@ -468,12 +471,9 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
|
||||
|
||||
inline
|
||||
js.Function.Global,
|
||||
js.Function.Temp,
|
||||
js.Value.Global,
|
||||
js.Value.Temp,
|
||||
js.Object.Global,
|
||||
js.Promise.Global,
|
||||
js.Promise.Temp,
|
||||
js.PromiseResolver.Global,
|
||||
js.Module.Global => return .{ .local = self, .handle = @ptrCast(value.local(self).handle) },
|
||||
|
||||
@@ -743,14 +743,13 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T {
|
||||
// probeJsValueToZig. Avoids having to duplicate this logic when probing.
|
||||
fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
||||
return switch (T) {
|
||||
js.Function, js.Function.Global, js.Function.Temp => {
|
||||
js.Function, js.Function.Global => {
|
||||
if (!js_val.isFunction()) {
|
||||
return null;
|
||||
}
|
||||
const js_func = js.Function{ .local = self, .handle = @ptrCast(js_val.handle) };
|
||||
return switch (T) {
|
||||
js.Function => js_func,
|
||||
js.Function.Temp => try js_func.temp(),
|
||||
js.Function.Global => try js_func.persist(),
|
||||
else => unreachable,
|
||||
};
|
||||
@@ -767,7 +766,6 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
||||
},
|
||||
js.Value => js_val,
|
||||
js.Value.Global => return try js_val.persist(),
|
||||
js.Value.Temp => return try js_val.temp(),
|
||||
js.Object => {
|
||||
if (!js_val.isObject()) {
|
||||
return null;
|
||||
@@ -788,7 +786,7 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
||||
return try obj.persist();
|
||||
},
|
||||
|
||||
js.Promise.Global, js.Promise.Temp => {
|
||||
js.Promise.Global => {
|
||||
if (!js_val.isPromise()) {
|
||||
return null;
|
||||
}
|
||||
@@ -796,11 +794,7 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
||||
.local = self,
|
||||
.handle = @ptrCast(js_val.handle),
|
||||
};
|
||||
return switch (T) {
|
||||
js.Promise.Temp => try js_promise.temp(),
|
||||
js.Promise.Global => try js_promise.persist(),
|
||||
else => unreachable,
|
||||
};
|
||||
return try js_promise.persist();
|
||||
},
|
||||
js.String => return js_val.isString(),
|
||||
js.String.OneByte => {
|
||||
|
||||
@@ -92,14 +92,7 @@ pub fn format(self: Object, writer: *std.Io.Writer) !void {
|
||||
}
|
||||
|
||||
pub fn persist(self: Object) !Global {
|
||||
var ctx = self.local.ctx;
|
||||
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
|
||||
try ctx.trackGlobal(global);
|
||||
|
||||
return .{ .handle = global };
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
pub fn getFunction(self: Object, name: []const u8) !?js.Function {
|
||||
@@ -171,21 +164,24 @@ pub fn toZig(self: Object, comptime T: type) !T {
|
||||
}
|
||||
|
||||
pub const Global = struct {
|
||||
handle: v8.Global,
|
||||
slot: *js.GlobalSlot,
|
||||
|
||||
pub fn deinit(self: *Global) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
|
||||
pub fn local(self: *const Global, l: *const js.Local) Object {
|
||||
// TODO: deprecated. @GlobalSlot
|
||||
pub const release = deinit;
|
||||
|
||||
pub fn local(self: Global, l: *const js.Local) Object {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.slot.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isEqual(self: *const Global, other: Object) bool {
|
||||
return v8.v8__Global__IsEqual(&self.handle, other.handle);
|
||||
pub fn isEqual(self: Global, other: Object) bool {
|
||||
return v8.v8__Global__IsEqual(&self.slot.handle, other.handle);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -74,57 +74,21 @@ pub fn markAsHandled(self: Promise) void {
|
||||
}
|
||||
|
||||
pub fn persist(self: Promise) !Global {
|
||||
return self._persist(true);
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
pub fn temp(self: Promise) !Temp {
|
||||
return self._persist(false);
|
||||
}
|
||||
pub const Global = struct {
|
||||
slot: *js.GlobalSlot,
|
||||
|
||||
fn _persist(self: *const Promise, comptime is_global: bool) !(if (is_global) Global else Temp) {
|
||||
var ctx = self.local.ctx;
|
||||
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
if (comptime is_global) {
|
||||
try ctx.trackGlobal(global);
|
||||
return .{ .handle = global, .temps = {} };
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
try ctx.trackTemp(global);
|
||||
return .{ .handle = global, .temps = &ctx.page.temps };
|
||||
}
|
||||
pub const release = deinit;
|
||||
|
||||
pub const Temp = G(.temp);
|
||||
pub const Global = G(.global);
|
||||
|
||||
const GlobalType = enum(u8) {
|
||||
temp,
|
||||
global,
|
||||
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)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fn G(comptime global_type: GlobalType) type {
|
||||
return struct {
|
||||
handle: v8.Global,
|
||||
temps: if (global_type == .temp) *std.AutoHashMapUnmanaged(usize, v8.Global) else void,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
|
||||
pub fn local(self: *const Self, l: *const js.Local) Promise {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn release(self: *const Self) void {
|
||||
if (self.temps.fetchRemove(self.handle.data_ptr)) |kv| {
|
||||
var g = kv.value;
|
||||
v8.v8__Global__Reset(&g);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -118,24 +118,22 @@ fn _reject(self: PromiseResolver, value: anytype) !void {
|
||||
}
|
||||
|
||||
pub fn persist(self: PromiseResolver) !Global {
|
||||
var ctx = self.local.ctx;
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
try ctx.trackGlobal(global);
|
||||
return .{ .handle = global };
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
pub const Global = struct {
|
||||
handle: v8.Global,
|
||||
slot: *js.GlobalSlot,
|
||||
|
||||
pub fn deinit(self: *Global) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
|
||||
pub fn local(self: *const Global, l: *const js.Local) PromiseResolver {
|
||||
pub const release = deinit;
|
||||
|
||||
pub fn local(self: Global, l: *const js.Local) PromiseResolver {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.slot.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -622,34 +622,17 @@ const CloneDelegate = struct {
|
||||
};
|
||||
|
||||
pub fn persist(self: Value) !Global {
|
||||
return self._persist(true);
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
pub fn temp(self: Value) !Temp {
|
||||
return self._persist(false);
|
||||
}
|
||||
|
||||
// Like persist(), but not tracked on the context: the caller owns the handle
|
||||
// and must deinit (Reset) it. A reset is only idempotent through the same
|
||||
// instance — copies alias one v8 slot, so keep a single canonical instance and
|
||||
// reset through it.
|
||||
pub fn bare(self: Value) BareGlobal {
|
||||
// like persist, but not tracked by the page. Caller takes responsibility for
|
||||
// resetting and freeing the allocation.
|
||||
pub fn persistBare(self: Value, arena: std.mem.Allocator) !*js.GlobalSlot {
|
||||
const slot = try arena.create(js.GlobalSlot);
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(self.local.ctx.isolate.handle, self.handle, &global);
|
||||
return .{ .handle = global, .temps = {} };
|
||||
}
|
||||
|
||||
fn _persist(self: *const Value, comptime is_global: bool) !(if (is_global) Global else Temp) {
|
||||
var ctx = self.local.ctx;
|
||||
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
if (comptime is_global) {
|
||||
try ctx.trackGlobal(global);
|
||||
return .{ .handle = global, .temps = {} };
|
||||
}
|
||||
try ctx.trackTemp(global);
|
||||
return .{ .handle = global, .temps = &ctx.page.temps };
|
||||
slot.* = .{ .handle = global, .tracker = null, .gindex = undefined };
|
||||
return slot;
|
||||
}
|
||||
|
||||
pub fn toZig(self: Value, comptime T: type) !T {
|
||||
@@ -696,48 +679,71 @@ pub fn format(self: Value, writer: *std.Io.Writer) !void {
|
||||
return js_str.format(writer);
|
||||
}
|
||||
|
||||
pub const Temp = G(.temp);
|
||||
pub const Global = G(.global);
|
||||
pub const BareGlobal = G(.bare);
|
||||
// Copyable handle to our v8::Global wrapper so that releasing a copy resets
|
||||
// the underlying v8::Global
|
||||
pub const Global = struct {
|
||||
slot: *js.GlobalSlot,
|
||||
|
||||
const GlobalType = enum(u8) {
|
||||
temp,
|
||||
global,
|
||||
bare,
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
pub const release = deinit;
|
||||
|
||||
pub fn local(self: Global, l: *const js.Local) Value {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.slot.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isEqual(self: Global, other: Value) bool {
|
||||
return v8.v8__Global__IsEqual(&self.slot.handle, other.handle);
|
||||
}
|
||||
};
|
||||
|
||||
fn G(comptime global_type: GlobalType) type {
|
||||
return struct {
|
||||
handle: v8.Global,
|
||||
temps: if (global_type == .temp) *std.AutoHashMapUnmanaged(usize, v8.Global) else void,
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Value: persisted handle early-release swap-removes and fixes up indices" {
|
||||
const frame = try testing.createFrame();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
const Self = @This();
|
||||
var ls: js.Local.Scope = undefined;
|
||||
frame.js.localScope(&ls);
|
||||
defer ls.deinit();
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
const tracker = &frame.js.page.globals;
|
||||
const base = tracker.list.items.len;
|
||||
|
||||
pub fn local(self: *const Self, l: *const js.Local) Value {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
var a = try (try ls.local.exec("({a:1})", null)).persist();
|
||||
var b = try (try ls.local.exec("({b:2})", null)).persist();
|
||||
var c = try (try ls.local.exec("({c:3})", null)).persist();
|
||||
|
||||
pub fn isEqual(self: *const Self, other: Value) bool {
|
||||
return v8.v8__Global__IsEqual(&self.handle, other.handle);
|
||||
}
|
||||
try testing.expectEqual(base + 3, tracker.list.items.len);
|
||||
try testing.expectEqual(base + 0, a.slot.gindex);
|
||||
try testing.expectEqual(base + 1, b.slot.gindex);
|
||||
try testing.expectEqual(base + 2, c.slot.gindex);
|
||||
|
||||
pub fn release(self: *const Self) void {
|
||||
if (self.temps.fetchRemove(self.handle.data_ptr)) |kv| {
|
||||
var g = kv.value;
|
||||
v8.v8__Global__Reset(&g);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Release the middle one: the last live slot (c) must move into b's spot and
|
||||
// have its stored index rewritten to match, or a later release corrupts.
|
||||
b.deinit();
|
||||
try testing.expectEqual(base + 2, tracker.list.items.len);
|
||||
try testing.expectEqual(base + 1, c.slot.gindex);
|
||||
try testing.expectEqual(c.slot, tracker.list.items[base + 1]);
|
||||
try testing.expectEqual(a.slot, tracker.list.items[base + 0]);
|
||||
|
||||
// a and c are still usable (right handle, not b's).
|
||||
try testing.expect(a.local(&ls.local).isObject());
|
||||
try testing.expect(c.local(&ls.local).isObject());
|
||||
|
||||
// Release the remaining two via the moved indices — must not corrupt.
|
||||
a.deinit();
|
||||
try testing.expectEqual(base + 1, tracker.list.items.len);
|
||||
try testing.expectEqual(base + 0, c.slot.gindex);
|
||||
try testing.expectEqual(c.slot, tracker.list.items[base + 0]);
|
||||
|
||||
c.deinit();
|
||||
try testing.expectEqual(base, tracker.list.items.len);
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Value: jsonStringify maps unserializable JS values to null" {
|
||||
const frame = try testing.createFrame();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
@@ -67,6 +67,84 @@ pub fn Bridge(comptime T: type) type {
|
||||
return bridge.Builder(T);
|
||||
}
|
||||
|
||||
// Our wrapper around a v8::Global designed to be tracked (in a GlobalTracker).
|
||||
pub const GlobalSlot = struct {
|
||||
handle: v8.Global,
|
||||
tracker: ?*GlobalTracker, // null for Bare globals (see IndexedDB)
|
||||
gindex: u32, // position in GlobalTracker, used to efficiently remove + reuse
|
||||
|
||||
pub fn reset(self: *GlobalSlot) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
}
|
||||
|
||||
// Eager free: reset the handle and, if page-tracked, drop the slot from the
|
||||
// tracker and return it to the pool. Idempotent for bare slots.
|
||||
pub fn release(self: *GlobalSlot) void {
|
||||
self.reset();
|
||||
if (self.tracker) |t| {
|
||||
t.untrack(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local(self: *const GlobalSlot, l: *const Local) Value {
|
||||
return .{
|
||||
.local = l,
|
||||
.handle = @ptrCast(v8.v8__Global__Get(&self.handle, l.isolate.handle)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Per-page owner of persisted v8 handles (v8::Global). Teardown resets all globals
|
||||
pub const GlobalTracker = struct {
|
||||
allocator: Allocator,
|
||||
list: std.ArrayList(*GlobalSlot) = .empty,
|
||||
pool: std.heap.MemoryPool(GlobalSlot),
|
||||
|
||||
pub fn init(allocator: Allocator) GlobalTracker {
|
||||
return .{ .allocator = allocator, .pool = std.heap.MemoryPool(GlobalSlot).init(allocator) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *GlobalTracker) void {
|
||||
for (self.list.items) |slot| {
|
||||
slot.reset();
|
||||
}
|
||||
self.list.deinit(self.allocator);
|
||||
self.pool.deinit();
|
||||
}
|
||||
|
||||
pub fn track(self: *GlobalTracker, handle: v8.Global) !*GlobalSlot {
|
||||
const slot = try self.pool.create();
|
||||
errdefer self.pool.destroy(slot);
|
||||
slot.* = .{
|
||||
.handle = handle,
|
||||
.tracker = self,
|
||||
.gindex = @intCast(self.list.items.len),
|
||||
};
|
||||
try self.list.append(self.allocator, slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
// swapRemove + updating the moved's index
|
||||
fn untrack(self: *GlobalTracker, slot: *GlobalSlot) void {
|
||||
const idx = slot.gindex;
|
||||
const moved = self.list.pop().?;
|
||||
if (moved != slot) {
|
||||
self.list.items[idx] = moved;
|
||||
// moved has..well...moved, we need to update its gindex
|
||||
moved.gindex = idx;
|
||||
}
|
||||
self.pool.destroy(slot);
|
||||
}
|
||||
};
|
||||
|
||||
// Build a v8.Global from a live handle and track it on the context's page.
|
||||
pub fn newTrackedSlot(ctx: *Context, handle: anytype) !*GlobalSlot {
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, handle, &global);
|
||||
errdefer v8.v8__Global__Reset(&global);
|
||||
return ctx.page.globals.track(global);
|
||||
}
|
||||
|
||||
// If a function returns a []i32, should that map to a plain-old
|
||||
// JavaScript array, or a Int32Array? It's ambiguous. By default, we'll
|
||||
// map arrays/slices to the JavaScript arrays. If you want a TypedArray
|
||||
@@ -126,14 +204,16 @@ pub fn ArrayBufferRef(comptime kind: ArrayType) type {
|
||||
|
||||
/// Persisted typed array.
|
||||
pub const Global = struct {
|
||||
handle: v8.Global,
|
||||
slot: *GlobalSlot,
|
||||
|
||||
pub fn deinit(self: *Global) void {
|
||||
v8.v8__Global__Reset(&self.handle);
|
||||
pub fn deinit(self: Global) void {
|
||||
self.slot.release();
|
||||
}
|
||||
|
||||
pub fn local(self: *const Global, l: *const Local) Self {
|
||||
return .{ .local = l, .handle = v8.v8__Global__Get(&self.handle, l.isolate.handle).? };
|
||||
pub const release = deinit;
|
||||
|
||||
pub fn local(self: Global, l: *const Local) Self {
|
||||
return .{ .local = l, .handle = v8.v8__Global__Get(&self.slot.handle, l.isolate.handle).? };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,12 +253,7 @@ pub fn ArrayBufferRef(comptime kind: ArrayType) type {
|
||||
}
|
||||
|
||||
pub fn persist(self: *const Self) !Global {
|
||||
var ctx = self.local.ctx;
|
||||
var global: v8.Global = undefined;
|
||||
v8.v8__Global__New(ctx.isolate.handle, self.handle, &global);
|
||||
try ctx.trackGlobal(global);
|
||||
|
||||
return .{ .handle = global };
|
||||
return .{ .slot = try js.newTrackedSlot(self.local.ctx, self.handle) };
|
||||
}
|
||||
|
||||
// Direct view into the typed array's backing memory.
|
||||
|
||||
@@ -88,7 +88,7 @@ pub fn postMessage(self: *BroadcastChannel, message: js.Value, exec: *Execution)
|
||||
const cloned = message.structuredCloneTo(&ls.local) catch {
|
||||
return error.DataClone;
|
||||
};
|
||||
break :blk try cloned.temp();
|
||||
break :blk try cloned.persist();
|
||||
};
|
||||
errdefer snapshot.release();
|
||||
|
||||
@@ -134,7 +134,7 @@ const PostMessageCallback = struct {
|
||||
sender: *BroadcastChannel,
|
||||
// A self-owned structured-clone snapshot of the posted message. Re-cloned
|
||||
// (never shared) into each receiver's MessageEvent, then released.
|
||||
message: js.Value.Temp,
|
||||
message: js.Value.Global,
|
||||
exec: *Execution,
|
||||
post_sequence: u64,
|
||||
|
||||
@@ -221,7 +221,7 @@ const PostMessageCallback = struct {
|
||||
continue;
|
||||
};
|
||||
|
||||
const cloned_temp = cloned.temp() catch |err| {
|
||||
const cloned_temp = cloned.persist() catch |err| {
|
||||
log.err(.dom, "BroadcastChannel.postMessage", .{ .err = err });
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ _on_messageerror: ?js.Function.Global = null,
|
||||
// and delivered once the worker is ready (i.e. once onmessage can be set).
|
||||
// Drained by drainPendingMessages, called from Worker.loadInitialScript
|
||||
// after the initial script has been evaluated.
|
||||
_pending_messages: std.ArrayList(?js.Value.Temp) = .empty,
|
||||
_pending_messages: std.ArrayList(?js.Value.Global) = .empty,
|
||||
|
||||
pub fn init(worker: *Worker, url: [:0]const u8) !*DedicatedWorkerGlobalScope {
|
||||
const self = try worker._arena.create(DedicatedWorkerGlobalScope);
|
||||
@@ -104,7 +104,7 @@ pub fn setOnMessageError(self: *DedicatedWorkerGlobalScope, setter: ?WorkerGloba
|
||||
self._on_messageerror = WorkerGlobalScope.getFunctionFromSetter(setter);
|
||||
}
|
||||
|
||||
pub fn requestAnimationFrame(self: *DedicatedWorkerGlobalScope, cb: js.Function.Temp, exec: *js.Execution) !u32 {
|
||||
pub fn requestAnimationFrame(self: *DedicatedWorkerGlobalScope, cb: js.Function.Global, exec: *js.Execution) !u32 {
|
||||
return self._proto._timers.schedule(exec, cb, 5, .{
|
||||
.repeat = false,
|
||||
.params = &.{},
|
||||
@@ -123,7 +123,7 @@ pub fn receiveMessage(self: *DedicatedWorkerGlobalScope, data: js.Value) !void {
|
||||
return;
|
||||
}
|
||||
|
||||
const cloned_data: ?js.Value.Temp = blk: {
|
||||
const cloned_data: ?js.Value.Global = blk: {
|
||||
// Enter our context to clone the message
|
||||
var ls: js.Local.Scope = undefined;
|
||||
self._proto.js.localScope(&ls);
|
||||
@@ -131,7 +131,7 @@ pub fn receiveMessage(self: *DedicatedWorkerGlobalScope, data: js.Value) !void {
|
||||
|
||||
// clones from where it currently is (the Worker's Page context) to our Context
|
||||
const cloned = data.structuredCloneTo(&ls.local) catch break :blk null;
|
||||
break :blk cloned.temp() catch break :blk null;
|
||||
break :blk cloned.persist() catch break :blk null;
|
||||
};
|
||||
|
||||
if (!self._worker._script_loaded) {
|
||||
@@ -147,7 +147,7 @@ pub fn receiveMessage(self: *DedicatedWorkerGlobalScope, data: js.Value) !void {
|
||||
try self.scheduleMessage(cloned_data);
|
||||
}
|
||||
|
||||
fn scheduleMessage(self: *DedicatedWorkerGlobalScope, cloned_data: ?js.Value.Temp) !void {
|
||||
fn scheduleMessage(self: *DedicatedWorkerGlobalScope, cloned_data: ?js.Value.Global) !void {
|
||||
const wgs = self._proto;
|
||||
const session = wgs._session;
|
||||
|
||||
@@ -183,7 +183,7 @@ pub fn drainPendingMessages(self: *DedicatedWorkerGlobalScope) void {
|
||||
}
|
||||
|
||||
const ReceiveMessageCallback = struct {
|
||||
data: ?js.Value.Temp,
|
||||
data: ?js.Value.Global,
|
||||
arena: Allocator,
|
||||
worker_scope: *DedicatedWorkerGlobalScope,
|
||||
|
||||
|
||||
@@ -42,12 +42,12 @@ _ready_state: ReadyState = .empty,
|
||||
_result: ?Result = null,
|
||||
_error: ?[]const u8 = null,
|
||||
|
||||
_on_abort: ?js.Function.Temp = null,
|
||||
_on_error: ?js.Function.Temp = null,
|
||||
_on_load: ?js.Function.Temp = null,
|
||||
_on_load_end: ?js.Function.Temp = null,
|
||||
_on_load_start: ?js.Function.Temp = null,
|
||||
_on_progress: ?js.Function.Temp = null,
|
||||
_on_abort: ?js.Function.Global = null,
|
||||
_on_error: ?js.Function.Global = null,
|
||||
_on_load: ?js.Function.Global = null,
|
||||
_on_load_end: ?js.Function.Global = null,
|
||||
_on_load_start: ?js.Function.Global = null,
|
||||
_on_progress: ?js.Function.Global = null,
|
||||
|
||||
_aborted: bool = false,
|
||||
|
||||
@@ -97,51 +97,51 @@ fn asEventTarget(self: *FileReader) *EventTarget {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getOnAbort(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnAbort(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_abort;
|
||||
}
|
||||
|
||||
pub fn setOnAbort(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnAbort(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_abort = cb;
|
||||
}
|
||||
|
||||
pub fn getOnError(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnError(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_error;
|
||||
}
|
||||
|
||||
pub fn setOnError(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnError(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_error = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoad(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnLoad(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_load;
|
||||
}
|
||||
|
||||
pub fn setOnLoad(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoad(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_load = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoadEnd(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnLoadEnd(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_load_end;
|
||||
}
|
||||
|
||||
pub fn setOnLoadEnd(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoadEnd(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_load_end = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoadStart(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnLoadStart(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_load_start;
|
||||
}
|
||||
|
||||
pub fn setOnLoadStart(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoadStart(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_load_start = cb;
|
||||
}
|
||||
|
||||
pub fn getOnProgress(self: *const FileReader) ?js.Function.Temp {
|
||||
pub fn getOnProgress(self: *const FileReader) ?js.Function.Global {
|
||||
return self._on_progress;
|
||||
}
|
||||
|
||||
pub fn setOnProgress(self: *FileReader, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnProgress(self: *FileReader, cb: ?js.Function.Global) !void {
|
||||
self._on_progress = cb;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ const IntersectionObserver = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_arena: Allocator,
|
||||
_callback: js.Function.Temp,
|
||||
_callback: js.Function.Global,
|
||||
_observing: std.ArrayList(*Element) = .{},
|
||||
_root: ?*Element = null,
|
||||
_root_margin: []const u8 = "0px",
|
||||
@@ -69,7 +69,7 @@ pub const ObserverInit = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub fn init(callback: js.Function.Temp, options: ?ObserverInit, frame: *Frame) !*IntersectionObserver {
|
||||
pub fn init(callback: js.Function.Global, options: ?ObserverInit, frame: *Frame) !*IntersectionObserver {
|
||||
const arena = try frame.getArena(.small, "IntersectionObserver");
|
||||
errdefer frame.releaseArena(arena);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn postMessage(self: *MessagePort, message: js.Value, exec: *Execution) !voi
|
||||
const c = message.structuredCloneTo(&ls.local) catch {
|
||||
return error.DataClone;
|
||||
};
|
||||
break :blk try c.temp();
|
||||
break :blk try c.persist();
|
||||
};
|
||||
errdefer cloned.release();
|
||||
|
||||
@@ -131,7 +131,7 @@ pub fn setOnMessageError(self: *MessagePort, cb: ?js.Function.Global) !void {
|
||||
|
||||
const PostMessageCallback = struct {
|
||||
port: *MessagePort,
|
||||
message: js.Value.Temp,
|
||||
message: js.Value.Global,
|
||||
exec: *Execution,
|
||||
|
||||
// Called by the scheduler if the task is dropped before it runs. `run` and
|
||||
|
||||
@@ -41,7 +41,7 @@ const MutationObserver = @This();
|
||||
|
||||
_rc: lp.RC(u8) = .{},
|
||||
_arena: Allocator,
|
||||
_callback: js.Function.Temp,
|
||||
_callback: js.Function.Global,
|
||||
_observing: std.ArrayList(Observing) = .{},
|
||||
_pending_records: std.ArrayList(*MutationRecord) = .{},
|
||||
|
||||
@@ -74,7 +74,7 @@ pub const ObserveOptions = struct {
|
||||
attributeFilter: ?[]const []const u8 = null,
|
||||
};
|
||||
|
||||
pub fn init(callback: js.Function.Temp, frame: *Frame) !*MutationObserver {
|
||||
pub fn init(callback: js.Function.Global, frame: *Frame) !*MutationObserver {
|
||||
const arena = try frame.getArena(.small, "MutationObserver");
|
||||
errdefer frame.releaseArena(arena);
|
||||
const self = try arena.create(MutationObserver);
|
||||
|
||||
@@ -68,7 +68,7 @@ pub const Mode = enum {
|
||||
|
||||
pub const ScheduleOpts = struct {
|
||||
repeat: bool,
|
||||
params: []js.Value.Temp,
|
||||
params: []js.Value.Global,
|
||||
name: []const u8,
|
||||
low_priority: bool = false,
|
||||
mode: Mode = .normal,
|
||||
@@ -77,7 +77,7 @@ pub const ScheduleOpts = struct {
|
||||
pub fn schedule(
|
||||
self: *Timers,
|
||||
exec: *js.Execution,
|
||||
cb: js.Function.Temp,
|
||||
cb: js.Function.Global,
|
||||
delay_ms: u32,
|
||||
opts: ScheduleOpts,
|
||||
) !u32 {
|
||||
@@ -95,9 +95,9 @@ pub fn schedule(
|
||||
const nesting = @min(self._nesting_level + 1, CLAMP_NESTING + 1);
|
||||
const delay = if (nesting > CLAMP_NESTING and delay_ms < CLAMP_MS) CLAMP_MS else delay_ms;
|
||||
|
||||
var persisted_params: []js.Value.Temp = &.{};
|
||||
var persisted_params: []js.Value.Global = &.{};
|
||||
if (opts.params.len > 0) {
|
||||
persisted_params = try arena.dupe(js.Value.Temp, opts.params);
|
||||
persisted_params = try arena.dupe(js.Value.Global, opts.params);
|
||||
}
|
||||
|
||||
const gop = try self._callbacks.getOrPut(exec.arena, timer_id);
|
||||
@@ -142,15 +142,15 @@ pub fn clear(self: *Timers, id: u32) void {
|
||||
// compiled into an anonymous function body, matching how legacy browsers
|
||||
// (and all current UAs) interpret `setTimeout("foo()", 100)`.
|
||||
pub const LegacyHandler = union(enum) {
|
||||
function: js.Function.Temp,
|
||||
function: js.Function.Global,
|
||||
string: js.String,
|
||||
|
||||
pub fn resolve(handler: LegacyHandler, exec: *js.Execution) !js.Function.Temp {
|
||||
pub fn resolve(handler: LegacyHandler, exec: *js.Execution) !js.Function.Global {
|
||||
switch (handler) {
|
||||
.function => |fun| return fun,
|
||||
.string => |str| {
|
||||
const fun = try exec.js.local.?.compileFunction(str, &.{}, &.{});
|
||||
return fun.temp();
|
||||
return fun.persist();
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -170,14 +170,14 @@ const ScheduleCallback = struct {
|
||||
// the Timer's _nesting_level so that any new timers will become nesting + 1
|
||||
nesting: u8,
|
||||
|
||||
cb: js.Function.Temp,
|
||||
cb: js.Function.Global,
|
||||
|
||||
mode: Mode,
|
||||
exec: *js.Execution,
|
||||
timers: *Timers,
|
||||
arena: Allocator,
|
||||
removed: bool = false,
|
||||
params: []const js.Value.Temp,
|
||||
params: []const js.Value.Global,
|
||||
|
||||
fn cancelled(ptr: *anyopaque) void {
|
||||
var self: *ScheduleCallback = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn actionSequence(_: *const WebDriver, sources: js.Value, frame: *Frame) !vo
|
||||
const arena = try frame.getArena(.tiny, "WebDriver.actionSequence");
|
||||
errdefer frame.releaseArena(arena);
|
||||
|
||||
const persisted = try sources.temp();
|
||||
const persisted = try sources.persist();
|
||||
errdefer persisted.release();
|
||||
|
||||
const action_sequence = try arena.create(ActionSequence);
|
||||
@@ -85,7 +85,7 @@ pub fn actionSequence(_: *const WebDriver, sources: js.Value, frame: *Frame) !vo
|
||||
const ActionSequence = struct {
|
||||
frame: *Frame,
|
||||
arena: Allocator,
|
||||
sources: js.Value.Temp,
|
||||
sources: js.Value.Global,
|
||||
|
||||
fn run(ptr: *anyopaque) !?u32 {
|
||||
const self: *ActionSequence = @ptrCast(@alignCast(ptr));
|
||||
|
||||
@@ -416,7 +416,7 @@ pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, exe
|
||||
return Fetch.init(input, options, exec);
|
||||
}
|
||||
|
||||
pub fn setTimeout(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []js.Value.Temp, exec: *js.Execution) !u32 {
|
||||
pub fn setTimeout(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []js.Value.Global, exec: *js.Execution) !u32 {
|
||||
const cb = try handler.resolve(exec);
|
||||
return self._timers.schedule(exec, cb, delay_ms orelse 0, .{
|
||||
.repeat = false,
|
||||
@@ -425,7 +425,7 @@ pub fn setTimeout(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn setInterval(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []js.Value.Temp, exec: *js.Execution) !u32 {
|
||||
pub fn setInterval(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []js.Value.Global, exec: *js.Execution) !u32 {
|
||||
const cb = try handler.resolve(exec);
|
||||
return self._timers.schedule(exec, cb, delay_ms orelse 0, .{
|
||||
.repeat = true,
|
||||
@@ -434,7 +434,7 @@ pub fn setInterval(self: *Window, handler: Timers.LegacyHandler, delay_ms: ?u32,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn setImmediate(self: *Window, cb: js.Function.Temp, params: []js.Value.Temp, exec: *js.Execution) !u32 {
|
||||
pub fn setImmediate(self: *Window, cb: js.Function.Global, params: []js.Value.Global, exec: *js.Execution) !u32 {
|
||||
return self._timers.schedule(exec, cb, 0, .{
|
||||
.repeat = false,
|
||||
.params = params,
|
||||
@@ -442,7 +442,7 @@ pub fn setImmediate(self: *Window, cb: js.Function.Temp, params: []js.Value.Temp
|
||||
});
|
||||
}
|
||||
|
||||
pub fn requestAnimationFrame(self: *Window, cb: js.Function.Temp, exec: *js.Execution) !u32 {
|
||||
pub fn requestAnimationFrame(self: *Window, cb: js.Function.Global, exec: *js.Execution) !u32 {
|
||||
return self._timers.schedule(exec, cb, 5, .{
|
||||
.repeat = false,
|
||||
.params = &.{},
|
||||
@@ -474,7 +474,7 @@ pub fn cancelAnimationFrame(self: *Window, id: u32) void {
|
||||
const RequestIdleCallbackOpts = struct {
|
||||
timeout: ?u32 = null,
|
||||
};
|
||||
pub fn requestIdleCallback(self: *Window, cb: js.Function.Temp, opts_: ?RequestIdleCallbackOpts, exec: *js.Execution) !u32 {
|
||||
pub fn requestIdleCallback(self: *Window, cb: js.Function.Global, opts_: ?RequestIdleCallbackOpts, exec: *js.Execution) !u32 {
|
||||
const opts = opts_ orelse RequestIdleCallbackOpts{};
|
||||
return self._timers.schedule(exec, cb, opts.timeout orelse 50, .{
|
||||
.mode = .idle,
|
||||
@@ -491,7 +491,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void {
|
||||
|
||||
pub fn reportError(self: *Window, err: js.Value, frame: *Frame) !void {
|
||||
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
||||
.@"error" = try err.temp(),
|
||||
.@"error" = try err.persist(),
|
||||
.message = err.toStringSlice() catch "Unknown error",
|
||||
.bubbles = false,
|
||||
.cancelable = true,
|
||||
@@ -725,7 +725,7 @@ pub fn postMessage(self: *Window, message: js.Value, target_origin: ?[]const u8,
|
||||
const c = message.structuredCloneTo(&ls.local) catch {
|
||||
return error.DataClone;
|
||||
};
|
||||
break :blk try c.temp();
|
||||
break :blk try c.persist();
|
||||
};
|
||||
errdefer cloned.release();
|
||||
|
||||
@@ -926,8 +926,8 @@ pub fn unhandledPromiseRejection(self: *Window, no_handler: bool, rejection: js.
|
||||
const target = self.asEventTarget();
|
||||
if (frame._event_manager.hasDirectListeners(target, event_name, attribute_callback)) {
|
||||
const event = (try @import("event/PromiseRejectionEvent.zig").init(event_name, .{
|
||||
.reason = if (rejection.reason()) |r| try r.temp() else null,
|
||||
.promise = try rejection.promise().temp(),
|
||||
.reason = if (rejection.reason()) |r| try r.persist() else null,
|
||||
.promise = try rejection.promise().persist(),
|
||||
}, frame._page)).asEvent();
|
||||
try frame._event_manager.dispatchDirect(target, event, attribute_callback, .{ .context = "window.unhandledrejection" });
|
||||
}
|
||||
@@ -965,7 +965,7 @@ const PostMessageCallback = struct {
|
||||
source: *Window,
|
||||
arena: Allocator,
|
||||
origin: []const u8,
|
||||
message: js.Value.Temp,
|
||||
message: js.Value.Global,
|
||||
ports: []const *MessagePort,
|
||||
|
||||
fn deinit(self: *PostMessageCallback) void {
|
||||
|
||||
@@ -265,13 +265,13 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
||||
}
|
||||
|
||||
// Fire an error event on the Worker object (parent context)
|
||||
fn fireErrorEvent(self: *Worker, message: []const u8, error_value: ?js.Value.Temp) void {
|
||||
fn fireErrorEvent(self: *Worker, message: []const u8, error_value: ?js.Value.Global) void {
|
||||
self._fireErrorEvent(message, error_value) catch |err| {
|
||||
log.warn(.browser, "worker fire error", .{ .err = err, .message = message });
|
||||
};
|
||||
}
|
||||
|
||||
fn _fireErrorEvent(self: *Worker, message: []const u8, error_value: ?js.Value.Temp) !void {
|
||||
fn _fireErrorEvent(self: *Worker, message: []const u8, error_value: ?js.Value.Global) !void {
|
||||
const frame = self._frame;
|
||||
const target = self.asEventTarget();
|
||||
const on_error = self._on_error;
|
||||
@@ -318,7 +318,7 @@ pub fn receiveMessage(self: *Worker, data: js.Value) !void {
|
||||
|
||||
// clones from where it currently is (the Worker context) to our Page's context
|
||||
const cloned = data.structuredCloneTo(&ls.local) catch |err| break :blk err;
|
||||
break :blk cloned.temp();
|
||||
break :blk cloned.persist();
|
||||
};
|
||||
|
||||
const message_arena = try frame.getArena(.tiny, "Worker.receiveMessage");
|
||||
@@ -376,7 +376,7 @@ fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global {
|
||||
}
|
||||
|
||||
const ReceiveMessageCallback = struct {
|
||||
data: anyerror!js.Value.Temp,
|
||||
data: anyerror!js.Value.Global,
|
||||
arena: Allocator,
|
||||
worker: *Worker,
|
||||
|
||||
|
||||
@@ -348,8 +348,8 @@ pub fn unhandledPromiseRejection(self: *WorkerGlobalScope, no_handler: bool, rej
|
||||
const target = self.asEventTarget();
|
||||
if (self._event_manager.hasDirectListeners(target, event_name, attribute_callback)) {
|
||||
const event = (try @import("event/PromiseRejectionEvent.zig").init(event_name, .{
|
||||
.reason = if (rejection.reason()) |r| try r.temp() else null,
|
||||
.promise = try rejection.promise().temp(),
|
||||
.reason = if (rejection.reason()) |r| try r.persist() else null,
|
||||
.promise = try rejection.promise().persist(),
|
||||
}, self._page)).asEvent();
|
||||
try self.dispatch(target, event, attribute_callback, .{});
|
||||
}
|
||||
@@ -423,7 +423,7 @@ fn importScript(self: *WorkerGlobalScope, arena: Allocator, url: [:0]const u8) !
|
||||
|
||||
pub fn reportError(self: *WorkerGlobalScope, err: JS.Value) !void {
|
||||
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
|
||||
.@"error" = try err.temp(),
|
||||
.@"error" = try err.persist(),
|
||||
.message = err.toStringSlice() catch "Unknown error",
|
||||
.bubbles = false,
|
||||
.cancelable = true,
|
||||
@@ -479,7 +479,7 @@ pub fn queueMicrotask(self: *WorkerGlobalScope, cb: JS.Function) void {
|
||||
self.js.queueMicrotaskFunc(cb);
|
||||
}
|
||||
|
||||
pub fn setTimeout(self: *WorkerGlobalScope, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []JS.Value.Temp, exec: *JS.Execution) !u32 {
|
||||
pub fn setTimeout(self: *WorkerGlobalScope, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []JS.Value.Global, exec: *JS.Execution) !u32 {
|
||||
const cb = try handler.resolve(exec);
|
||||
return self._timers.schedule(exec, cb, delay_ms orelse 0, .{
|
||||
.repeat = false,
|
||||
@@ -492,7 +492,7 @@ pub fn clearTimeout(self: *WorkerGlobalScope, id: u32) void {
|
||||
self._timers.clear(id);
|
||||
}
|
||||
|
||||
pub fn setInterval(self: *WorkerGlobalScope, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []JS.Value.Temp, exec: *JS.Execution) !u32 {
|
||||
pub fn setInterval(self: *WorkerGlobalScope, handler: Timers.LegacyHandler, delay_ms: ?u32, params: []JS.Value.Global, exec: *JS.Execution) !u32 {
|
||||
const cb = try handler.resolve(exec);
|
||||
return self._timers.schedule(exec, cb, delay_ms orelse 0, .{
|
||||
.repeat = true,
|
||||
|
||||
@@ -43,7 +43,7 @@ _timeline: ?js.Object.Global = null,
|
||||
_ready_resolver: ?js.PromiseResolver.Global = null,
|
||||
_finished_resolver: ?js.PromiseResolver.Global = null,
|
||||
_startTime: ?f64 = null,
|
||||
_onFinish: ?js.Function.Temp = null,
|
||||
_onFinish: ?js.Function.Global = null,
|
||||
_playState: PlayState = .idle,
|
||||
|
||||
// Fake the animation by passing the states:
|
||||
@@ -179,7 +179,7 @@ pub fn setStartTime(self: *Animation, value: ?f64, frame: *Frame) !void {
|
||||
return self.play(frame);
|
||||
}
|
||||
|
||||
pub fn getOnFinish(self: *const Animation) ?js.Function.Temp {
|
||||
pub fn getOnFinish(self: *const Animation) ?js.Function.Global {
|
||||
return self._onFinish;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ fn update(ctx: *anyopaque) !?u32 {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn setOnFinish(self: *Animation, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnFinish(self: *Animation, cb: ?js.Function.Global) !void {
|
||||
self._onFinish = cb;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ const Allocator = std.mem.Allocator;
|
||||
const CustomEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_detail: ?js.Value.Temp = null,
|
||||
_detail: ?js.Value.Global = null,
|
||||
_arena: Allocator,
|
||||
|
||||
const CustomEventOptions = struct {
|
||||
detail: ?js.Value.Temp = null,
|
||||
detail: ?js.Value.Global = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(CustomEvent, CustomEventOptions);
|
||||
@@ -64,7 +64,7 @@ pub fn initCustomEvent(
|
||||
event_string: []const u8,
|
||||
bubbles: ?bool,
|
||||
cancelable: ?bool,
|
||||
detail_: ?js.Value.Temp,
|
||||
detail_: ?js.Value.Global,
|
||||
) !void {
|
||||
// This function can only be called after the constructor has called.
|
||||
// So we assume proto is initialized already by constructor.
|
||||
@@ -94,7 +94,7 @@ pub fn asEvent(self: *CustomEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getDetail(self: *const CustomEvent) ?js.Value.Temp {
|
||||
pub fn getDetail(self: *const CustomEvent) ?js.Value.Global {
|
||||
return self._detail;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ _message: []const u8 = "",
|
||||
_filename: []const u8 = "",
|
||||
_line_number: u32 = 0,
|
||||
_column_number: u32 = 0,
|
||||
_error: ?js.Value.Temp = null,
|
||||
_error: ?js.Value.Global = null,
|
||||
_arena: Allocator,
|
||||
|
||||
pub const ErrorEventOptions = struct {
|
||||
@@ -42,7 +42,7 @@ pub const ErrorEventOptions = struct {
|
||||
filename: ?[]const u8 = null,
|
||||
lineno: u32 = 0,
|
||||
colno: u32 = 0,
|
||||
@"error": ?js.Value.Temp = null,
|
||||
@"error": ?js.Value.Global = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions);
|
||||
@@ -116,7 +116,7 @@ pub fn getColumnNumber(self: *const ErrorEvent) u32 {
|
||||
return self._column_number;
|
||||
}
|
||||
|
||||
pub fn getError(self: *const ErrorEvent) ?js.Value.Temp {
|
||||
pub fn getError(self: *const ErrorEvent) ?js.Value.Global {
|
||||
return self._error;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ const MessageEventOptions = struct {
|
||||
};
|
||||
|
||||
pub const Data = union(enum) {
|
||||
value: js.Value.Temp,
|
||||
value: js.Value.Global,
|
||||
string: []const u8,
|
||||
arraybuffer: js.ArrayBuffer,
|
||||
blob: *@import("../Blob.zig"),
|
||||
|
||||
@@ -27,12 +27,12 @@ const String = lp.String;
|
||||
const PromiseRejectionEvent = @This();
|
||||
|
||||
_proto: *Event,
|
||||
_reason: ?js.Value.Temp = null,
|
||||
_promise: ?js.Promise.Temp = null,
|
||||
_reason: ?js.Value.Global = null,
|
||||
_promise: ?js.Promise.Global = null,
|
||||
|
||||
const PromiseRejectionEventOptions = struct {
|
||||
reason: ?js.Value.Temp = null,
|
||||
promise: ?js.Promise.Temp = null,
|
||||
reason: ?js.Value.Global = null,
|
||||
promise: ?js.Promise.Global = null,
|
||||
};
|
||||
|
||||
const Options = Event.inheritOptions(PromiseRejectionEvent, PromiseRejectionEventOptions);
|
||||
@@ -79,11 +79,11 @@ pub fn asEvent(self: *PromiseRejectionEvent) *Event {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn getReason(self: *const PromiseRejectionEvent) ?js.Value.Temp {
|
||||
pub fn getReason(self: *const PromiseRejectionEvent) ?js.Value.Global {
|
||||
return self._reason;
|
||||
}
|
||||
|
||||
pub fn getPromise(self: *const PromiseRejectionEvent) ?js.Promise.Temp {
|
||||
pub fn getPromise(self: *const PromiseRejectionEvent) ?js.Promise.Global {
|
||||
return self._promise;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,10 +75,10 @@ _close_reason: []const u8 = "",
|
||||
_protocol: []const u8 = "",
|
||||
|
||||
// Event handlers
|
||||
_on_open: ?js.Function.Temp = null,
|
||||
_on_message: ?js.Function.Temp = null,
|
||||
_on_error: ?js.Function.Temp = null,
|
||||
_on_close: ?js.Function.Temp = null,
|
||||
_on_open: ?js.Function.Global = null,
|
||||
_on_message: ?js.Function.Global = null,
|
||||
_on_error: ?js.Function.Global = null,
|
||||
_on_close: ?js.Function.Global = null,
|
||||
|
||||
pub const ReadyState = enum(u8) {
|
||||
connecting = 0,
|
||||
@@ -434,53 +434,53 @@ pub fn setBinaryType(self: *WebSocket, value: []const u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOnOpen(self: *const WebSocket) ?js.Function.Temp {
|
||||
pub fn getOnOpen(self: *const WebSocket) ?js.Function.Global {
|
||||
return self._on_open;
|
||||
}
|
||||
|
||||
pub fn setOnOpen(self: *WebSocket, cb_: ?js.Function) !void {
|
||||
if (self._on_open) |old| old.release();
|
||||
if (cb_) |cb| {
|
||||
self._on_open = try cb.tempWithThis(self);
|
||||
self._on_open = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_open = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOnMessage(self: *const WebSocket) ?js.Function.Temp {
|
||||
pub fn getOnMessage(self: *const WebSocket) ?js.Function.Global {
|
||||
return self._on_message;
|
||||
}
|
||||
|
||||
pub fn setOnMessage(self: *WebSocket, cb_: ?js.Function) !void {
|
||||
if (self._on_message) |old| old.release();
|
||||
if (cb_) |cb| {
|
||||
self._on_message = try cb.tempWithThis(self);
|
||||
self._on_message = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_message = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOnError(self: *const WebSocket) ?js.Function.Temp {
|
||||
pub fn getOnError(self: *const WebSocket) ?js.Function.Global {
|
||||
return self._on_error;
|
||||
}
|
||||
|
||||
pub fn setOnError(self: *WebSocket, cb_: ?js.Function) !void {
|
||||
if (self._on_error) |old| old.release();
|
||||
if (cb_) |cb| {
|
||||
self._on_error = try cb.tempWithThis(self);
|
||||
self._on_error = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_error = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOnClose(self: *const WebSocket) ?js.Function.Temp {
|
||||
pub fn getOnClose(self: *const WebSocket) ?js.Function.Global {
|
||||
return self._on_close;
|
||||
}
|
||||
|
||||
pub fn setOnClose(self: *WebSocket, cb_: ?js.Function) !void {
|
||||
if (self._on_close) |old| old.release();
|
||||
if (cb_) |cb| {
|
||||
self._on_close = try cb.tempWithThis(self);
|
||||
self._on_close = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_close = null;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ _response_headers: std.ArrayList([]const u8) = .empty,
|
||||
_response_type: ResponseType = .text,
|
||||
|
||||
_ready_state: ReadyState = .unsent,
|
||||
_on_ready_state_change: ?js.Function.Temp = null,
|
||||
_on_ready_state_change: ?js.Function.Global = null,
|
||||
_with_credentials: bool = false,
|
||||
_timeout: u32 = 0,
|
||||
|
||||
@@ -147,13 +147,13 @@ fn asEventTarget(self: *XMLHttpRequest) *EventTarget {
|
||||
return self._proto._proto;
|
||||
}
|
||||
|
||||
pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function.Temp {
|
||||
pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function.Global {
|
||||
return self._on_ready_state_change;
|
||||
}
|
||||
|
||||
pub fn setOnReadyStateChange(self: *XMLHttpRequest, cb_: ?js.Function) !void {
|
||||
if (cb_) |cb| {
|
||||
self._on_ready_state_change = try cb.tempWithThis(self);
|
||||
self._on_ready_state_change = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_ready_state_change = null;
|
||||
}
|
||||
|
||||
@@ -27,13 +27,13 @@ const XMLHttpRequestEventTarget = @This();
|
||||
|
||||
_type: Type,
|
||||
_proto: *EventTarget,
|
||||
_on_abort: ?js.Function.Temp = null,
|
||||
_on_error: ?js.Function.Temp = null,
|
||||
_on_load: ?js.Function.Temp = null,
|
||||
_on_load_end: ?js.Function.Temp = null,
|
||||
_on_load_start: ?js.Function.Temp = null,
|
||||
_on_progress: ?js.Function.Temp = null,
|
||||
_on_timeout: ?js.Function.Temp = null,
|
||||
_on_abort: ?js.Function.Global = null,
|
||||
_on_error: ?js.Function.Global = null,
|
||||
_on_load: ?js.Function.Global = null,
|
||||
_on_load_end: ?js.Function.Global = null,
|
||||
_on_load_start: ?js.Function.Global = null,
|
||||
_on_progress: ?js.Function.Global = null,
|
||||
_on_timeout: ?js.Function.Global = null,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
request: *@import("XMLHttpRequest.zig"),
|
||||
@@ -83,61 +83,61 @@ pub fn dispatch(self: *XMLHttpRequestEventTarget, comptime event_type: DispatchT
|
||||
);
|
||||
}
|
||||
|
||||
pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_abort;
|
||||
}
|
||||
|
||||
pub fn setOnAbort(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnAbort(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_abort = cb;
|
||||
}
|
||||
|
||||
pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_error;
|
||||
}
|
||||
|
||||
pub fn setOnError(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnError(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_error = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_load;
|
||||
}
|
||||
|
||||
pub fn setOnLoad(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoad(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_load = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_load_end;
|
||||
}
|
||||
|
||||
pub fn setOnLoadEnd(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoadEnd(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_load_end = cb;
|
||||
}
|
||||
|
||||
pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_load_start;
|
||||
}
|
||||
|
||||
pub fn setOnLoadStart(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnLoadStart(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_load_start = cb;
|
||||
}
|
||||
|
||||
pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_progress;
|
||||
}
|
||||
|
||||
pub fn setOnProgress(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Temp) !void {
|
||||
pub fn setOnProgress(self: *XMLHttpRequestEventTarget, cb: ?js.Function.Global) !void {
|
||||
self._on_progress = cb;
|
||||
}
|
||||
|
||||
pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function.Temp {
|
||||
pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function.Global {
|
||||
return self._on_timeout;
|
||||
}
|
||||
|
||||
pub fn setOnTimeout(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void {
|
||||
if (cb_) |cb| {
|
||||
self._on_timeout = try cb.tempWithThis(self);
|
||||
self._on_timeout = try cb.persistWithThis(self);
|
||||
} else {
|
||||
self._on_timeout = null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ _index_id: ?i64 = null,
|
||||
|
||||
// the JS value of this Cursor, pre-converted and cached as an optimization
|
||||
// since this cursor will be the request value on every iteration.
|
||||
_js: *js.Value.BareGlobal,
|
||||
_js: *js.GlobalSlot,
|
||||
|
||||
// Encoded current key; null before iteration and at the end. For an index cursor
|
||||
// this is the index key; for an object store it equals the primary key.
|
||||
@@ -62,7 +62,7 @@ _primary_key: ?[]const u8 = null,
|
||||
_value: ?[]const u8 = null,
|
||||
// The deserialized JS value, cached so repeated `.value` reads return the same
|
||||
// object (and observe mutations to it). Reset whenever the cursor repositions.
|
||||
_value_js: ?*js.Value.BareGlobal = null,
|
||||
_value_js: ?*js.GlobalSlot = null,
|
||||
|
||||
// Backing storage for _key/_primary_key/_value, reused across positions so a
|
||||
// long scan holds one record's worth of memory, not the whole traversal.
|
||||
@@ -384,7 +384,7 @@ fn exhaust(self: *IDBCursor) void {
|
||||
// value until transaction teardown.
|
||||
fn invalidateValue(self: *IDBCursor) void {
|
||||
if (self._value_js) |slot| {
|
||||
slot.deinit();
|
||||
slot.reset();
|
||||
self._value_js = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ _deleted: bool = false,
|
||||
_created: bool = false,
|
||||
// not just for efficiency, we must return the same v8::Array every time the
|
||||
// compound key is accessed.
|
||||
_key_path_js: ?*js.Value.BareGlobal = null,
|
||||
_key_path_js: ?*js.GlobalSlot = null,
|
||||
|
||||
pub fn init(obj_store: *IDBObjectStore, info: Engine.IndexInfo, name: []const u8) !*IDBIndex {
|
||||
const self = try obj_store._txn._arena.create(IDBIndex);
|
||||
|
||||
@@ -52,7 +52,7 @@ _created: bool = false,
|
||||
_indexes: std.ArrayList(*IDBIndex) = .empty,
|
||||
// not just for efficiency, we must return the same v8::Array every time the
|
||||
// compound key is accessed.
|
||||
_key_path_js: ?*js.Value.BareGlobal = null,
|
||||
_key_path_js: ?*js.GlobalSlot = null,
|
||||
|
||||
pub fn init(
|
||||
txn: *IDBTransaction,
|
||||
@@ -358,10 +358,10 @@ fn write(self: *IDBObjectStore, value: js.Value, key_arg: ?js.Value, kind: Write
|
||||
} }, exec);
|
||||
}
|
||||
|
||||
pub fn runWrite(self: *IDBObjectStore, request: *IDBRequest, kind: WriteKind, value_global: *js.Value.BareGlobal, prepared: PreparedKey, exec: *Execution) !void {
|
||||
pub fn runWrite(self: *IDBObjectStore, request: *IDBRequest, kind: WriteKind, value_global: *js.GlobalSlot, prepared: PreparedKey, exec: *Execution) !void {
|
||||
// Written (or failed) is written: the pinned value is dead once this op
|
||||
// ran, so release its handle now instead of at transaction teardown.
|
||||
defer value_global.deinit();
|
||||
defer value_global.reset();
|
||||
self.writeInner(request, kind, value_global, prepared, exec) catch |err| {
|
||||
if (err != error.Constraint) {
|
||||
log.warn(.storage, "idb write", .{ .err = err, .kind = kind, .sqlite = self._engine.lastError() });
|
||||
@@ -370,7 +370,7 @@ pub fn runWrite(self: *IDBObjectStore, request: *IDBRequest, kind: WriteKind, va
|
||||
};
|
||||
}
|
||||
|
||||
fn writeInner(self: *IDBObjectStore, request: *IDBRequest, kind: WriteKind, value_global: *js.Value.BareGlobal, prepared: PreparedKey, exec: *Execution) !void {
|
||||
fn writeInner(self: *IDBObjectStore, request: *IDBRequest, kind: WriteKind, value_global: *js.GlobalSlot, prepared: PreparedKey, exec: *Execution) !void {
|
||||
const local = exec.js.local.?;
|
||||
const value = value_global.local(local);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ const ReadyState = enum {
|
||||
|
||||
const Result = union(enum) {
|
||||
none: ?js.Undefined, // null or undefined (different APIs return different values)
|
||||
value: *js.Value.BareGlobal, // the result of a get/add/put, or a positioned cursor
|
||||
value: *js.GlobalSlot, // the result of a get/add/put, or a positioned cursor
|
||||
database: *IDBDatabase, // the result of an open
|
||||
};
|
||||
|
||||
@@ -139,8 +139,8 @@ fn clearOwnedResult(self: *IDBRequest) void {
|
||||
self._result_owned = false;
|
||||
switch (self._result) {
|
||||
.value => |global| {
|
||||
// It's ok to keep this in txn._globals, deinit can be called multiple times
|
||||
global.deinit();
|
||||
// It's ok to keep this in txn._globals, reset can be called multiple times
|
||||
global.reset();
|
||||
self._result = .{ .none = js.Undefined{} };
|
||||
},
|
||||
.none, .database => {},
|
||||
@@ -159,7 +159,7 @@ pub fn setValue(self: *IDBRequest, value: js.Value) !void {
|
||||
|
||||
// Not exposed to JS, called internally. The handle is borrowed (a cursor's
|
||||
// transaction-owned _js), not owned by this request.
|
||||
pub fn setValueGlobal(self: *IDBRequest, global: *js.Value.BareGlobal) void {
|
||||
pub fn setValueGlobal(self: *IDBRequest, global: *js.GlobalSlot) void {
|
||||
self.clearOwnedResult();
|
||||
self._result = .{ .value = global };
|
||||
}
|
||||
@@ -371,7 +371,7 @@ pub const Operation = union(enum) {
|
||||
|
||||
const StoreQuery = struct { store: *IDBObjectStore, bounds: Engine.Bounds };
|
||||
const StoreGetAll = struct { store: *IDBObjectStore, args: IDBKeyRange.GetAllArgs, mode: IDBObjectStore.GetAllMode };
|
||||
const StoreWrite = struct { store: *IDBObjectStore, kind: IDBObjectStore.WriteKind, value: *js.Value.BareGlobal, key: IDBObjectStore.PreparedKey };
|
||||
const StoreWrite = struct { store: *IDBObjectStore, kind: IDBObjectStore.WriteKind, value: *js.GlobalSlot, key: IDBObjectStore.PreparedKey };
|
||||
const IndexQuery = struct { index: *IDBIndex, bounds: Engine.Bounds };
|
||||
const IndexGetAll = struct { index: *IDBIndex, args: IDBKeyRange.GetAllArgs, mode: IDBObjectStore.GetAllMode };
|
||||
const CursorIterate = struct { cursor: *IDBCursor, seek: IDBCursor.Seek, offset: u32 };
|
||||
|
||||
@@ -65,7 +65,7 @@ _arena: Allocator,
|
||||
// v8 handles owned by the transaction, swept (reset) in deinit. Slots are
|
||||
// arena-allocated so an early release and the sweep hit the same instance —
|
||||
// a v8 Global reset is only idempotent through a single instance.
|
||||
_globals: std.ArrayList(*js.Value.BareGlobal) = .empty,
|
||||
_globals: std.ArrayList(*js.GlobalSlot) = .empty,
|
||||
|
||||
// objectStore() must return the same object for a given name within one
|
||||
// transaction (per spec); this also keeps repeated lookups off sqlite.
|
||||
@@ -180,7 +180,7 @@ pub fn deinit(self: *IDBTransaction, page: *Page) void {
|
||||
std.debug.assert(self._parked == false);
|
||||
}
|
||||
for (self._globals.items) |slot| {
|
||||
slot.deinit();
|
||||
slot.reset();
|
||||
}
|
||||
page.releaseArena(self._arena);
|
||||
}
|
||||
@@ -194,12 +194,12 @@ pub fn releaseRef(self: *IDBTransaction, page: *Page) void {
|
||||
}
|
||||
|
||||
// Persist a JS value with the transaction's lifetime: the handle is reset when the
|
||||
// transaction's memory is released — or earlier, by calling deinit() on the
|
||||
// transaction's memory is released — or earlier, by calling reset() on the
|
||||
// returned slot (the sweep's second reset is then a no-op).
|
||||
pub fn persist(self: *IDBTransaction, value: js.Value) !*js.Value.BareGlobal {
|
||||
const slot = try self._arena.create(js.Value.BareGlobal);
|
||||
pub fn persist(self: *IDBTransaction, value: js.Value) !*js.GlobalSlot {
|
||||
const slot = try value.persistBare(self._arena);
|
||||
errdefer slot.reset();
|
||||
try self._globals.append(self._arena, slot);
|
||||
slot.* = value.bare();
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ pub fn registerTypes() []const type {
|
||||
// the keyPath attribute need identity equality. For a compound key (an array)
|
||||
// we have to return the same v8::rray on every reach. So users of KeyPath will
|
||||
// cache it locally, and store it in the Transaction's globals list for cleanup.
|
||||
pub fn cachedKeyPathJs(cache: *?*js.Value.BareGlobal, txn: *IDBTransaction, kp: ?Key.KeyPath, exec: *js.Execution) !js.Value {
|
||||
pub fn cachedKeyPathJs(cache: *?*js.GlobalSlot, txn: *IDBTransaction, kp: ?Key.KeyPath, exec: *js.Execution) !js.Value {
|
||||
const local = exec.js.local.?;
|
||||
if (kp) |path| {
|
||||
if (std.meta.activeTag(path) == .list) {
|
||||
|
||||
Reference in New Issue
Block a user