mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-04-03 16:10:29 +00:00
Start to eagerly reset globals.
Currently, when you create a Global (Value, Object, Function, ...) it exists
until the context is destroyed.
This PR adds the ability to eagerly free them when they fall out of scope, which
is only possible because of the new finalizer hooks.
Previously, we had js.Value, js.Value.Global; js.Function, js.Function.Global,
etc. This PR introduces a .Temp variant: js.Value.Temp and js.Function.Temp.
This is purely a discriminatory type and it behaves (and IS) a Global. The
difference is that it can be released:
page.js.release(self._on_ready_state_change.?)
Why a new type? There's no guarantee that a global (the existing .Global or the
new .Temp) will get released before the context ends. For this reason, we always
track them in order to free the on context deninit:
```zig
for (self.global_functions.items) |*global| {
v8.v8__Global__Reset(global);
}
```
If a .Temp is eagerly released, we need to remove it from this list. The simple
solution would be to switch `global_functions` from an ArrayList to a HashMap.
But that adds overhead for values that we know we'll never be able to eagerly
release. For this reason, .Temp are stored in a hashmap (and can be released)
and .Globla are stored in an ArrayList (and cannot be released). It's a micro-
optimization...eagerly releasing doesn't have to O(N) scan the list, and we only
pay the memory overhead of the hashmap for values that have a change to be
eagerly freed.
Eager-freeing is now applied to both the callbacn and the values for window
timers (setTimeout, setInterval, RAF). And to the XHR ready_state_change
callback. (we'll do more as we go).
This commit is contained in:
@@ -293,61 +293,29 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
|
||||
}
|
||||
}
|
||||
|
||||
if (T == js.Function) {
|
||||
// we're returning a callback
|
||||
return .{ .local = self, .handle = @ptrCast(value.handle) };
|
||||
}
|
||||
// zig fmt: off
|
||||
switch (T) {
|
||||
js.Value => return value,
|
||||
js.Exception => return .{ .local = self, .handle = isolate.throwException(value.handle) },
|
||||
|
||||
if (T == js.Function.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
inline
|
||||
js.Function,
|
||||
js.Object,
|
||||
js.Promise,
|
||||
js.String => return .{ .local = self, .handle = @ptrCast(value.handle) },
|
||||
|
||||
if (T == js.Object) {
|
||||
// we're returning a v8.Object
|
||||
return .{ .local = self, .handle = @ptrCast(value.handle) };
|
||||
}
|
||||
|
||||
if (T == js.Object.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
|
||||
if (T == js.Value.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
|
||||
if (T == js.Promise.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
|
||||
if (T == js.PromiseResolver.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
|
||||
if (T == js.Module.Global) {
|
||||
// Auto-convert Global to local for bridge
|
||||
return .{ .local = self, .handle = @ptrCast(value.local(self).handle) };
|
||||
}
|
||||
|
||||
if (T == js.Value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (T == js.Promise) {
|
||||
return .{ .local = self, .handle = @ptrCast(value.handle) };
|
||||
}
|
||||
|
||||
if (T == js.Exception) {
|
||||
return .{ .local = self, .handle = isolate.throwException(value.handle) };
|
||||
}
|
||||
|
||||
if (T == js.String) {
|
||||
return .{ .local = self, .handle = @ptrCast(value.handle) };
|
||||
inline
|
||||
js.Function.Global,
|
||||
js.Function.Temp,
|
||||
js.Value.Global,
|
||||
js.Value.Temp,
|
||||
js.Object.Global,
|
||||
js.Promise.Global,
|
||||
js.PromiseResolver.Global,
|
||||
js.Module.Global => return .{ .local = self, .handle = @ptrCast(value.local(self).handle) },
|
||||
else => {}
|
||||
}
|
||||
// zig fmt: on
|
||||
|
||||
if (@hasDecl(T, "runtimeGenericWrap")) {
|
||||
const wrap = try value.runtimeGenericWrap(self.ctx.page);
|
||||
@@ -596,17 +564,17 @@ 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, js.Function.Global, js.Function.Temp => {
|
||||
if (!js_val.isFunction()) {
|
||||
return null;
|
||||
}
|
||||
return .{ .local = self, .handle = @ptrCast(js_val.handle) };
|
||||
},
|
||||
js.Function.Global => {
|
||||
if (!js_val.isFunction()) {
|
||||
return null;
|
||||
}
|
||||
return try (js.Function{ .local = self, .handle = @ptrCast(js_val.handle) }).persist();
|
||||
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,
|
||||
};
|
||||
},
|
||||
// zig fmt: off
|
||||
js.TypedArray(u8), js.TypedArray(u16), js.TypedArray(u32), js.TypedArray(u64),
|
||||
@@ -620,6 +588,7 @@ 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;
|
||||
|
||||
Reference in New Issue
Block a user