diff --git a/build.zig.zon b/build.zig.zon index aa2dda2d2..6f7ee9c15 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,10 +13,10 @@ .hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd", }, .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/97bcfb61da8c97de1321d677a6727a927a9db9a4.tar.gz", - .hash = "v8-0.0.0-xddH69DoIADZ8YXZ_EIx_tKdQKEoGsgob_3_ZIi0O_nV", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/363e2899e6d782ad999edbfae048228871230467.tar.gz", + .hash = "v8-0.0.0-xddH6wHzIAARDy1uFvPqqBpTXzhlnEGDTuX9IAUQz3oU", }, - //.v8 = .{ .path = "../zig-v8-fork" }, + // .v8 = .{ .path = "../zig-v8-fork" }, //.tigerbeetle_io = .{ .path = "../tigerbeetle-io" }, }, } diff --git a/src/browser/browser.zig b/src/browser/browser.zig index c5f96b368..fe654d73e 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -193,7 +193,7 @@ pub const Session = struct { self.state.cookie_jar = &self.cookie_jar; errdefer self.arena.deinit(); - self.executor = try browser.env.startExecutor(Window, &self.state, self, .main); + self.executor = try browser.env.startExecutor(Window, &self.state, self); errdefer browser.env.stopExecutor(self.executor); self.inspector = try Env.Inspector.init(self.arena.allocator(), self.executor, ctx); diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index ed2973552..b8d257aba 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -168,6 +168,11 @@ pub fn CDPT(comptime TypeProvider: type) type { } fn dispatchCommand(command: anytype, method: []const u8) !void { + const v8 = @import("v8"); + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, command.cdp.browser.env.isolate); + defer temp_scope.deinit(); + const domain = blk: { const i = std.mem.indexOfScalarPos(u8, method, 0, '.') orelse { return error.InvalidMethod; @@ -340,7 +345,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { pub fn deinit(self: *Self) void { if (self.isolated_world) |isolated_world| { - isolated_world.executor.endScope(); + isolated_world.executor.context.enter(); self.cdp.browser.env.stopExecutor(isolated_world.executor); self.isolated_world = null; } @@ -360,8 +365,12 @@ pub fn BrowserContext(comptime CDP_T: type) type { ) !void { if (self.isolated_world != null) return error.CurrentlyOnly1IsolatedWorldSupported; - const executor = try self.cdp.browser.env.startExecutor(@import("../browser/html/window.zig").Window, &self.session.state, self.session, .isolated); - errdefer self.cdp.browser.env.stopExecutor(executor); + const executor = try self.cdp.browser.env.startExecutor(@import("../browser/html/window.zig").Window, &self.session.state, self.session); + executor.context.exit(); + errdefer { + executor.context.enter(); + self.cdp.browser.env.stopExecutor(executor); + } // TBD should we endScope on removePage and re-startScope on createPage? // Window will be refactored into the executor so we leave it ugly here for now as a reminder. diff --git a/src/cdp/testing.zig b/src/cdp/testing.zig index 4dd9c4dee..35751732f 100644 --- a/src/cdp/testing.zig +++ b/src/cdp/testing.zig @@ -114,12 +114,11 @@ const Session = struct { const Env = struct { pub const Executor = MockExecutor; - pub fn startExecutor(self: *Env, comptime Global: type, state: anytype, module_loader: anytype, kind: anytype) !*Executor { + pub fn startExecutor(self: *Env, comptime Global: type, state: anytype, module_loader: anytype) !*Executor { _ = self; _ = Global; _ = state; _ = module_loader; - _ = kind; return error.MockExecutor; } pub fn stopExecutor(self: *Env, executor: *Executor) void { diff --git a/src/main.zig b/src/main.zig index 2dcbfffb8..5e45f81d8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -97,6 +97,11 @@ pub fn main() !void { var browser = try Browser.init(app); defer browser.deinit(); + const v8 = @import("v8"); + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, browser.env.isolate); + defer temp_scope.deinit(); + var session = try browser.newSession({}); // page diff --git a/src/runtime/js.zig b/src/runtime/js.zig index e1a577c6d..41f58a635 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -155,9 +155,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // the global isolate isolate: v8.Isolate, - // this is the global scope that all our classes are defined in - global_scope: v8.HandleScope, - // just kept around because we need to free it on deinit isolate_params: v8.CreateParams, @@ -180,9 +177,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // Send a lowMemoryNotification whenever we stop an executor gc_hints: bool, - // Used in debug mode to assert that we only have one executor at a time - has_executor: if (builtin.mode == .Debug) bool else void = if (builtin.mode == .Debug) false else {}, - const Self = @This(); const State = S; @@ -203,9 +197,9 @@ pub fn Env(comptime S: type, comptime types: anytype) type { isolate.enter(); errdefer isolate.exit(); - var global_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&global_scope, isolate); - errdefer global_scope.deinit(); + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, isolate); + defer temp_scope.deinit(); const env = try allocator.create(Self); errdefer allocator.destroy(env); @@ -216,7 +210,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { .allocator = allocator, .isolate_params = params, .gc_hints = opts.gc_hints, - .global_scope = global_scope, .prototype_lookup = undefined, .executor_pool = std.heap.MemoryPool(Executor).init(allocator), }; @@ -227,7 +220,9 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // we can get its index via: @field(TYPE_LOOKUP, type_name).index const templates = &env.templates; inline for (Types, 0..) |s, i| { - templates[i] = env.generateClass(@field(types, s.name)); + @setEvalBranchQuota(10_000); + // TODO kinda shot deinit, however these live for the livetime of the isolate anyways + templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, env.generateClass(@field(types, s.name))).castToFunctionTemplate(); } // Above, we've created all our our FunctionTemplates. Now that we @@ -253,7 +248,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { } pub fn deinit(self: *Self) void { - self.global_scope.deinit(); self.isolate.exit(); self.isolate.deinit(); self.executor_pool.deinit(); @@ -265,22 +259,10 @@ pub fn Env(comptime S: type, comptime types: anytype) type { self.isolate.performMicrotasksCheckpoint(); } - pub fn startExecutor(self: *Self, comptime Global: type, state: State, module_loader: anytype, kind: WorldKind) !*Executor { - if (comptime builtin.mode == .Debug) { - if (kind == .main) { - std.debug.assert(self.has_executor == false); - self.has_executor = true; - } - } + pub fn startExecutor(self: *Self, comptime Global: type, state: State, module_loader: anytype) !*Executor { const isolate = self.isolate; const templates = &self.templates; - // Acts like an Arena. Most things V8 has to allocate from this point - // on will be tied to this handle_scope - which we deinit in - // stopExecutor - var handle_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&handle_scope, isolate); - // The global FunctionTemplate (i.e. Window). const globals = v8.FunctionTemplate.initDefault(isolate); @@ -308,9 +290,10 @@ pub fn Env(comptime S: type, comptime types: anytype) type { globals.inherit(templates[proto_index]); } - const context = v8.Context.init(isolate, global_template, null); - if (kind == .main) context.enter(); - errdefer if (kind == .main) context.exit(); + const context_local = v8.Context.init(isolate, global_template, null); + const context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext(); + context.enter(); + errdefer context.exit(); // This shouldn't be necessary, but it is: // https://groups.google.com/g/v8-users/c/qAQQBmbi--8 @@ -347,10 +330,8 @@ pub fn Env(comptime S: type, comptime types: anytype) type { executor.* = .{ .state = state, .isolate = isolate, - .kind = kind, .context = context, .templates = templates, - .handle_scope = handle_scope, .call_arena = undefined, .scope_arena = undefined, ._call_arena_instance = std.heap.ArenaAllocator.init(self.allocator), @@ -389,13 +370,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // `gc_hints` option is enabled, we'll use the `lowMemoryNotification` // call on the isolate to encourage v8 to free the context. pub fn stopExecutor(self: *Self, executor: *Executor) void { - if (comptime builtin.mode == .Debug) { - if (executor.kind == .main) { - std.debug.assert(self.has_executor == true); - self.has_executor = false; - } - } - executor.deinit(); self.executor_pool.destroy(executor); if (self.gc_hints) { @@ -770,22 +744,14 @@ pub fn Env(comptime S: type, comptime types: anytype) type { const PersistentObject = v8.Persistent(v8.Object); const PersistentFunction = v8.Persistent(v8.Function); - const WorldKind = enum { - main, - isolated, - worker, - }; - // This is capable of executing JavaScript. pub const Executor = struct { state: State, isolate: v8.Isolate, - kind: WorldKind, - - handle_scope: v8.HandleScope, // @intFromPtr of our Executor is stored in this context, so given // a context, we can always get the Executor back. + // This context is a persistent object. The persistent needs to be recovered and reset. context: v8.Context, // Because calls can be nested (i.e.a function calling a callback), @@ -833,8 +799,9 @@ pub fn Env(comptime S: type, comptime types: anytype) type { fn deinit(self: *Executor) void { if (self.scope != null) self.endScope(); - if (self.kind == .main) self.context.exit(); - self.handle_scope.deinit(); + self.context.exit(); + var presistent_context = v8.Persistent(v8.Context).recoverCast(self.context); + presistent_context.deinit(); self._call_arena_instance.deinit(); self._scope_arena_instance.deinit(); @@ -926,12 +893,11 @@ pub fn Env(comptime S: type, comptime types: anytype) type { pub fn startScope(self: *Executor, global: anytype) !void { std.debug.assert(self.scope == null); - var handle_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&handle_scope, self.isolate); self.scope = Scope{ - .handle_scope = handle_scope, .arena = self.scope_arena, }; + + // DO we still need this? _ = try self._mapZigInstanceToJs(self.context.getGlobal(), global); } @@ -1130,7 +1096,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // in executor, rather than having one for each of these. pub const Scope = struct { arena: Allocator, - handle_scope: v8.HandleScope, callbacks: std.ArrayListUnmanaged(v8.Persistent(v8.Function)) = .{}, identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .{}, @@ -1142,7 +1107,6 @@ pub fn Env(comptime S: type, comptime types: anytype) type { for (self.callbacks.items) |*cb| { cb.deinit(); } - self.handle_scope.deinit(); } fn trackCallback(self: *Scope, pf: PersistentFunction) !void { @@ -1173,14 +1137,26 @@ pub fn Env(comptime S: type, comptime types: anytype) type { } pub fn call(self: *const Callback, args: anytype) !void { + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, self.executor.isolate); + defer temp_scope.deinit(); + return self.callWithThis(self.getThis(), args); } pub fn tryCall(self: *const Callback, args: anytype, result: *Result) !void { + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, self.executor.isolate); + defer temp_scope.deinit(); + return self.tryCallWithThis(self.getThis(), args, result); } pub fn tryCallWithThis(self: *const Callback, this: anytype, args: anytype, result: *Result) !void { + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, self.executor.isolate); + defer temp_scope.deinit(); + var try_catch: TryCatch = undefined; try_catch.init(self.executor); defer try_catch.deinit(); @@ -1199,6 +1175,10 @@ pub fn Env(comptime S: type, comptime types: anytype) type { } pub fn callWithThis(self: *const Callback, this: anytype, args: anytype) !void { + var temp_scope: v8.HandleScope = undefined; + v8.HandleScope.init(&temp_scope, self.executor.isolate); + defer temp_scope.deinit(); + const executor = self.executor; const js_this = try executor.valueToExistingObject(this); diff --git a/src/runtime/testing.zig b/src/runtime/testing.zig index fcfab82b1..2a09b2e82 100644 --- a/src/runtime/testing.zig +++ b/src/runtime/testing.zig @@ -43,7 +43,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty const G = if (Global == void) DefaultGlobal else Global; - runner.executor = try runner.env.startExecutor(G, state, runner, .main); + runner.executor = try runner.env.startExecutor(G, state, runner); errdefer runner.env.stopExecutor(runner.executor); try runner.executor.startScope(if (Global == void) &default_global else global); diff --git a/src/testing.zig b/src/testing.zig index 191dde5b6..e37b5e40e 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -433,7 +433,7 @@ pub const JsRunner = struct { .tls_verify_host = false, }); - runner.executor = try runner.env.startExecutor(Window, &runner.state, runner, .main); + runner.executor = try runner.env.startExecutor(Window, &runner.state, runner); errdefer runner.env.stopExecutor(runner.executor); try runner.executor.startScope(&runner.window);