From 3b1cd06615181b2a06e9afefce615d19173672b9 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 29 Dec 2025 20:02:06 +0800 Subject: [PATCH 01/26] Make js.Array and js.Value directly contain their v8 handles. --- build.zig.zon | 4 +- src/browser/js/Array.zig | 22 +++++---- src/browser/js/Context.zig | 20 ++++---- src/browser/js/Integer.zig | 35 ++++++++++++++ src/browser/js/Value.zig | 47 +++++++++++-------- src/browser/js/global.zig | 48 ++++++++++++++++++++ src/browser/js/js.zig | 3 ++ src/browser/webapi/CustomElementRegistry.zig | 2 +- 8 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 src/browser/js/Integer.zig create mode 100644 src/browser/js/global.zig diff --git a/build.zig.zon b/build.zig.zon index 2053538f..805a294c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,8 +6,8 @@ .minimum_zig_version = "0.15.2", .dependencies = .{ .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/d6b5f89cfc7feece29359e8c848bb916e8ecfab6.tar.gz", - .hash = "v8-0.0.0-xddH6_0gBABrJc5cL6-P2wGvvweTTCgWdpmClr9r-C-s", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/direct_v8.tar.gz", + .hash = "v8-0.0.0-xddH69smBABCCW8Q-9pislHtX8OolAmcuHk8QoTPx78F", }, //.v8 = .{ .path = "../zig-v8-fork" }, .@"boringssl-zig" = .{ diff --git a/src/browser/js/Array.zig b/src/browser/js/Array.zig index 95bc0e32..95ccf4e3 100644 --- a/src/browser/js/Array.zig +++ b/src/browser/js/Array.zig @@ -21,18 +21,24 @@ const js = @import("js.zig"); const v8 = js.v8; const Array = @This(); -js_arr: v8.Array, -context: *js.Context, + +ctx: *js.Context, +handle: *const v8.c.Array, pub fn len(self: Array) usize { - return @intCast(self.js_arr.length()); + return v8.c.v8__Array__Length(self.handle); } -pub fn get(self: Array, index: usize) !js.Value { - const idx_key = v8.Integer.initU32(self.context.isolate, @intCast(index)); - const js_obj = self.js_arr.castTo(v8.Object); +pub fn get(self: Array, index: u32) !js.Value { + const ctx = self.ctx; + + const idx = js.Integer.init(ctx.isolate.handle, index); + const handle = v8.c.v8__Object__Get(@ptrCast(self.handle), ctx.v8_context.handle, idx.handle) orelse { + return error.JsException; + }; + return .{ - .context = self.context, - .js_val = try js_obj.getValue(self.context.v8_context, idx_key.toValue()), + .ctx = self.ctx, + .handle = handle, }; } diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index e80a658b..c3e397cc 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -86,8 +86,8 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty, // we now simply persist every time persist() is called. js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty, -// js_value_list tracks persisted js values. -js_value_list: std.ArrayListUnmanaged(PersistentValue) = .empty, +// tracks Global(v8.c.Value). +global_values: std.ArrayList(js.Global(js.Value)) = .empty, // Various web APIs depend on having a persistent promise resolver. They // require for this PromiseResolver to be valid for a lifetime longer than @@ -165,8 +165,8 @@ pub fn deinit(self: *Context) void { p.deinit(); } - for (self.js_value_list.items) |*p| { - p.deinit(); + for (self.global_values.items) |*global| { + global.deinit(); } for (self.persisted_promise_resolvers.items) |*p| { @@ -374,12 +374,10 @@ pub fn createException(self: *const Context, e: v8.Value) js.Exception { }; } -// Wrap a v8.Value, largely so that we can provide a convenient -// toString function pub fn createValue(self: *Context, value: v8.Value) js.Value { return .{ - .js_val = value, - .context = self, + .ctx = self, + .handle = value.handle, }; } @@ -499,7 +497,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp } if (T == js.Value) { - return value.js_val; + return .{ .handle = value.handle }; } if (T == js.Promise) { @@ -837,8 +835,8 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: v8.Value) !?T { // Caller wants an opaque js.Object. Probably a parameter // that it needs to pass back into a callback. js.Value => js.Value{ - .js_val = js_value, - .context = self, + .ctx = self, + .handle = js_value.handle, }, // Caller wants an opaque js.Object. Probably a parameter // that it needs to pass back into a callback. diff --git a/src/browser/js/Integer.zig b/src/browser/js/Integer.zig new file mode 100644 index 00000000..5d7cef5b --- /dev/null +++ b/src/browser/js/Integer.zig @@ -0,0 +1,35 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// 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 . + +const std = @import("std"); +const js = @import("js.zig"); + +const v8 = js.v8; + +const Integer = @This(); + +handle: *const v8.c.Integer, + +pub fn init(isolate: *v8.c.Isolate, value: anytype) Integer { + const handle = switch (@TypeOf(value)) { + i8, i16, i32 => v8.c.v8__Integer__New(isolate, value).?, + u8, u16, u32 => v8.c.v8__Integer__NewFromUnsigned(isolate, value).?, + else => |T| @compileError("cannot create v8::Integer from: " ++ @typeName(T)), + }; + return .{ .handle = handle }; +} diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 90962415..43892733 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -21,24 +21,25 @@ const js = @import("js.zig"); const v8 = js.v8; +const IS_DEBUG = @import("builtin").mode == .Debug; + const Allocator = std.mem.Allocator; -const PersistentValue = v8.Persistent(v8.Value); - const Value = @This(); -js_val: v8.Value, -context: *js.Context, + +ctx: *js.Context, +handle: *const v8.c.Value, pub fn isObject(self: Value) bool { - return self.js_val.isObject(); + return v8.c.v8__Value__IsObject(self.handle); } pub fn isString(self: Value) bool { - return self.js_val.isString(); + return v8.c.v8__Value__IsString(self.handle); } pub fn isArray(self: Value) bool { - return self.js_val.isArray(); + return v8.c.v8__Value__IsArray(self.handle); } pub fn isNull(self: Value) bool { @@ -50,7 +51,7 @@ pub fn isUndefined(self: Value) bool { } pub fn toString(self: Value, allocator: Allocator) ![]const u8 { - return self.context.valueToString(self.js_val, .{ .allocator = allocator }); + return self.ctx.valueToString(.{ .handle = self.handle }, .{ .allocator = allocator }); } pub fn toBool(self: Value) bool { @@ -60,17 +61,19 @@ pub fn toBool(self: Value) bool { pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { const json_string = v8.String.initUtf8(ctx.isolate, json); const value = try v8.Json.parse(ctx.v8_context, json_string); - return Value{ .context = ctx, .js_val = value }; + return .{ .ctx = ctx, .handle = value.handle }; } pub fn persist(self: Value) !Value { - const js_val = self.js_val; - var context = self.context; + var ctx = self.ctx; - const persisted = PersistentValue.init(context.isolate, js_val); - try context.js_value_list.append(context.arena, persisted); + const global = js.Global(Value).init(ctx.isolate.handle, self); + try ctx.global_values.append(ctx.arena, global); - return Value{ .context = context, .js_val = persisted.toValue() }; + return .{ + .ctx = ctx, + .handle = global.local(), + }; } pub fn toZig(self: Value, comptime T: type) !T { @@ -78,15 +81,23 @@ pub fn toZig(self: Value, comptime T: type) !T { } pub fn toObject(self: Value) js.Object { + if (comptime IS_DEBUG) { + std.debug.assert(self.isObject()); + } + return .{ - .context = self.context, - .js_obj = self.js_val.castTo(v8.Object), + .context = self.ctx, + .js_obj = .{ .handle = self.handle }, }; } pub fn toArray(self: Value) js.Array { + if (comptime IS_DEBUG) { + std.debug.assert(self.isArray()); + } + return .{ - .context = self.context, - .js_arr = self.js_val.castTo(v8.Array), + .ctx = self.ctx, + .handle = self.handle, }; } diff --git a/src/browser/js/global.zig b/src/browser/js/global.zig new file mode 100644 index 00000000..94540b65 --- /dev/null +++ b/src/browser/js/global.zig @@ -0,0 +1,48 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// 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 . + +const std = @import("std"); +const js = @import("js.zig"); + +const v8 = js.v8; + +pub fn Global(comptime T: type) type { + const H = @FieldType(T, "handle"); + + return struct { + global: v8.c.Global, + + const Self = @This(); + + pub fn init(isolate: *v8.c.Isolate, data: T) Self { + var global: v8.c.Global = undefined; + v8.c.v8__Global__New(isolate, data.handle, &global); + return .{ + .global = global, + }; + } + + pub fn deinit(self: *Self) void { + v8.c.v8__Global__Reset(&self.global); + } + + pub fn local(self: *const Self) H { + return @ptrCast(@alignCast(@as(*const anyopaque, @ptrFromInt(self.global.data_ptr)))); + } + }; +} diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index f3e30c9b..82a81979 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -37,6 +37,9 @@ pub const Object = @import("Object.zig"); pub const TryCatch = @import("TryCatch.zig"); pub const Function = @import("Function.zig"); +pub const Integer = @import("Integer.zig"); +pub const Global = @import("global.zig").Global; + const Allocator = std.mem.Allocator; pub fn Bridge(comptime T: type) type { diff --git a/src/browser/webapi/CustomElementRegistry.zig b/src/browser/webapi/CustomElementRegistry.zig index b0646c9a..2cb8c2b1 100644 --- a/src/browser/webapi/CustomElementRegistry.zig +++ b/src/browser/webapi/CustomElementRegistry.zig @@ -72,7 +72,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu if (observed_attrs.isArray()) { var js_arr = observed_attrs.toArray(); for (0..js_arr.len()) |i| { - const attr_val = js_arr.get(i) catch continue; + const attr_val = js_arr.get(@intCast(i)) catch continue; const attr_name = attr_val.toString(page.arena) catch continue; const owned_attr = page.dupeString(attr_name) catch continue; definition.observed_attributes.put(page.arena, owned_attr, {}) catch continue; From 8aaef674fe558c184edeadf811c8f0a5f22e2304 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 30 Dec 2025 16:27:42 +0800 Subject: [PATCH 02/26] Migrate Function and String --- src/browser/EventManager.zig | 2 +- src/browser/Page.zig | 2 +- src/browser/js/Context.zig | 29 ++---- src/browser/js/Function.zig | 94 ++++++++++++------- src/browser/js/String.zig | 53 +++++++++++ src/browser/js/Value.zig | 32 ++++++- src/browser/js/global.zig | 4 +- src/browser/js/js.zig | 10 +- src/browser/webapi/AbortSignal.zig | 2 +- src/browser/webapi/CustomElementRegistry.zig | 4 +- src/browser/webapi/EventTarget.zig | 2 +- src/browser/webapi/IntersectionObserver.zig | 7 +- src/browser/webapi/KeyValueList.zig | 2 +- src/browser/webapi/MessagePort.zig | 4 +- src/browser/webapi/MutationObserver.zig | 2 +- src/browser/webapi/NodeFilter.zig | 9 +- src/browser/webapi/Window.zig | 16 ++-- src/browser/webapi/element/html/Script.zig | 22 +++-- src/browser/webapi/media/TextTrackCue.zig | 4 +- .../navigation/NavigationEventTarget.zig | 2 +- src/browser/webapi/net/URLSearchParams.zig | 2 +- src/browser/webapi/net/XMLHttpRequest.zig | 2 +- .../webapi/net/XMLHttpRequestEventTarget.zig | 14 +-- src/browser/webapi/streams/ReadableStream.zig | 2 +- 24 files changed, 212 insertions(+), 110 deletions(-) create mode 100644 src/browser/js/String.zig diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index 067cd367..6c188b83 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -449,7 +449,7 @@ const Function = union(enum) { fn eqlFunction(self: Function, func: js.Function) bool { return switch (self) { - .value => |v| return v.id == func.id, + .value => |v| return v.id() == func.id(), else => false, }; } diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 032e5ac6..86374179 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -1309,7 +1309,7 @@ pub fn appendNew(self: *Page, parent: *Node, child: Node.NodeOrText) !void { // called from the parser when the node and all its children have been added pub fn nodeComplete(self: *Page, node: *Node) !void { Node.Build.call(node, "complete", .{ node, self }) catch |err| { - log.err(.bug, "build.complete", .{ .tag = node.getNodeName(self), .err = err }); + log.err(.bug, "build.complete", .{ .tag = node.getNodeName(&self.buf), .err = err }); return err; }; return self.nodeIsReady(true, node); diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index c3e397cc..c8fc312c 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -65,10 +65,6 @@ call_arena: Allocator, // the call which is calling the callback. call_depth: usize = 0, -// Callbacks are PesistendObjects. When the context ends, we need -// to free every callback we created. -callbacks: std.ArrayListUnmanaged(v8.Persistent(v8.Function)) = .empty, - // Serves two purposes. Like `callbacks` above, this is used to free // every PeristentObjet we've created during the lifetime of the context. // More importantly, it serves as an identity map - for a given Zig @@ -86,8 +82,8 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty, // we now simply persist every time persist() is called. js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty, -// tracks Global(v8.c.Value). global_values: std.ArrayList(js.Global(js.Value)) = .empty, +global_functions: std.ArrayList(js.Global(js.Function)) = .empty, // Various web APIs depend on having a persistent promise resolver. They // require for this PromiseResolver to be valid for a lifetime longer than @@ -169,6 +165,10 @@ pub fn deinit(self: *Context) void { global.deinit(); } + for (self.global_functions.items) |*global| { + global.deinit(); + } + for (self.persisted_promise_resolvers.items) |*p| { p.deinit(); } @@ -188,9 +188,6 @@ pub fn deinit(self: *Context) void { } } - for (self.callbacks.items) |*cb| { - cb.deinit(); - } if (self.handle_scope) |*scope| { scope.deinit(); self.v8_context.exit(); @@ -199,10 +196,6 @@ pub fn deinit(self: *Context) void { presistent_context.deinit(); } -fn trackCallback(self: *Context, pf: PersistentFunction) !void { - return self.callbacks.append(self.arena, pf); -} - // == Executors == pub fn eval(self: *Context, src: []const u8, name: ?[]const u8) !void { _ = try self.exec(src, name); @@ -392,13 +385,9 @@ pub fn createFunction(self: *Context, js_value: v8.Value) !js.Function { // caller should have made sure this was a function std.debug.assert(js_value.isFunction()); - const func = v8.Persistent(v8.Function).init(self.isolate, js_value.castTo(v8.Function)); - try self.trackCallback(func); - return .{ - .func = func, - .context = self, - .id = js_value.castTo(v8.Object).getIdentityHash(), + .ctx = self, + .handle = @ptrCast(js_value.handle), }; } @@ -488,7 +477,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp if (T == js.Function) { // we're returning a callback - return value.func.toValue(); + return .{ .handle = @ptrCast(value.handle) }; } if (T == js.Object) { @@ -2031,7 +2020,7 @@ pub fn queueSlotchangeDelivery(self: *Context) !void { } pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void { - self.isolate.enqueueMicrotaskFunc(cb.func.castToFunction()); + self.isolate.enqueueMicrotaskFunc(.{ .handle = cb.handle }); } // == Misc == diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index 109fa231..21d45014 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -20,24 +20,25 @@ const std = @import("std"); const js = @import("js.zig"); const v8 = js.v8; -const PersistentFunction = v8.Persistent(v8.Function); - const Allocator = std.mem.Allocator; const Function = @This(); -id: usize, -context: *js.Context, +ctx: *js.Context, this: ?v8.Object = null, -func: PersistentFunction, +handle: *const v8.c.Function, pub const Result = struct { stack: ?[]const u8, exception: []const u8, }; +pub fn id(self: *const Function) u32 { + return @as(u32, @bitCast(v8.c.v8__Object__GetIdentityHash(@ptrCast(self.handle)))); +} + pub fn getName(self: *const Function, allocator: Allocator) ![]const u8 { - const name = self.func.castToFunction().getName(); + const name = v8.c.v8__Function__GetName(self.handle).?; return self.context.valueToString(name, .{ .allocator = allocator }); } @@ -50,28 +51,27 @@ pub fn withThis(self: *const Function, value: anytype) !Function { const this_obj = if (@TypeOf(value) == js.Object) value.js_obj else - (try self.context.zigValueToJs(value, .{})).castTo(v8.Object); + (try self.ctx.zigValueToJs(value, .{})).castTo(v8.Object); return .{ - .id = self.id, + .ctx = self.ctx, .this = this_obj, - .func = self.func, - .context = self.context, + .handle = self.handle, }; } pub fn newInstance(self: *const Function, result: *Result) !js.Object { - const context = self.context; + const ctx = self.ctx; var try_catch: js.TryCatch = undefined; - try_catch.init(context); + try_catch.init(ctx); defer try_catch.deinit(); // This creates a new instance using this Function as a constructor. - // This returns a generic Object - const js_obj = self.func.castToFunction().initInstance(context.v8_context, &.{}) orelse { + // const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{})); + const handle = v8.c.v8__Function__NewInstance(self.handle, ctx.v8_context.handle, 0, null) orelse { if (try_catch.hasCaught()) { - const allocator = context.call_arena; + const allocator = ctx.call_arena; result.stack = try_catch.stack(allocator) catch null; result.exception = (try_catch.exception(allocator) catch "???") orelse "???"; } else { @@ -82,8 +82,8 @@ pub fn newInstance(self: *const Function, result: *Result) !js.Object { }; return .{ - .context = context, - .js_obj = js_obj, + .context = ctx, + .js_obj = .{ .handle = handle }, }; } @@ -97,12 +97,13 @@ pub fn tryCall(self: *const Function, comptime T: type, args: anytype, result: * pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, result: *Result) !T { var try_catch: js.TryCatch = undefined; - try_catch.init(self.context); + + try_catch.init(self.ctx); defer try_catch.deinit(); return self.callWithThis(T, this, args) catch |err| { if (try_catch.hasCaught()) { - const allocator = self.context.call_arena; + const allocator = self.ctx.call_arena; result.stack = try_catch.stack(allocator) catch null; result.exception = (try_catch.exception(allocator) catch @errorName(err)) orelse @errorName(err); } else { @@ -114,8 +115,9 @@ pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, a } pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype) !T { - const context = self.context; + const ctx = self.ctx; +<<<<<<< HEAD // When we're calling a function from within JavaScript itself, this isn't // necessary. We're within a Caller instantiation, which will already have // incremented the call_depth and it won't decrement it until the Caller is @@ -147,33 +149,35 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args const fields = s.fields; var js_args: [fields.len]v8.Value = undefined; inline for (fields, 0..) |f, i| { - js_args[i] = try context.zigValueToJs(@field(aargs, f.name), .{}); + js_args[i] = try ctx.zigValueToJs(@field(aargs, f.name), .{}); } const cargs: [fields.len]v8.Value = js_args; break :blk &cargs; }, .pointer => blk: { - var values = try context.call_arena.alloc(v8.Value, args.len); + var values = try ctx.call_arena.alloc(v8.Value, args.len); for (args, 0..) |a, i| { - values[i] = try context.zigValueToJs(a, .{}); + values[i] = try ctx.zigValueToJs(a, .{}); } break :blk values; }, else => @compileError("JS Function called with invalid paremter type"), }; - const result = self.func.castToFunction().call(context.v8_context, js_this, js_args); - if (result == null) { + const c_args = @as(?[*]const ?*v8.c.Value, @ptrCast(js_args.ptr)); + const handle = v8.c.v8__Function__Call(self.handle, ctx.v8_context.handle, js_this.handle, @as(c_int, @intCast(js_args.len)), c_args) orelse { // std.debug.print("CB ERR: {s}\n", .{self.src() catch "???"}); return error.JSExecCallback; - } + }; - if (@typeInfo(T) == .void) return {}; - return context.jsValueToZig(T, result.?); + if (@typeInfo(T) == .void) { + return {}; + } + return ctx.jsValueToZig(T, .{ .handle = handle }); } fn getThis(self: *const Function) v8.Object { - return self.this orelse self.context.v8_context.getGlobal(); + return self.this orelse self.ctx.v8_context.getGlobal(); } pub fn src(self: *const Function) ![]const u8 { @@ -182,8 +186,32 @@ pub fn src(self: *const Function) ![]const u8 { } pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value { - const func_obj = self.func.castToFunction().toObject(); - const key = v8.String.initUtf8(self.context.isolate, name); - const value = func_obj.getValue(self.context.v8_context, key) catch return null; - return self.context.createValue(value); + const ctx = self.ctx; + const key = v8.String.initUtf8(ctx.isolate, name); + const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse { + return error.JsException; + }; + + return .{ + .ctx = ctx, + .handle = handle, + }; +} + +pub fn persist(self: *const Function) !Function { + var ctx = self.ctx; + + const global = js.Global(Function).init(ctx.isolate.handle, self.handle); + try ctx.global_functions.append(ctx.arena, global); + + return .{ + .ctx = ctx, + .this = self.this, + .handle = global.local(), + }; +} + +pub fn persistWithThis(self: *const Function, value: anytype) !Function { + var persisted = try self.persist(); + return persisted.withThis(value); } diff --git a/src/browser/js/String.zig b/src/browser/js/String.zig new file mode 100644 index 00000000..a170e538 --- /dev/null +++ b/src/browser/js/String.zig @@ -0,0 +1,53 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// 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 . + +const std = @import("std"); +const js = @import("js.zig"); + +const Allocator = std.mem.Allocator; + +const v8 = js.v8; + +const String = @This(); + +ctx: *js.Context, +handle: *const v8.c.String, + +pub const ToZigOpts = struct { + allocator: ?Allocator = null, +}; + +pub fn toZig(self: String, opts: ToZigOpts) ![]u8 { + return self._toZig(false, opts); +} + +pub fn toZigZ(self: String, opts: ToZigOpts) ![:0]u8 { + return self._toZig(true, opts); +} + +fn _toZig(self: String, comptime null_terminate: bool, opts: ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) { + const isolate = self.ctx.isolate.handle; + const allocator = opts.allocator orelse self.ctx.call_arena; + const len: u32 = @intCast(v8.c.v8__String__Utf8Length(self.handle, isolate)); + const buf = if (null_terminate) try allocator.allocSentinel(u8, len, 0) else try allocator.alloc(u8, len); + + const options = v8.c.NO_NULL_TERMINATION | v8.c.REPLACE_INVALID_UTF8; + const n = v8.c.v8__String__WriteUtf8(self.handle, isolate, buf.ptr, buf.len, options); + std.debug.assert(n == len); + return buf; +} diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 43892733..b419688d 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -50,8 +50,34 @@ pub fn isUndefined(self: Value) bool { return self.js_val.isUndefined(); } -pub fn toString(self: Value, allocator: Allocator) ![]const u8 { - return self.ctx.valueToString(.{ .handle = self.handle }, .{ .allocator = allocator }); +pub fn isSymbol(self: Value) bool { + return v8.c.v8__Value__IsSymbol(self.handle); +} + +pub fn toString(self: Value, opts: js.String.ToZigOpts) ![]u8 { + return self._toString(false, opts); +} +pub fn toStringZ(self: Value, opts: js.String.ToZigOpts) ![:0]u8 { + return self._toString(true, opts); +} + +fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) { + const ctx = self.ctx; + + if (self.isSymbol()) { + const sym_handle = v8.c.v8__Symbol__Description(@ptrCast(self.handle), ctx.isolate.handle).?; + return _toString(.{ .handle = @ptrCast(sym_handle), .ctx = ctx }, null_terminate, opts); + } + + const str_handle = v8.c.v8__Value__ToString(self.handle, ctx.v8_context.handle) orelse { + return error.JsException; + }; + + const str = js.String{ .ctx = ctx, .handle = str_handle }; + if (comptime null_terminate) { + return js.String.toZigZ(str, opts); + } + return js.String.toZig(str, opts); } pub fn toBool(self: Value) bool { @@ -67,7 +93,7 @@ pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { pub fn persist(self: Value) !Value { var ctx = self.ctx; - const global = js.Global(Value).init(ctx.isolate.handle, self); + const global = js.Global(Value).init(ctx.isolate.handle, self.handle); try ctx.global_values.append(ctx.arena, global); return .{ diff --git a/src/browser/js/global.zig b/src/browser/js/global.zig index 94540b65..82c2c55b 100644 --- a/src/browser/js/global.zig +++ b/src/browser/js/global.zig @@ -29,9 +29,9 @@ pub fn Global(comptime T: type) type { const Self = @This(); - pub fn init(isolate: *v8.c.Isolate, data: T) Self { + pub fn init(isolate: *v8.c.Isolate, handle: H) Self { var global: v8.c.Global = undefined; - v8.c.v8__Global__New(isolate, data.handle, &global); + v8.c.v8__Global__New(isolate, handle, &global); return .{ .global = global, }; diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 82a81979..55b9b724 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -33,6 +33,7 @@ pub const Platform = @import("Platform.zig"); pub const This = @import("This.zig"); pub const Value = @import("Value.zig"); pub const Array = @import("Array.zig"); +pub const String = @import("String.zig"); pub const Object = @import("Object.zig"); pub const TryCatch = @import("TryCatch.zig"); pub const Function = @import("Function.zig"); @@ -157,15 +158,6 @@ pub const PersistentPromiseResolver = struct { pub const Promise = v8.Promise; -// When doing jsValueToZig, string ([]const u8) are managed by the -// call_arena. That means that if the API wants to persist the string -// (which is relatively common), it needs to dupe it again. -// If the parameter is an Env.String rather than a []const u8, then -// the page's arena will be used (rather than the call arena). -pub const String = struct { - string: []const u8, -}; - pub const Exception = struct { inner: v8.Value, context: *const Context, diff --git a/src/browser/webapi/AbortSignal.zig b/src/browser/webapi/AbortSignal.zig index c4b0f7ed..f85afe23 100644 --- a/src/browser/webapi/AbortSignal.zig +++ b/src/browser/webapi/AbortSignal.zig @@ -51,7 +51,7 @@ pub fn getOnAbort(self: *const AbortSignal) ?js.Function { pub fn setOnAbort(self: *AbortSignal, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_abort = try cb.withThis(self); + self._on_abort = try cb.persistWithThis(self); } else { self._on_abort = null; } diff --git a/src/browser/webapi/CustomElementRegistry.zig b/src/browser/webapi/CustomElementRegistry.zig index 2cb8c2b1..70c67bef 100644 --- a/src/browser/webapi/CustomElementRegistry.zig +++ b/src/browser/webapi/CustomElementRegistry.zig @@ -63,7 +63,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu const definition = try page._factory.create(CustomElementDefinition{ .name = owned_name, - .constructor = constructor, + .constructor = try constructor.persist(), .extends = extends_tag, }); @@ -73,7 +73,7 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu var js_arr = observed_attrs.toArray(); for (0..js_arr.len()) |i| { const attr_val = js_arr.get(@intCast(i)) catch continue; - const attr_name = attr_val.toString(page.arena) catch continue; + const attr_name = attr_val.toString(.{ .allocator = page.arena }) catch continue; const owned_attr = page.dupeString(attr_name) catch continue; definition.observed_attributes.put(page.arena, owned_attr, {}) catch continue; } diff --git a/src/browser/webapi/EventTarget.zig b/src/browser/webapi/EventTarget.zig index 1c75b0f0..1e6d806b 100644 --- a/src/browser/webapi/EventTarget.zig +++ b/src/browser/webapi/EventTarget.zig @@ -72,8 +72,8 @@ pub fn addEventListener(self: *EventTarget, typ: []const u8, callback_: ?EventLi const callback = callback_ orelse return; const em_callback = switch (callback) { - .function => |func| EventManager.Callback{ .function = func }, .object => |obj| EventManager.Callback{ .object = try obj.persist() }, + .function => |func| EventManager.Callback{ .function = try func.persist() }, }; const options = blk: { diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index 5bc94428..42eedcf5 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -70,7 +70,12 @@ pub fn init(callback: js.Function, options: ?ObserverInit, page: *Page) !*Inters .array => |arr| try page.arena.dupe(f64, arr), }; - return page._factory.create(IntersectionObserver{ ._callback = callback, ._root = opts.root, ._root_margin = root_margin, ._threshold = threshold }); + return page._factory.create(IntersectionObserver{ + ._callback = try callback.persist(), + ._root = opts.root, + ._root_margin = root_margin, + ._threshold = threshold, + }); } pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void { diff --git a/src/browser/webapi/KeyValueList.zig b/src/browser/webapi/KeyValueList.zig index b91e6d3b..b239e7f0 100644 --- a/src/browser/webapi/KeyValueList.zig +++ b/src/browser/webapi/KeyValueList.zig @@ -68,7 +68,7 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?N while (try it.next()) |name| { const js_value = try js_obj.get(name); - const value = try js_value.toString(arena); + const value = try js_value.toString(.{ .allocator = arena }); const normalized = if (comptime normalizer) |n| n(name, page) else name; list._entries.appendAssumeCapacity(.{ diff --git a/src/browser/webapi/MessagePort.zig b/src/browser/webapi/MessagePort.zig index ea6e79ca..d6e85aa9 100644 --- a/src/browser/webapi/MessagePort.zig +++ b/src/browser/webapi/MessagePort.zig @@ -94,7 +94,7 @@ pub fn getOnMessage(self: *const MessagePort) ?js.Function { pub fn setOnMessage(self: *MessagePort, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_message = cb; + self._on_message = try cb.persist(); } else { self._on_message = null; } @@ -106,7 +106,7 @@ pub fn getOnMessageError(self: *const MessagePort) ?js.Function { pub fn setOnMessageError(self: *MessagePort, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_message_error = cb; + self._on_message_error = try cb.persist(); } else { self._on_message_error = null; } diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig index 1e35e2b8..d496406f 100644 --- a/src/browser/webapi/MutationObserver.zig +++ b/src/browser/webapi/MutationObserver.zig @@ -55,7 +55,7 @@ pub const ObserveOptions = struct { pub fn init(callback: js.Function, page: *Page) !*MutationObserver { return page._factory.create(MutationObserver{ - ._callback = callback, + ._callback = try callback.persist(), }); } diff --git a/src/browser/webapi/NodeFilter.zig b/src/browser/webapi/NodeFilter.zig index b1020fc5..a83cb5f2 100644 --- a/src/browser/webapi/NodeFilter.zig +++ b/src/browser/webapi/NodeFilter.zig @@ -36,10 +36,13 @@ pub const FilterOpts = union(enum) { pub fn init(opts_: ?FilterOpts) !NodeFilter { const opts = opts_ orelse return .{ ._func = null, ._original_filter = null }; const func = switch (opts) { - .function => |func| func, - .object => |obj| obj.acceptNode, + .function => |func| try func.persist(), + .object => |obj| try obj.acceptNode.persist(), + }; + return .{ + ._func = func, + ._original_filter = opts_, }; - return .{ ._func = func, ._original_filter = opts_ }; } // Constants diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 977273a9..24d7e97a 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -150,7 +150,7 @@ pub fn getOnLoad(self: *const Window) ?js.Function { } pub fn setOnLoad(self: *Window, setter: ?FunctionSetter) !void { - self._on_load = getFunctionFromSetter(setter); + self._on_load = try getFunctionFromSetter(setter); } pub fn getOnPageShow(self: *const Window) ?js.Function { @@ -158,7 +158,7 @@ pub fn getOnPageShow(self: *const Window) ?js.Function { } pub fn setOnPageShow(self: *Window, setter: ?FunctionSetter) !void { - self._on_pageshow = getFunctionFromSetter(setter); + self._on_pageshow = try getFunctionFromSetter(setter); } pub fn getOnPopState(self: *const Window) ?js.Function { @@ -166,7 +166,7 @@ pub fn getOnPopState(self: *const Window) ?js.Function { } pub fn setOnPopState(self: *Window, setter: ?FunctionSetter) !void { - self._on_popstate = getFunctionFromSetter(setter); + self._on_popstate = try getFunctionFromSetter(setter); } pub fn getOnError(self: *const Window) ?js.Function { @@ -174,7 +174,7 @@ pub fn getOnError(self: *const Window) ?js.Function { } pub fn setOnError(self: *Window, setter: ?FunctionSetter) !void { - self._on_error = getFunctionFromSetter(setter); + self._on_error = try getFunctionFromSetter(setter); } pub fn getOnUnhandledRejection(self: *const Window) ?js.Function { @@ -182,7 +182,7 @@ pub fn getOnUnhandledRejection(self: *const Window) ?js.Function { } pub fn setOnUnhandledRejection(self: *Window, setter: ?FunctionSetter) !void { - self._on_unhandled_rejection = getFunctionFromSetter(setter); + self._on_unhandled_rejection = try getFunctionFromSetter(setter); } pub fn fetch(_: *const Window, input: Fetch.Input, options: ?Fetch.InitOpts, page: *Page) !js.Promise { @@ -497,7 +497,7 @@ fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: Schedul errdefer _ = self._timers.remove(timer_id); const callback = try page._factory.create(ScheduleCallback{ - .cb = cb, + .cb = try cb.persist(), .page = page, .mode = opts.mode, .name = opts.name, @@ -622,10 +622,10 @@ const FunctionSetter = union(enum) { // window.onload = {}; doesn't fail, but it doesn't do anything. // seems like setting to null is ok (though, at least on Firefix, it preserves // the original value, which we could do, but why?) -fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function { +fn getFunctionFromSetter(setter_: ?FunctionSetter) !?js.Function { const setter = setter_ orelse return null; return switch (setter) { - .func => |func| func, + .func => |func| try func.persist(), .anything => null, }; } diff --git a/src/browser/webapi/element/html/Script.zig b/src/browser/webapi/element/html/Script.zig index c4fa83de..8531d275 100644 --- a/src/browser/webapi/element/html/Script.zig +++ b/src/browser/webapi/element/html/Script.zig @@ -80,7 +80,7 @@ pub fn getOnLoad(self: *const Script) ?js.Function { pub fn setOnLoad(self: *Script, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_load = cb; + self._on_load = try cb.persist(); } else { self._on_load = null; } @@ -92,7 +92,7 @@ pub fn getOnError(self: *const Script) ?js.Function { pub fn setOnError(self: *Script, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_error = cb; + self._on_error = try cb.persist(); } else { self._on_error = null; } @@ -136,16 +136,22 @@ pub const Build = struct { self._src = element.getAttributeSafe("src") orelse ""; if (element.getAttributeSafe("onload")) |on_load| { - self._on_load = page.js.stringToFunction(on_load) catch |err| blk: { - log.err(.js, "script.onload", .{ .err = err, .str = on_load }); - break :blk null; + self._on_load = blk: { + const func = page.js.stringToFunction(on_load) catch |err| { + log.err(.js, "script.onload", .{ .err = err, .str = on_load }); + break :blk null; + }; + break :blk try func.persist(); }; } if (element.getAttributeSafe("onerror")) |on_error| { - self._on_error = page.js.stringToFunction(on_error) catch |err| blk: { - log.err(.js, "script.onerror", .{ .err = err, .str = on_error }); - break :blk null; + self._on_error = blk: { + const func = page.js.stringToFunction(on_error) catch |err| { + log.err(.js, "script.onerror", .{ .err = err, .str = on_error }); + break :blk null; + }; + break :blk try func.persist(); }; } } diff --git a/src/browser/webapi/media/TextTrackCue.zig b/src/browser/webapi/media/TextTrackCue.zig index e590fa7f..b82a95f1 100644 --- a/src/browser/webapi/media/TextTrackCue.zig +++ b/src/browser/webapi/media/TextTrackCue.zig @@ -79,7 +79,7 @@ pub fn getOnEnter(self: *const TextTrackCue) ?js.Function { pub fn setOnEnter(self: *TextTrackCue, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_enter = try cb.withThis(self); + self._on_enter = try cb.persistWithThis(self); } else { self._on_enter = null; } @@ -91,7 +91,7 @@ pub fn getOnExit(self: *const TextTrackCue) ?js.Function { pub fn setOnExit(self: *TextTrackCue, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_exit = try cb.withThis(self); + self._on_exit = try cb.persistWithThis(self); } else { self._on_exit = null; } diff --git a/src/browser/webapi/navigation/NavigationEventTarget.zig b/src/browser/webapi/navigation/NavigationEventTarget.zig index 4ab6df66..7bf5d858 100644 --- a/src/browser/webapi/navigation/NavigationEventTarget.zig +++ b/src/browser/webapi/navigation/NavigationEventTarget.zig @@ -57,7 +57,7 @@ pub fn getOnCurrentEntryChange(self: *NavigationEventTarget) ?js.Function { pub fn setOnCurrentEntryChange(self: *NavigationEventTarget, listener: ?js.Function) !void { if (listener) |listen| { - self._on_currententrychange = try listen.withThis(self); + self._on_currententrychange = try listen.persistWithThis(self); } else { self._on_currententrychange = null; } diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 52615ccb..e9384d28 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -50,7 +50,7 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*URLSearchParams { break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page); } if (js_val.isString()) { - break :blk try paramsFromString(arena, try js_val.toString(arena), &page.buf); + break :blk try paramsFromString(arena, try js_val.toString(.{ .allocator = arena }), &page.buf); } return error.InvalidArgument; }, diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index c3aef7f6..93ee323f 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -104,7 +104,7 @@ pub fn getOnReadyStateChange(self: *const XMLHttpRequest) ?js.Function { pub fn setOnReadyStateChange(self: *XMLHttpRequest, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_ready_state_change = try cb.withThis(self); + self._on_ready_state_change = try cb.persistWithThis(self); } else { self._on_ready_state_change = null; } diff --git a/src/browser/webapi/net/XMLHttpRequestEventTarget.zig b/src/browser/webapi/net/XMLHttpRequestEventTarget.zig index b8efe47d..20bea353 100644 --- a/src/browser/webapi/net/XMLHttpRequestEventTarget.zig +++ b/src/browser/webapi/net/XMLHttpRequestEventTarget.zig @@ -77,7 +77,7 @@ pub fn getOnAbort(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnAbort(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_abort = try cb.withThis(self); + self._on_abort = try cb.persistWithThis(self); } else { self._on_abort = null; } @@ -89,7 +89,7 @@ pub fn getOnError(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnError(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_error = try cb.withThis(self); + self._on_error = try cb.persistWithThis(self); } else { self._on_error = null; } @@ -101,7 +101,7 @@ pub fn getOnLoad(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnLoad(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_load = try cb.withThis(self); + self._on_load = try cb.persistWithThis(self); } else { self._on_load = null; } @@ -113,7 +113,7 @@ pub fn getOnLoadEnd(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnLoadEnd(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_load_end = try cb.withThis(self); + self._on_load_end = try cb.persistWithThis(self); } else { self._on_load_end = null; } @@ -125,7 +125,7 @@ pub fn getOnLoadStart(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnLoadStart(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_load_start = try cb.withThis(self); + self._on_load_start = try cb.persistWithThis(self); } else { self._on_load_start = null; } @@ -137,7 +137,7 @@ pub fn getOnProgress(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnProgress(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_progress = try cb.withThis(self); + self._on_progress = try cb.persistWithThis(self); } else { self._on_progress = null; } @@ -149,7 +149,7 @@ pub fn getOnTimeout(self: *const XMLHttpRequestEventTarget) ?js.Function { pub fn setOnTimeout(self: *XMLHttpRequestEventTarget, cb_: ?js.Function) !void { if (cb_) |cb| { - self._on_timeout = try cb.withThis(self); + self._on_timeout = try cb.persistWithThis(self); } else { self._on_timeout = null; } diff --git a/src/browser/webapi/streams/ReadableStream.zig b/src/browser/webapi/streams/ReadableStream.zig index 70c59ba0..ed1d0149 100644 --- a/src/browser/webapi/streams/ReadableStream.zig +++ b/src/browser/webapi/streams/ReadableStream.zig @@ -85,7 +85,7 @@ pub fn init(src_: ?UnderlyingSource, strategy_: ?QueueingStrategy, page: *Page) } if (src.pull) |pull| { - self._pull_fn = pull; + self._pull_fn = try pull.persist(); try self.callPullIfNeeded(); } } From 6ecf52cc03a34c6b07dba965103f373bda0b933f Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 31 Dec 2025 12:39:25 +0800 Subject: [PATCH 03/26] port Platform and Inspector to use v8's C handles/functions directly --- src/browser/js/Env.zig | 10 +- src/browser/js/Function.zig | 13 +- src/browser/js/Inspector.zig | 408 ++++++++++++++++++++++++++++++++--- src/browser/js/Platform.zig | 20 +- src/browser/js/js.zig | 11 +- src/cdp/cdp.zig | 2 +- src/main_wpt.zig | 2 +- 7 files changed, 404 insertions(+), 62 deletions(-) diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index fd09044d..c025234d 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -119,8 +119,10 @@ pub fn deinit(self: *Env) void { self.allocator.free(self.templates); } -pub fn newInspector(self: *Env, arena: Allocator, ctx: anytype) !Inspector { - return Inspector.init(arena, self.isolate, ctx); +pub fn newInspector(self: *Env, arena: Allocator, ctx: anytype) !*Inspector { + const inspector = try arena.create(Inspector); + try Inspector.init(inspector, self.isolate.handle, ctx); + return inspector; } pub fn runMicrotasks(self: *const Env) void { @@ -128,11 +130,11 @@ pub fn runMicrotasks(self: *const Env) void { } pub fn pumpMessageLoop(self: *const Env) bool { - return self.platform.inner.pumpMessageLoop(self.isolate, false); + return v8.c.v8__Platform__PumpMessageLoop(self.platform.handle, self.isolate.handle, false); } pub fn runIdleTasks(self: *const Env) void { - return self.platform.inner.runIdleTasks(self.isolate, 1); + v8.c.v8__Platform__RunIdleTasks(self.platform.handle, self.isolate.handle, 1); } pub fn newExecutionWorld(self: *Env) !ExecutionWorld { return .{ diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index 21d45014..bafb1e40 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -37,16 +37,6 @@ pub fn id(self: *const Function) u32 { return @as(u32, @bitCast(v8.c.v8__Object__GetIdentityHash(@ptrCast(self.handle)))); } -pub fn getName(self: *const Function, allocator: Allocator) ![]const u8 { - const name = v8.c.v8__Function__GetName(self.handle).?; - return self.context.valueToString(name, .{ .allocator = allocator }); -} - -pub fn setName(self: *const Function, name: []const u8) void { - const v8_name = v8.String.initUtf8(self.context.isolate, name); - self.func.castToFunction().setName(v8_name); -} - pub fn withThis(self: *const Function, value: anytype) !Function { const this_obj = if (@TypeOf(value) == js.Object) value.js_obj @@ -181,8 +171,7 @@ fn getThis(self: *const Function) v8.Object { } pub fn src(self: *const Function) ![]const u8 { - const value = self.func.castToFunction().toValue(); - return self.context.valueToString(value, .{}); + return self.context.valueToString(.{ .handle = @ptrCast(self.handle) }, .{}); } pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value { diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index cb8eb2bd..edf62a58 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -23,43 +23,76 @@ const v8 = js.v8; const Context = @import("Context.zig"); const Allocator = std.mem.Allocator; +const RndGen = std.Random.DefaultPrng; + +const CONTEST_GROUP_ID = 1; +const CLIENT_TRUST_LEVEL = 1; const Inspector = @This(); -pub const RemoteObject = v8.RemoteObject; - -isolate: v8.Isolate, -inner: *v8.Inspector, -session: v8.InspectorSession, +handle: *v8.c.Inspector, +isolate: *v8.c.Isolate, +client: Client, +channel: Channel, +session: Session, +rnd: RndGen = RndGen.init(0), +ctx_handle: ?*const v8.c.Context = null, // We expect allocator to be an arena -pub fn init(allocator: Allocator, isolate: v8.Isolate, ctx: anytype) !Inspector { +// Note: This initializes the pre-allocated inspector in-place +pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { const ContextT = @TypeOf(ctx); - const InspectorContainer = switch (@typeInfo(ContextT)) { + const Container = switch (@typeInfo(ContextT)) { .@"struct" => ContextT, .pointer => |ptr| ptr.child, .void => NoopInspector, else => @compileError("invalid context type"), }; - // If necessary, turn a void context into something we can safely ptrCast const safe_context: *anyopaque = if (ContextT == void) @ptrCast(@constCast(&{})) else ctx; - const channel = v8.InspectorChannel.init( + // Initialize the fields that callbacks need first + self.* = .{ + .handle = undefined, + .isolate = isolate, + .client = undefined, + .channel = undefined, + .rnd = RndGen.init(0), + .ctx_handle = null, + .session = undefined, + }; + + // Create client and set inspector data BEFORE creating the inspector + // because V8 will call generateUniqueId during inspector creation + const client = Client.init(); + self.client = client; + client.setInspector(self); + + // Now create the inspector - generateUniqueId will work because data is set + const handle = v8.c.v8_inspector__Inspector__Create(isolate, client.handle).?; + self.handle = handle; + + // Create the channel + const channel = Channel.init( safe_context, - InspectorContainer.onInspectorResponse, - InspectorContainer.onInspectorEvent, - InspectorContainer.onRunMessageLoopOnPause, - InspectorContainer.onQuitMessageLoopOnPause, + Container.onInspectorResponse, + Container.onInspectorEvent, + Container.onRunMessageLoopOnPause, + Container.onQuitMessageLoopOnPause, isolate, ); + self.channel = channel; + channel.setInspector(self); - const client = v8.InspectorClient.init(); - - const inner = try allocator.create(v8.Inspector); - v8.Inspector.init(inner, client, channel, isolate); - return .{ .inner = inner, .isolate = isolate, .session = inner.connect() }; + // Create the session + const session_handle = v8.c.v8_inspector__Inspector__Connect( + handle, + CONTEST_GROUP_ID, + channel.handle, + CLIENT_TRUST_LEVEL, + ).?; + self.session = .{ .handle = session_handle }; } pub fn deinit(self: *const Inspector) void { @@ -68,7 +101,9 @@ pub fn deinit(self: *const Inspector) void { defer temp_scope.deinit(); self.session.deinit(); - self.inner.deinit(); + self.client.deinit(); + self.channel.deinit(); + v8.c.v8_inspector__Inspector__DELETE(self.handle); } pub fn send(self: *const Inspector, msg: []const u8) void { @@ -76,9 +111,9 @@ pub fn send(self: *const Inspector, msg: []const u8) void { // available when doing this. Pages (and thus the HandleScope) // comes and goes, but CDP can keep sending messages. const isolate = self.isolate; - var temp_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&temp_scope, isolate); - defer temp_scope.deinit(); + var temp_scope: v8.c.HandleScope = undefined; + v8.c.v8__HandleScope__CONSTRUCT(&temp_scope, isolate); + defer v8.c.v8__HandleScope__DESTRUCT(&temp_scope); self.session.dispatchProtocolMessage(isolate, msg); } @@ -99,7 +134,27 @@ pub fn contextCreated( aux_data: ?[]const u8, is_default_context: bool, ) void { - self.inner.contextCreated(context.v8_context, name, origin, aux_data, is_default_context); + _ = is_default_context; // TODO: Should this be passed to the C API? + var auxData_ptr: [*c]const u8 = undefined; + var auxData_len: usize = undefined; + if (aux_data) |data| { + auxData_ptr = data.ptr; + auxData_len = data.len; + } else { + auxData_ptr = null; + auxData_len = 0; + } + v8.c.v8_inspector__Inspector__ContextCreated( + self.handle, + name.ptr, + name.len, + origin.ptr, + origin.len, + auxData_ptr, + auxData_len, + CONTEST_GROUP_ID, + context.v8_context.handle, + ); } // Retrieves the RemoteObject for a given value. @@ -118,9 +173,9 @@ pub fn getRemoteObject( // We do not want to expose this as a parameter for now const generate_preview = false; return self.session.wrapObject( - context.isolate, - context.v8_context, - js_value, + context.isolate.handle, + context.v8_context.handle, + js_value.handle, group, generate_preview, ); @@ -136,15 +191,210 @@ pub fn getNodePtr(self: *const Inspector, allocator: Allocator, object_id: []con const unwrapped = try self.session.unwrapObject(allocator, object_id); // The values context and groupId are not used here const js_val = unwrapped.value; - if (js_val.isObject() == false) { + if (!v8.c.v8__Value__IsObject(js_val)) { return error.ObjectIdIsNotANode; } const Node = @import("../webapi/Node.zig"); - return Context.typeTaggedAnyOpaque(*Node, js_val.castTo(v8.Object)) catch { + // Wrap the C handle in a v8.Object for typeTaggedAnyOpaque + const js_obj = v8.Object{ .handle = js_val }; + return Context.typeTaggedAnyOpaque(*Node, js_obj) catch { return error.ObjectIdIsNotANode; }; } +pub const RemoteObject = struct { + handle: *v8.c.RemoteObject, + + pub fn deinit(self: RemoteObject) void { + v8.c.v8_inspector__RemoteObject__DELETE(self.handle); + } + + pub fn getType(self: RemoteObject, allocator: Allocator) ![]const u8 { + var ctype_: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.c.v8_inspector__RemoteObject__getType(self.handle, &allocator, &ctype_)) return error.V8AllocFailed; + return cZigStringToString(ctype_) orelse return error.InvalidType; + } + + pub fn getSubtype(self: RemoteObject, allocator: Allocator) !?[]const u8 { + if (!v8.c.v8_inspector__RemoteObject__hasSubtype(self.handle)) return null; + + var csubtype: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.c.v8_inspector__RemoteObject__getSubtype(self.handle, &allocator, &csubtype)) return error.V8AllocFailed; + return cZigStringToString(csubtype); + } + + pub fn getClassName(self: RemoteObject, allocator: Allocator) !?[]const u8 { + if (!v8.c.v8_inspector__RemoteObject__hasClassName(self.handle)) return null; + + var cclass_name: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.c.v8_inspector__RemoteObject__getClassName(self.handle, &allocator, &cclass_name)) return error.V8AllocFailed; + return cZigStringToString(cclass_name); + } + + pub fn getDescription(self: RemoteObject, allocator: Allocator) !?[]const u8 { + if (!v8.c.v8_inspector__RemoteObject__hasDescription(self.handle)) return null; + + var description: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.c.v8_inspector__RemoteObject__getDescription(self.handle, &allocator, &description)) return error.V8AllocFailed; + return cZigStringToString(description); + } + + pub fn getObjectId(self: RemoteObject, allocator: Allocator) !?[]const u8 { + if (!v8.c.v8_inspector__RemoteObject__hasObjectId(self.handle)) return null; + + var cobject_id: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.c.v8_inspector__RemoteObject__getObjectId(self.handle, &allocator, &cobject_id)) return error.V8AllocFailed; + return cZigStringToString(cobject_id); + } +}; + +const Session = struct { + handle: *v8.c.InspectorSession, + + fn deinit(self: Session) void { + v8.c.v8_inspector__Session__DELETE(self.handle); + } + + fn dispatchProtocolMessage(self: Session, isolate: *v8.c.Isolate, msg: []const u8) void { + v8.c.v8_inspector__Session__dispatchProtocolMessage( + self.handle, + isolate, + msg.ptr, + msg.len, + ); + } + + fn wrapObject( + self: Session, + isolate: *v8.c.Isolate, + ctx: *const v8.c.Context, + val: *const v8.c.Value, + grpname: []const u8, + generatepreview: bool, + ) !RemoteObject { + const remote_object = v8.c.v8_inspector__Session__wrapObject( + self.handle, + isolate, + ctx, + val, + grpname.ptr, + grpname.len, + generatepreview, + ).?; + return .{ .handle = remote_object }; + } + + fn unwrapObject( + self: Session, + allocator: Allocator, + object_id: []const u8, + ) !UnwrappedObject { + const in_object_id = v8.c.CZigString{ + .ptr = object_id.ptr, + .len = object_id.len, + }; + var out_error: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + var out_value_handle: ?*v8.c.Value = null; + var out_context_handle: ?*v8.c.Context = null; + var out_object_group: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + + const result = v8.c.v8_inspector__Session__unwrapObject( + self.handle, + &allocator, + &out_error, + in_object_id, + &out_value_handle, + &out_context_handle, + &out_object_group, + ); + + if (!result) { + const error_str = cZigStringToString(out_error) orelse return error.UnwrapFailed; + std.log.err("unwrapObject failed: {s}", .{error_str}); + return error.UnwrapFailed; + } + + return .{ + .value = out_value_handle.?, + .context = out_context_handle.?, + .object_group = cZigStringToString(out_object_group), + }; + } +}; + +const UnwrappedObject = struct { + value: *const v8.c.Value, + context: *const v8.c.Context, + object_group: ?[]const u8, +}; + +const Channel = struct { + handle: *v8.c.InspectorChannelImpl, + + // callbacks + ctx: *anyopaque, + onNotif: onNotifFn = undefined, + onResp: onRespFn = undefined, + onRunMessageLoopOnPause: onRunMessageLoopOnPauseFn = undefined, + onQuitMessageLoopOnPause: onQuitMessageLoopOnPauseFn = undefined, + + pub const onNotifFn = *const fn (ctx: *anyopaque, msg: []const u8) void; + pub const onRespFn = *const fn (ctx: *anyopaque, call_id: u32, msg: []const u8) void; + pub const onRunMessageLoopOnPauseFn = *const fn (ctx: *anyopaque, context_group_id: u32) void; + pub const onQuitMessageLoopOnPauseFn = *const fn (ctx: *anyopaque) void; + + fn init( + ctx: *anyopaque, + onResp: onRespFn, + onNotif: onNotifFn, + onRunMessageLoopOnPause: onRunMessageLoopOnPauseFn, + onQuitMessageLoopOnPause: onQuitMessageLoopOnPauseFn, + isolate: *v8.c.Isolate, + ) Channel { + const handle = v8.c.v8_inspector__Channel__IMPL__CREATE(isolate); + return .{ + .handle = handle, + .ctx = ctx, + .onResp = onResp, + .onNotif = onNotif, + .onRunMessageLoopOnPause = onRunMessageLoopOnPause, + .onQuitMessageLoopOnPause = onQuitMessageLoopOnPause, + }; + } + + fn deinit(self: Channel) void { + v8.c.v8_inspector__Channel__IMPL__DELETE(self.handle); + } + + fn setInspector(self: Channel, inspector: *anyopaque) void { + v8.c.v8_inspector__Channel__IMPL__SET_DATA(self.handle, inspector); + } + + fn resp(self: Channel, call_id: u32, msg: []const u8) void { + self.onResp(self.ctx, call_id, msg); + } + + fn notif(self: Channel, msg: []const u8) void { + self.onNotif(self.ctx, msg); + } +}; + +const Client = struct { + handle: *v8.c.InspectorClientImpl, + + fn init() Client { + return .{ .handle = v8.c.v8_inspector__Client__IMPL__CREATE() }; + } + + fn deinit(self: Client) void { + v8.c.v8_inspector__Client__IMPL__DELETE(self.handle); + } + + fn setInspector(self: Client, inspector: *anyopaque) void { + v8.c.v8_inspector__Client__IMPL__SET_DATA(self.handle, inspector); + } +}; + const NoopInspector = struct { pub fn onInspectorResponse(_: *anyopaque, _: u32, _: []const u8) void {} pub fn onInspectorEvent(_: *anyopaque, _: []const u8) void {} @@ -152,15 +402,107 @@ const NoopInspector = struct { pub fn onQuitMessageLoopOnPause(_: *anyopaque) void {} }; -pub fn getTaggedAnyOpaque(value: v8.Value) ?*js.TaggedAnyOpaque { - if (value.isObject() == false) { +fn fromData(data: *anyopaque) *Inspector { + return @ptrCast(@alignCast(data)); +} + +pub fn getTaggedAnyOpaque(value: *const v8.c.Value) ?*js.TaggedAnyOpaque { + if (!v8.c.v8__Value__IsObject(value)) { return null; } - const obj = value.castTo(v8.Object); - if (obj.internalFieldCount() == 0) { + const internal_field_count = v8.c.v8__Object__InternalFieldCount(value); + if (internal_field_count == 0) { return null; } - const external_data = obj.getInternalField(0).castTo(v8.External).get().?; + const external_value = v8.c.v8__Object__GetInternalField(value, 0).?; + const external_data = v8.c.v8__External__Value(external_value).?; return @ptrCast(@alignCast(external_data)); } + +fn cZigStringToString(s: v8.c.CZigString) ?[]const u8 { + if (s.ptr == null) return null; + return s.ptr[0..s.len]; +} + +// C export functions for Inspector callbacks +pub export fn v8_inspector__Client__IMPL__generateUniqueId( + _: *v8.c.InspectorClientImpl, + data: *anyopaque, +) callconv(.c) i64 { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + return inspector.rnd.random().int(i64); +} + +pub export fn v8_inspector__Client__IMPL__runMessageLoopOnPause( + _: *v8.c.InspectorClientImpl, + data: *anyopaque, + ctx_group_id: c_int, +) callconv(.c) void { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + inspector.channel.onRunMessageLoopOnPause(inspector.channel.ctx, @intCast(ctx_group_id)); +} + +pub export fn v8_inspector__Client__IMPL__quitMessageLoopOnPause( + _: *v8.c.InspectorClientImpl, + data: *anyopaque, +) callconv(.c) void { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + inspector.channel.onQuitMessageLoopOnPause(inspector.channel.ctx); +} + +pub export fn v8_inspector__Client__IMPL__runIfWaitingForDebugger( + _: *v8.c.InspectorClientImpl, + _: *anyopaque, + _: c_int, +) callconv(.c) void { + // TODO +} + +pub export fn v8_inspector__Client__IMPL__consoleAPIMessage( + _: *v8.c.InspectorClientImpl, + _: *anyopaque, + _: c_int, + _: v8.c.MessageErrorLevel, + _: *v8.c.StringView, + _: *v8.c.StringView, + _: c_uint, + _: c_uint, + _: *v8.c.StackTrace, +) callconv(.c) void {} + +pub export fn v8_inspector__Client__IMPL__ensureDefaultContextInGroup( + _: *v8.c.InspectorClientImpl, + data: *anyopaque, +) callconv(.c) ?*const v8.c.Context { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + return inspector.ctx_handle; +} + +pub export fn v8_inspector__Channel__IMPL__sendResponse( + _: *v8.c.InspectorChannelImpl, + data: *anyopaque, + call_id: c_int, + msg: [*c]u8, + length: usize, +) callconv(.c) void { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + inspector.channel.resp(@as(u32, @intCast(call_id)), msg[0..length]); +} + +pub export fn v8_inspector__Channel__IMPL__sendNotification( + _: *v8.c.InspectorChannelImpl, + data: *anyopaque, + msg: [*c]u8, + length: usize, +) callconv(.c) void { + const inspector: *Inspector = @ptrCast(@alignCast(data)); + inspector.channel.notif(msg[0..length]); +} + +pub export fn v8_inspector__Channel__IMPL__flushProtocolNotifications( + _: *v8.c.InspectorChannelImpl, + _: *anyopaque, +) callconv(.c) void { + // TODO +} diff --git a/src/browser/js/Platform.zig b/src/browser/js/Platform.zig index dcec9fe6..6390abad 100644 --- a/src/browser/js/Platform.zig +++ b/src/browser/js/Platform.zig @@ -20,20 +20,22 @@ const js = @import("js.zig"); const v8 = js.v8; const Platform = @This(); -inner: v8.Platform, +handle: *v8.c.Platform, pub fn init() !Platform { - if (v8.initV8ICU() == false) { + if (v8.c.v8__V8__InitializeICU() == false) { return error.FailedToInitializeICU; } - const platform = v8.Platform.initDefault(0, true); - v8.initV8Platform(platform); - v8.initV8(); - return .{ .inner = platform }; + // 0 - threadpool size, 0 == let v8 decide + // 1 - idle_task_support, 1 == enabled + const handle = v8.c.v8__Platform__NewDefaultPlatform(0, 1).?; + v8.c.v8__V8__InitializePlatform(handle); + v8.c.v8__V8__Initialize(); + return .{ .handle = handle }; } pub fn deinit(self: Platform) void { - _ = v8.deinitV8(); - v8.deinitV8Platform(); - self.inner.deinit(); + _ = v8.c.v8__V8__Dispose(); + v8.c.v8__V8__DisposePlatform(); + v8.c.v8__Platform__DELETE(self.handle); } diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 55b9b724..45c1c327 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -481,7 +481,7 @@ pub export fn v8_inspector__Client__IMPL__valueSubtype( _: *v8.c.InspectorClientImpl, c_value: *const v8.C_Value, ) callconv(.c) [*c]const u8 { - const external_entry = Inspector.getTaggedAnyOpaque(.{ .handle = c_value }) orelse return null; + const external_entry = Inspector.getTaggedAnyOpaque(c_value) orelse return null; return if (external_entry.subtype) |st| @tagName(st) else null; } @@ -498,10 +498,17 @@ pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype( // We _must_ include a non-null description in order for the subtype value // to be included. Besides that, I don't know if the value has any meaning - const external_entry = Inspector.getTaggedAnyOpaque(.{ .handle = c_value }) orelse return null; + const external_entry = Inspector.getTaggedAnyOpaque(c_value) orelse return null; return if (external_entry.subtype == null) null else ""; } +/// Enables C to allocate using the given Zig allocator +pub export fn zigAlloc(self: *anyopaque, bytes: usize) callconv(.c) ?[*]u8 { + const allocator: *Allocator = @ptrCast(@alignCast(self)); + const allocated_bytes = allocator.alloc(u8, bytes) catch return null; + return allocated_bytes.ptr; +} + test "TaggedAnyOpaque" { // If we grow this, fine, but it should be a conscious decision try std.testing.expectEqual(24, @sizeOf(TaggedAnyOpaque)); diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 9a62c267..3aef758a 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -331,7 +331,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { node_registry: Node.Registry, node_search_list: Node.Search.List, - inspector: js.Inspector, + inspector: *js.Inspector, isolated_worlds: std.ArrayListUnmanaged(IsolatedWorld), http_proxy_changed: bool = false, diff --git a/src/main_wpt.zig b/src/main_wpt.zig index e140b37e..27df82c1 100644 --- a/src/main_wpt.zig +++ b/src/main_wpt.zig @@ -135,7 +135,7 @@ fn run( return err; }; - return value.toString(arena); + return value.toString(.{ .allocator = arena }); } const Writer = struct { From 3442f99a492542212c545c328fca8fb723ab5bb9 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 31 Dec 2025 12:41:27 +0800 Subject: [PATCH 04/26] remove unused js.This --- src/browser/js/Caller.zig | 20 -------------------- src/browser/js/This.zig | 40 --------------------------------------- src/browser/js/js.zig | 2 -- 3 files changed, 62 deletions(-) delete mode 100644 src/browser/js/This.zig diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index 51fa23ea..8e60dab7 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -377,24 +377,6 @@ fn getArgs(self: *const Caller, comptime F: type, comptime offset: usize, info: break :blk params[0 .. params.len - 1]; } - // If the last parameter is a special JsThis, set it, and exclude it - // from our params slice, because we don't want to bind it to - // a JS argument - if (comptime params[params.len - 1].type.? == js.This) { - @field(args, tupleFieldName(params.len - 1 + offset)) = .{ .obj = .{ - .context = context, - .js_obj = info.getThis(), - } }; - - // AND the 2nd last parameter is state - if (params.len > 1 and comptime isPage(params[params.len - 2].type.?)) { - @field(args, tupleFieldName(params.len - 2 + offset)) = self.context.page; - break :blk params[0 .. params.len - 2]; - } - - break :blk params[0 .. params.len - 1]; - } - // we have neither a Page nor a JsObject. All params must be // bound to a JavaScript value. break :blk params; @@ -446,8 +428,6 @@ fn getArgs(self: *const Caller, comptime F: type, comptime offset: usize, info: if (comptime isPage(param.type.?)) { @compileError("Page must be the last parameter (or 2nd last if there's a JsThis): " ++ @typeName(F)); - } else if (comptime param.type.? == js.This) { - @compileError("JsThis must be the last parameter: " ++ @typeName(F)); } else if (i >= js_parameter_count) { if (@typeInfo(param.type.?) != .optional) { return error.InvalidArgument; diff --git a/src/browser/js/This.zig b/src/browser/js/This.zig deleted file mode 100644 index e86e97e3..00000000 --- a/src/browser/js/This.zig +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) -// -// Francis Bouvier -// Pierre Tachoire -// -// 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 . - -const std = @import("std"); -const js = @import("js.zig"); - -// This only exists so that we know whether a function wants the opaque -// JS argument (js.Object), or if it wants the receiver as an opaque -// value. -// js.Object is normally used when a method wants an opaque JS object -// that it'll pass into a callback. -// This is used when the function wants to do advanced manipulation -// of the v8.Object bound to the instance. For example, postAttach is an -// example of using This. - -const This = @This(); -obj: js.Object, - -pub fn setIndex(self: This, index: u32, value: anytype, opts: js.Object.SetOpts) !void { - return self.obj.setIndex(index, value, opts); -} - -pub fn set(self: This, key: []const u8, value: anytype, opts: js.Object.SetOpts) !void { - return self.obj.set(key, value, opts); -} diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 45c1c327..733768c9 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -29,8 +29,6 @@ pub const Inspector = @import("Inspector.zig"); pub const Snapshot = @import("Snapshot.zig"); pub const Platform = @import("Platform.zig"); -// TODO: Is "This" really necessary? -pub const This = @import("This.zig"); pub const Value = @import("Value.zig"); pub const Array = @import("Array.zig"); pub const String = @import("String.zig"); From a5038893fe520df09f3502b913b06d69400f3717 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 31 Dec 2025 13:47:56 +0800 Subject: [PATCH 05/26] port Snapshot --- src/browser/js/Snapshot.zig | 212 ++++++++++++++++++++++-------------- 1 file changed, 128 insertions(+), 84 deletions(-) diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index d227bee3..4c49079e 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -41,8 +41,8 @@ const embedded_snapshot_blob = if (@import("build_config").snapshot_path) |path| // sequence data_start: usize, -// The snapshot data (v8.StartupData is a ptr to the data and len). -startup_data: v8.StartupData, +// The snapshot data (v8.c.StartupData is a ptr to the data and len). +startup_data: v8.c.StartupData, // V8 doesn't know how to serialize external references, and pretty much any hook // into Zig is an external reference (e.g. every accessor and function callback). @@ -74,8 +74,8 @@ fn loadEmbedded() ?Snapshot { const data_start = std.mem.readInt(usize, embedded_snapshot_blob[0..@sizeOf(usize)], .little); const blob = embedded_snapshot_blob[@sizeOf(usize)..]; - const startup_data = v8.StartupData{ .data = blob.ptr, .raw_size = @intCast(blob.len) }; - if (!v8.SnapshotCreator.startupDataIsValid(startup_data)) { + const startup_data = v8.c.StartupData{ .data = blob.ptr, .raw_size = @intCast(blob.len) }; + if (!v8.c.v8__StartupData__IsValid(startup_data)) { return null; } @@ -110,45 +110,48 @@ pub fn fromEmbedded(self: Snapshot) bool { } fn isValid(self: Snapshot) bool { - return v8.SnapshotCreator.startupDataIsValid(self.startup_data); + return v8.c.v8__StartupData__IsValid(self.startup_data); } pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionTemplate) v8.ObjectTemplate { // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance - const js_global = v8.FunctionTemplate.initDefault(isolate); - js_global.setClassName(v8.String.initUtf8(isolate, "Window")); + const js_global = v8.c.v8__FunctionTemplate__New__DEFAULT(isolate); + const window_name = v8.c.v8__String__NewFromUtf8(isolate, "Window", v8.c.kNormal, 6); + v8.c.v8__FunctionTemplate__SetClassName(js_global, window_name); + // Find Window in JsApis by name (avoids circular import) const window_index = comptime bridge.JsApiLookup.getId(Window.JsApi); - js_global.inherit(templates[window_index]); - return js_global.getInstanceTemplate(); + v8.c.v8__FunctionTemplate__Inherit(js_global, templates[window_index]); + + return v8.c.v8__FunctionTemplate__InstanceTemplate(js_global); } pub fn create(allocator: Allocator) !Snapshot { var external_references = collectExternalReferences(); - var params = v8.initCreateParams(); - params.array_buffer_allocator = v8.createDefaultArrayBufferAllocator(); - defer v8.destroyArrayBufferAllocator(params.array_buffer_allocator.?); + var params: v8.c.CreateParams = undefined; + v8.c.v8__Isolate__CreateParams__CONSTRUCT(¶ms); + params.array_buffer_allocator = v8.c.v8__ArrayBuffer__Allocator__NewDefaultAllocator(); + defer v8.c.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); params.external_references = @ptrCast(&external_references); - var snapshot_creator: v8.SnapshotCreator = undefined; - v8.SnapshotCreator.init(&snapshot_creator, ¶ms); - defer snapshot_creator.deinit(); + const snapshot_creator = v8.c.v8__SnapshotCreator__CREATE(¶ms); + defer v8.c.v8__SnapshotCreator__DESTRUCT(snapshot_creator); var data_start: usize = 0; - const isolate = snapshot_creator.getIsolate(); + const isolate = v8.c.v8__SnapshotCreator__getIsolate(snapshot_creator).?; { // CreateBlob, which we'll call once everything is setup, MUST NOT // be called from an active HandleScope. Hence we have this scope to // clean it up before we call CreateBlob - var handle_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&handle_scope, isolate); - defer handle_scope.deinit(); + var handle_scope: v8.c.HandleScope = undefined; + v8.c.v8__HandleScope__CONSTRUCT(&handle_scope, isolate); + defer v8.c.v8__HandleScope__DESTRUCT(&handle_scope); // Create templates (constructors only) FIRST - var templates: [JsApis.len]v8.FunctionTemplate = undefined; + var templates: [JsApis.len]*v8.c.FunctionTemplate = undefined; inline for (JsApis, 0..) |JsApi, i| { @setEvalBranchQuota(10_000); templates[i] = generateConstructor(JsApi, isolate); @@ -159,23 +162,23 @@ pub fn create(allocator: Allocator) !Snapshot { // This must come before attachClass so inheritance is set up first inline for (JsApis, 0..) |JsApi, i| { if (comptime protoIndexLookup(JsApi)) |proto_index| { - templates[i].inherit(templates[proto_index]); + v8.c.v8__FunctionTemplate__Inherit(templates[i], templates[proto_index]); } } // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance - const global_template = createGlobalTemplate(isolate, templates[0..]); - const context = v8.Context.init(isolate, global_template, null); - context.enter(); - defer context.exit(); + const global_template = createGlobalTemplate(isolate, templates[0..]); + const context = v8.c.v8__Context__New(isolate, global_template, null); + v8.c.v8__Context__Enter(context); + defer v8.c.v8__Context__Exit(context); // Add templates to context snapshot var last_data_index: usize = 0; inline for (JsApis, 0..) |_, i| { @setEvalBranchQuota(10_000); - const data_index = snapshot_creator.addDataWithContext(context, @ptrCast(templates[i].handle)); + const data_index = v8.c.v8__SnapshotCreator__AddData2(snapshot_creator, context, @ptrCast(templates[i])); if (i == 0) { data_start = data_index; last_data_index = data_index; @@ -193,16 +196,18 @@ pub fn create(allocator: Allocator) !Snapshot { } // Realize all templates by getting their functions and attaching to global - const global_obj = context.getGlobal(); + const global_obj = v8.c.v8__Context__Global(context); inline for (JsApis, 0..) |JsApi, i| { - const func = templates[i].getFunction(context); + const func = v8.c.v8__FunctionTemplate__GetFunction(templates[i], context); // Attach to global if it has a name if (@hasDecl(JsApi.Meta, "name")) { if (@hasDecl(JsApi.Meta, "constructor_alias")) { - const v8_class_name = v8.String.initUtf8(isolate, JsApi.Meta.constructor_alias); - _ = global_obj.setValue(context, v8_class_name, func); + const alias = JsApi.Meta.constructor_alias; + const v8_class_name = v8.c.v8__String__NewFromUtf8(isolate, alias.ptr, v8.c.kNormal, @intCast(alias.len)); + var maybe_result: v8.c.MaybeBool = undefined; + v8.c.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); // @TODO: This is wrong. This name should be registered with the // illegalConstructorCallback. I.e. new Image() is OK, but @@ -210,11 +215,15 @@ pub fn create(allocator: Allocator) !Snapshot { // But we _have_ to register the name, i.e. HTMLImageElement // has to be registered so, for now, instead of creating another // template, we just hook it into the constructor. - const illegal_class_name = v8.String.initUtf8(isolate, JsApi.Meta.name); - _ = global_obj.setValue(context, illegal_class_name, func); + const name = JsApi.Meta.name; + const illegal_class_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); + var maybe_result2: v8.c.MaybeBool = undefined; + v8.c.v8__Object__Set(global_obj, context, illegal_class_name, func, &maybe_result2); } else { - const v8_class_name = v8.String.initUtf8(isolate, JsApi.Meta.name); - _ = global_obj.setValue(context, v8_class_name, func); + const name = JsApi.Meta.name; + const v8_class_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); + var maybe_result: v8.c.MaybeBool = undefined; + v8.c.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); } } } @@ -222,8 +231,10 @@ pub fn create(allocator: Allocator) !Snapshot { { // If we want to overwrite the built-in console, we have to // delete the built-in one. - const console_key = v8.String.initUtf8(isolate, "console"); - if (global_obj.deleteValue(context, console_key) == false) { + const console_key = v8.c.v8__String__NewFromUtf8(isolate, "console", v8.c.kNormal, 7); + var maybe_deleted: v8.c.MaybeBool = undefined; + v8.c.v8__Object__Delete(global_obj, context, console_key, &maybe_deleted); + if (maybe_deleted.value == false) { return error.ConsoleDeleteError; } } @@ -233,23 +244,30 @@ pub fn create(allocator: Allocator) !Snapshot { // TODO: see if newer V8 engines have a way around this. inline for (JsApis, 0..) |JsApi, i| { if (comptime protoIndexLookup(JsApi)) |proto_index| { - const proto_obj = templates[proto_index].getFunction(context).toObject(); - const self_obj = templates[i].getFunction(context).toObject(); - _ = self_obj.setPrototype(context, proto_obj); + const proto_func = v8.c.v8__FunctionTemplate__GetFunction(templates[proto_index], context); + const proto_obj: *const v8.c.Object = @ptrCast(proto_func); + + const self_func = v8.c.v8__FunctionTemplate__GetFunction(templates[i], context); + const self_obj: *const v8.c.Object = @ptrCast(self_func); + + var maybe_result: v8.c.MaybeBool = undefined; + v8.c.v8__Object__SetPrototype(self_obj, context, proto_obj, &maybe_result); } } { // Custom exception // TODO: this is an horrible hack, I can't figure out how to do this cleanly. - const code = v8.String.initUtf8(isolate, "DOMException.prototype.__proto__ = Error.prototype"); - _ = try (try v8.Script.compile(context, code, null)).run(context); + const code_str = "DOMException.prototype.__proto__ = Error.prototype"; + const code = v8.c.v8__String__NewFromUtf8(isolate, code_str.ptr, v8.c.kNormal, @intCast(code_str.len)); + const script = v8.c.v8__Script__Compile(context, code, null) orelse return error.ScriptCompileFailed; + _ = v8.c.v8__Script__Run(script, context) orelse return error.ScriptRunFailed; } - snapshot_creator.setDefaultContext(context); + v8.c.v8__SnapshotCreator__setDefaultContext(snapshot_creator, context); } - const blob = snapshot_creator.createBlob(v8.FunctionCodeHandling.kKeep); + const blob = v8.c.v8__SnapshotCreator__createBlob(snapshot_creator, v8.c.kKeep); const owned = try allocator.dupe(u8, blob.data[0..@intCast(blob.raw_size)]); return .{ @@ -365,7 +383,7 @@ fn collectExternalReferences() [countExternalReferences()]isize { // via `new ClassName()` - but they could, for example, be created in // Zig and returned from a function call, which is why we need the // FunctionTemplate. -fn generateConstructor(comptime JsApi: type, isolate: v8.Isolate) v8.FunctionTemplate { +fn generateConstructor(comptime JsApi: type, isolate: *v8.c.Isolate) *v8.c.FunctionTemplate { const callback = blk: { if (@hasDecl(JsApi, "constructor")) { break :blk JsApi.constructor.func; @@ -375,19 +393,22 @@ fn generateConstructor(comptime JsApi: type, isolate: v8.Isolate) v8.FunctionTem break :blk illegalConstructorCallback; }; - const template = v8.FunctionTemplate.initCallback(isolate, callback); + const template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, callback).?); if (!@hasDecl(JsApi.Meta, "empty_with_no_proto")) { - template.getInstanceTemplate().setInternalFieldCount(1); + const instance_template = v8.c.v8__FunctionTemplate__InstanceTemplate(template); + v8.c.v8__ObjectTemplate__SetInternalFieldCount(instance_template, 1); } - const class_name = v8.String.initUtf8(isolate, if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi)); - template.setClassName(class_name); + const name_str = if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi); + const class_name = v8.c.v8__String__NewFromUtf8(isolate, name_str.ptr, v8.c.kNormal, @intCast(name_str.len)); + v8.c.v8__FunctionTemplate__SetClassName(template, class_name); return template; } // Attaches JsApi members to the prototype template (normal case) -fn attachClass(comptime JsApi: type, isolate: v8.Isolate, template: v8.FunctionTemplate) void { - const target = template.getPrototypeTemplate(); - const instance = template.getInstanceTemplate(); + +fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.FunctionTemplate) void { + const target = v8.c.v8__FunctionTemplate__PrototypeTemplate(template); + const instance = v8.c.v8__FunctionTemplate__InstanceTemplate(template); const declarations = @typeInfo(JsApi).@"struct".decls; inline for (declarations) |d| { @@ -397,60 +418,80 @@ fn attachClass(comptime JsApi: type, isolate: v8.Isolate, template: v8.FunctionT switch (definition) { bridge.Accessor => { - const js_name = v8.String.initUtf8(isolate, name).toName(); - const getter_callback = v8.FunctionTemplate.initCallback(isolate, value.getter); + const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); + const getter_callback = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.getter).?); if (value.setter == null) { if (value.static) { - template.setAccessorGetter(js_name, getter_callback); + v8.c.v8__Template__SetAccessorProperty__DEFAULT(@ptrCast(template), js_name, getter_callback); } else { - target.setAccessorGetter(js_name, getter_callback); + v8.c.v8__ObjectTemplate__SetAccessorProperty__DEFAULT(target, js_name, getter_callback); } } else { std.debug.assert(value.static == false); - const setter_callback = v8.FunctionTemplate.initCallback(isolate, value.setter); - target.setAccessorGetterAndSetter(js_name, getter_callback, setter_callback); + const setter_callback = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.setter.?).?); + v8.c.v8__ObjectTemplate__SetAccessorProperty__DEFAULT2(target, js_name, getter_callback, setter_callback); } }, bridge.Function => { - const function_template = v8.FunctionTemplate.initCallback(isolate, value.func); - const js_name = v8.String.initUtf8(isolate, name).toName(); + const function_template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); + const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); if (value.static) { - template.set(js_name, function_template, v8.PropertyAttribute.None); + v8.c.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.c.None); } else { - target.set(js_name, function_template, v8.PropertyAttribute.None); + v8.c.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.c.None); } }, bridge.Indexed => { - const configuration = v8.IndexedPropertyHandlerConfiguration{ + var configuration: v8.c.IndexedPropertyHandlerConfiguration = .{ .getter = value.getter, + .setter = null, + .query = null, + .deleter = null, + .enumerator = null, + .definer = null, + .descriptor = null, + .data = null, + .flags = 0, }; instance.setIndexedProperty(configuration, null); }, - bridge.NamedIndexed => instance.setNamedProperty(.{ - .getter = value.getter, - .setter = value.setter, - .deleter = value.deleter, - .flags = v8.PropertyHandlerFlags.OnlyInterceptStrings | v8.PropertyHandlerFlags.NonMasking, - }, null), + bridge.NamedIndexed => { + const instance_template = v8.c.v8__FunctionTemplate__InstanceTemplate(template); + var configuration: v8.c.NamedPropertyHandlerConfiguration = .{ + .getter = value.getter, + .setter = value.setter, + .query = null, + .deleter = value.deleter, + .enumerator = null, + .definer = null, + .descriptor = null, + .data = null, + .flags = v8.c.kOnlyInterceptStrings | v8.c.kNonMasking, + }; + v8.c.v8__ObjectTemplate__SetNamedHandler(instance, &configuration); + }, bridge.Iterator => { - const function_template = v8.FunctionTemplate.initCallback(isolate, value.func); + const function_template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); const js_name = if (value.async) - v8.Symbol.getAsyncIterator(isolate).toName() + v8.c.v8__Symbol__GetAsyncIterator(isolate) else - v8.Symbol.getIterator(isolate).toName(); - target.set(js_name, function_template, v8.PropertyAttribute.None); + v8.c.v8__Symbol__GetIterator(isolate); + v8.c.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.c.None); }, bridge.Property => { - const js_value = switch (value) { - .int => |v| js.simpleZigValueToJs(isolate, v, true, false), + // simpleZigValueToJs still uses old v8.Isolate wrapper, so create a temp wrapper + const iso_wrapper = v8.Isolate{ .handle = isolate }; + const js_value_wrapper = switch (value) { + .int => |v| js.simpleZigValueToJs(iso_wrapper, v, true, false), }; + const js_value = js_value_wrapper.handle; - const js_name = v8.String.initUtf8(isolate, name).toName(); + const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); // apply it both to the type itself - template.set(js_name, js_value, v8.PropertyAttribute.ReadOnly + v8.PropertyAttribute.DontDelete); + v8.c.v8__Template__Set(@ptrCast(template), js_name, js_value, v8.c.ReadOnly + v8.c.DontDelete); // and to instances of the type - target.set(js_name, js_value, v8.PropertyAttribute.ReadOnly + v8.PropertyAttribute.DontDelete); + v8.c.v8__Template__Set(@ptrCast(target), js_name, js_value, v8.c.ReadOnly + v8.c.DontDelete); }, bridge.Constructor => {}, // already handled in generateConstructor else => {}, @@ -458,8 +499,8 @@ fn attachClass(comptime JsApi: type, isolate: v8.Isolate, template: v8.FunctionT } if (@hasDecl(JsApi.Meta, "htmldda")) { - instance.markAsUndetectable(); - instance.setCallAsFunctionHandler(JsApi.Meta.callable.func); + v8.c.v8__ObjectTemplate__MarkAsUndetectable(instance); + v8.c.v8__ObjectTemplate__SetCallAsFunctionHandler(instance, JsApi.Meta.callable.func); } if (@hasDecl(JsApi.Meta, "name")) { @@ -482,10 +523,13 @@ fn protoIndexLookup(comptime JsApi: type) ?bridge.JsApiLookup.BackingInt { } // Shared illegal constructor callback for types without explicit constructors -fn illegalConstructorCallback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const info = v8.FunctionCallbackInfo.initFromV8(raw_info); - const iso = info.getIsolate(); +fn illegalConstructorCallback(raw_info: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info); log.warn(.js, "Illegal constructor call", .{}); - const js_exception = iso.throwException(js._createException(iso, "Illegal Constructor")); - info.getReturnValue().set(js_exception); + const message = v8.c.v8__String__NewFromUtf8(isolate, "Illegal Constructor", v8.c.kNormal, 19); + const js_exception = v8.c.v8__Exception__TypeError(message); + _ = v8.c.v8__Isolate__ThrowException(isolate, js_exception); + var return_value: v8.c.ReturnValue = undefined; + v8.c.v8__FunctionCallbackInfo__GetReturnValue(raw_info, &return_value); + v8.c.v8__ReturnValue__Set(return_value, js_exception); } From 93f0d24673722529ee22f681345690077f8ddcce Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 31 Dec 2025 14:44:59 +0800 Subject: [PATCH 06/26] port TryCatch --- src/browser/js/TryCatch.zig | 43 +++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/browser/js/TryCatch.zig b/src/browser/js/TryCatch.zig index baf30710..db48dace 100644 --- a/src/browser/js/TryCatch.zig +++ b/src/browser/js/TryCatch.zig @@ -24,43 +24,50 @@ const Allocator = std.mem.Allocator; const TryCatch = @This(); -inner: v8.TryCatch, -context: *const js.Context, +handle: v8.c.TryCatch, +ctx: *const js.Context, pub fn init(self: *TryCatch, context: *const js.Context) void { - self.context = context; - self.inner.init(context.isolate); + self.ctx = context; + v8.c.v8__TryCatch__CONSTRUCT(&self.handle, context.isolate.handle); } pub fn hasCaught(self: TryCatch) bool { - return self.inner.hasCaught(); + return v8.c.v8__TryCatch__HasCaught(&self.handle); } // the caller needs to deinit the string returned pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 { - const msg = self.inner.getException() orelse return null; - return try self.context.valueToString(msg, .{ .allocator = allocator }); + const msg_value = v8.c.v8__TryCatch__Exception(&self.handle) orelse return null; + const msg = v8.Value{ .handle = msg_value }; + return try self.ctx.valueToString(msg, .{ .allocator = allocator }); } // the caller needs to deinit the string returned pub fn stack(self: TryCatch, allocator: Allocator) !?[]const u8 { - const context = self.context; - const s = self.inner.getStackTrace(context.v8_context) orelse return null; - return try context.valueToString(s, .{ .allocator = allocator }); + const ctx = self.ctx; + const s_value = v8.c.v8__TryCatch__StackTrace(&self.handle, ctx.v8_context.handle) orelse return null; + const s = v8.Value{ .handle = s_value }; + return try ctx.valueToString(s, .{ .allocator = allocator }); } // the caller needs to deinit the string returned pub fn sourceLine(self: TryCatch, allocator: Allocator) !?[]const u8 { - const context = self.context; - const msg = self.inner.getMessage() orelse return null; - const sl = msg.getSourceLine(context.v8_context) orelse return null; - return try context.jsStringToZig(sl, .{ .allocator = allocator }); + const ctx = self.ctx; + const msg = v8.c.v8__TryCatch__Message(&self.handle) orelse return null; + const sl = v8.c.v8__Message__GetSourceLine(msg, ctx.v8_context.handle) orelse return null; + const sl_string = v8.String{ .handle = sl }; + return try ctx.jsStringToZig(sl_string, .{ .allocator = allocator }); } pub fn sourceLineNumber(self: TryCatch) ?u32 { - const context = self.context; - const msg = self.inner.getMessage() orelse return null; - return msg.getLineNumber(context.v8_context); + const ctx = self.ctx; + const msg = v8.c.v8__TryCatch__Message(&self.handle) orelse return null; + const line = v8.c.v8__Message__GetLineNumber(msg, ctx.v8_context.handle); + if (line < 0) { + return null; + } + return @intCast(line); } // a shorthand method to return either the entire stack message @@ -78,5 +85,5 @@ pub fn err(self: TryCatch, allocator: Allocator) !?[]const u8 { } pub fn deinit(self: *TryCatch) void { - self.inner.deinit(); + v8.c.v8__TryCatch__DESTRUCT(&self.handle); } From ca5a385b51d3278ae5ec90da261851b5ed909133 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 31 Dec 2025 17:44:42 +0800 Subject: [PATCH 07/26] Port js.Object Use js.Value in apis that should take values (not objects), like console.log and setTimeout and reportError. --- src/browser/js/Array.zig | 7 ++ src/browser/js/Context.zig | 42 ++++----- src/browser/js/Function.zig | 18 ++-- src/browser/js/Object.zig | 99 ++++++++++------------ src/browser/js/Value.zig | 12 ++- src/browser/tests/net/response.html | 46 +++++----- src/browser/webapi/Console.zig | 16 ++-- src/browser/webapi/Document.zig | 7 +- src/browser/webapi/Element.zig | 2 +- src/browser/webapi/KeyValueList.zig | 2 +- src/browser/webapi/MessagePort.zig | 4 +- src/browser/webapi/Window.zig | 22 ++--- src/browser/webapi/animation/Animation.zig | 18 ++-- src/browser/webapi/event/ErrorEvent.zig | 6 +- src/browser/webapi/event/MessageEvent.zig | 6 +- 15 files changed, 167 insertions(+), 140 deletions(-) diff --git a/src/browser/js/Array.zig b/src/browser/js/Array.zig index 95ccf4e3..f2ae8348 100644 --- a/src/browser/js/Array.zig +++ b/src/browser/js/Array.zig @@ -42,3 +42,10 @@ pub fn get(self: Array, index: u32) !js.Value { .handle = handle, }; } + +pub fn asObject(self: Array) js.Object { + return .{ + .ctx = self.ctx, + .handle = @ptrCast(self.handle), + }; +} diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index c8fc312c..e9ad00be 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -80,9 +80,8 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty, // the @intFromPtr(js_obj.handle). But v8 can re-use address. Without // a reliable way to know if an object has already been persisted, // we now simply persist every time persist() is called. -js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty, - global_values: std.ArrayList(js.Global(js.Value)) = .empty, +global_objects: std.ArrayList(js.Global(js.Object)) = .empty, global_functions: std.ArrayList(js.Global(js.Function)) = .empty, // Various web APIs depend on having a persistent promise resolver. They @@ -157,11 +156,11 @@ pub fn deinit(self: *Context) void { } } - for (self.js_object_list.items) |*p| { - p.deinit(); + for (self.global_values.items) |*global| { + global.deinit(); } - for (self.global_values.items) |*global| { + for (self.global_objects.items) |*global| { global.deinit(); } @@ -352,11 +351,11 @@ fn postCompileModule(self: *Context, mod: v8.Module, url: [:0]const u8) !void { } // == Creators == -pub fn createArray(self: *Context, len: u32) js.Object { - const arr = v8.Array.init(self.isolate, len); +pub fn createArray(self: *Context, len: u32) js.Array { + const handle = v8.c.v8__Array__New(self.isolate.handle, @intCast(len)).?; return .{ - .context = self, - .js_obj = arr.castTo(v8.Object), + .ctx = self, + .handle = handle, }; } @@ -376,8 +375,8 @@ pub fn createValue(self: *Context, value: v8.Value) js.Value { pub fn createObject(self: *Context, js_value: v8.Value) js.Object { return .{ - .js_obj = js_value.castTo(v8.Object), - .context = self, + .ctx = self, + .handle = @ptrCast(js_value.handle), }; } @@ -482,7 +481,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp if (T == js.Object) { // we're returning a v8.Object - return value.js_obj.toValue(); + return .{ .handle = @ptrCast(value.handle) }; } if (T == js.Value) { @@ -658,15 +657,15 @@ pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T { // or whether no parameter was passed. if (comptime o.child == js.Value) { return js.Value{ - .context = self, - .js_val = js_value, + .ctx = self, + .handle = js_value.handle, }; } if (comptime o.child == js.Object) { return js.Object{ - .context = self, - .js_obj = js_value.castTo(v8.Object), + .ctx = self, + .handle = @ptrCast(js_value.handle), }; } @@ -829,9 +828,14 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: v8.Value) !?T { }, // Caller wants an opaque js.Object. Probably a parameter // that it needs to pass back into a callback. - js.Object => js.Object{ - .js_obj = js_value.castTo(v8.Object), - .context = self, + js.Object => { + if (!js_value.isObject()) { + return null; + } + return js.Object{ + .ctx = self, + .handle = @ptrCast(js_value.handle), + }; }, else => { if (!js_value.isObject()) { diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index bafb1e40..06aa68ba 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -25,7 +25,7 @@ const Allocator = std.mem.Allocator; const Function = @This(); ctx: *js.Context, -this: ?v8.Object = null, +this: ?*const v8.c.Object = null, handle: *const v8.c.Function, pub const Result = struct { @@ -39,9 +39,9 @@ pub fn id(self: *const Function) u32 { pub fn withThis(self: *const Function, value: anytype) !Function { const this_obj = if (@TypeOf(value) == js.Object) - value.js_obj + value.handle else - (try self.ctx.zigValueToJs(value, .{})).castTo(v8.Object); + (try self.ctx.zigValueToJs(value, .{})).handle; return .{ .ctx = self.ctx, @@ -72,8 +72,8 @@ pub fn newInstance(self: *const Function, result: *Result) !js.Object { }; return .{ - .context = ctx, - .js_obj = .{ .handle = handle }, + .ctx = ctx, + .handle = handle, }; } @@ -166,8 +166,12 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args return ctx.jsValueToZig(T, .{ .handle = handle }); } -fn getThis(self: *const Function) v8.Object { - return self.this orelse self.ctx.v8_context.getGlobal(); +fn getThis(self: *const Function) js.Object { + const handle = self.this orelse self.ctx.v8_context.getGlobal().handle; + return .{ + .ctx = self.ctx, + .handle = handle, + }; } pub fn src(self: *const Function) ![]const u8 { diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index 4520e396..589d01c8 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -28,11 +28,12 @@ const PersistentObject = v8.Persistent(v8.Object); const Allocator = std.mem.Allocator; const Object = @This(); -js_obj: v8.Object, -context: *js.Context, + +ctx: *js.Context, +handle: *const v8.c.Object, pub fn getId(self: Object) u32 { - return self.js_obj.getIdentityHash(); + return @bitCast(v8.c.v8__Object__GetIdentityHash(self.handle)); } pub const SetOpts = packed struct(u32) { @@ -51,58 +52,57 @@ pub fn setIndex(self: Object, index: u32, value: anytype, opts: SetOpts) !void { } pub fn set(self: Object, key: []const u8, value: anytype, opts: SetOpts) error{ FailedToSet, OutOfMemory }!void { - const context = self.context; + const ctx = self.ctx; - const js_key = v8.String.initUtf8(context.isolate, key); - const js_value = try context.zigValueToJs(value, .{}); + const js_key = v8.c.v8__String__NewFromUtf8(ctx.isolate.handle, key.ptr, v8.c.kNormal, @intCast(key.len)).?; + const js_value = try ctx.zigValueToJs(value, .{}); - const res = self.js_obj.defineOwnProperty(context.v8_context, js_key.toName(), js_value, @bitCast(opts)) orelse false; + var out: v8.c.MaybeBool = undefined; + v8.c.v8__Object__DefineOwnProperty(self.handle, ctx.v8_context.handle, @ptrCast(js_key), js_value.handle, @bitCast(opts), &out); + + const res = if (out.has_value) out.value else false; if (!res) { return error.FailedToSet; } } pub fn get(self: Object, key: []const u8) !js.Value { - const context = self.context; - const js_key = v8.String.initUtf8(context.isolate, key); - const js_val = try self.js_obj.getValue(context.v8_context, js_key); - return context.createValue(js_val); -} - -pub fn isTruthy(self: Object) bool { - const js_value = self.js_obj.toValue(); - return js_value.toBool(self.context.isolate); + const ctx = self.ctx; + const js_key = v8.c.v8__String__NewFromUtf8(ctx.isolate.handle, key.ptr, v8.c.kNormal, @intCast(key.len)).?; + const js_val_handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, js_key) orelse return error.JsException; + const js_val = v8.Value{ .handle = js_val_handle }; + return ctx.createValue(js_val); } pub fn toString(self: Object) ![]const u8 { - const js_value = self.js_obj.toValue(); - return self.context.valueToString(js_value, .{}); + const js_value = v8.Value{ .handle = @ptrCast(self.handle) }; + return self.ctx.valueToString(js_value, .{}); } pub fn format(self: Object, writer: *std.Io.Writer) !void { if (comptime IS_DEBUG) { - return self.context.debugValue(self.js_obj.toValue(), writer); + const js_value = v8.Value{ .handle = @ptrCast(self.handle) }; + return self.ctx.debugValue(js_value, writer); } const str = self.toString() catch return error.WriteFailed; return writer.writeAll(str); } pub fn toJson(self: Object, allocator: Allocator) ![]u8 { - const json_string = try v8.Json.stringify(self.context.v8_context, self.js_obj.toValue(), null); - const str = try self.context.jsStringToZig(json_string, .{ .allocator = allocator }); - return str; + const json_str_handle = v8.c.v8__JSON__Stringify(self.ctx.v8_context.handle, @ptrCast(self.handle), null) orelse return error.JsException; + const json_string = v8.String{ .handle = json_str_handle }; + return self.ctx.jsStringToZig(json_string, .{ .allocator = allocator }); } pub fn persist(self: Object) !Object { - var context = self.context; - const js_obj = self.js_obj; + var ctx = self.ctx; - const persisted = PersistentObject.init(context.isolate, js_obj); - try context.js_object_list.append(context.arena, persisted); + const global = js.Global(Object).init(ctx.isolate.handle, self.handle); + try ctx.global_objects.append(ctx.arena, global); return .{ - .context = context, - .js_obj = persisted.castToObject(), + .ctx = ctx, + .handle = global.local(), }; } @@ -110,15 +110,16 @@ pub fn getFunction(self: Object, name: []const u8) !?js.Function { if (self.isNullOrUndefined()) { return null; } - const context = self.context; + const ctx = self.ctx; - const js_name = v8.String.initUtf8(context.isolate, name); + const js_name = v8.c.v8__String__NewFromUtf8(ctx.isolate.handle, name.ptr, v8.c.kNormal, @intCast(name.len)).?; + const js_val_handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, js_name) orelse return error.JsException; + const js_value = v8.Value{ .handle = js_val_handle }; - const js_value = try self.js_obj.getValue(context.v8_context, js_name.toName()); if (!js_value.isFunction()) { return null; } - return try context.createFunction(js_value); + return try ctx.createFunction(js_value); } pub fn callMethod(self: Object, comptime T: type, method_name: []const u8, args: anytype) !T { @@ -126,41 +127,33 @@ pub fn callMethod(self: Object, comptime T: type, method_name: []const u8, args: return func.callWithThis(T, self, args); } -pub fn isNull(self: Object) bool { - return self.js_obj.toValue().isNull(); -} - -pub fn isUndefined(self: Object) bool { - return self.js_obj.toValue().isUndefined(); -} - pub fn isNullOrUndefined(self: Object) bool { - return self.js_obj.toValue().isNullOrUndefined(); + return v8.c.v8__Value__IsNullOrUndefined(@ptrCast(self.handle)); } pub fn nameIterator(self: Object) NameIterator { - const context = self.context; - const js_obj = self.js_obj; + const ctx = self.ctx; - const array = js_obj.getPropertyNames(context.v8_context); - const count = array.length(); + const handle = v8.c.v8__Object__GetPropertyNames(self.handle, ctx.v8_context.handle).?; + const count = v8.c.v8__Array__Length(handle); return .{ + .ctx = ctx, + .handle = handle, .count = count, - .context = context, - .js_obj = array.castTo(v8.Object), }; } pub fn toZig(self: Object, comptime T: type) !T { - return self.context.jsValueToZig(T, self.js_obj.toValue()); + const js_value = v8.Value{ .handle = @ptrCast(self.handle) }; + return self.ctx.jsValueToZig(T, js_value); } pub const NameIterator = struct { count: u32, idx: u32 = 0, - js_obj: v8.Object, - context: *const Context, + ctx: *const Context, + handle: *const v8.c.Array, pub fn next(self: *NameIterator) !?[]const u8 { const idx = self.idx; @@ -169,8 +162,8 @@ pub const NameIterator = struct { } self.idx += 1; - const context = self.context; - const js_val = try self.js_obj.getAtIndex(context.v8_context, idx); - return try context.valueToString(js_val, .{}); + const js_val_handle = v8.c.v8__Object__GetIndex(@ptrCast(self.handle), self.ctx.v8_context.handle, idx) orelse return error.JsException; + const js_val = v8.Value{ .handle = js_val_handle }; + return try self.ctx.valueToString(js_val, .{}); } }; diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index b419688d..39ca9bde 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -112,8 +112,8 @@ pub fn toObject(self: Value) js.Object { } return .{ - .context = self.ctx, - .js_obj = .{ .handle = self.handle }, + .ctx = self.ctx, + .handle = self.handle, }; } @@ -127,3 +127,11 @@ pub fn toArray(self: Value) js.Array { .handle = self.handle, }; } + +pub fn format(self: Value, writer: *std.Io.Writer) !void { + if (comptime IS_DEBUG) { + return self.ctx.debugValue(.{ .handle = self.handle }, writer); + } + const str = self.toString(.{}) catch return error.WriteFailed; + return writer.writeAll(str); +} diff --git a/src/browser/tests/net/response.html b/src/browser/tests/net/response.html index c632c3ff..b7c149ba 100644 --- a/src/browser/tests/net/response.html +++ b/src/browser/tests/net/response.html @@ -2,12 +2,12 @@ - diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index aa976ac1..6e1ba9da 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -770,8 +770,8 @@ pub fn getAdoptedStyleSheets(self: *Document, page: *Page) !js.Object { if (self._adopted_style_sheets) |ass| { return ass; } - const js_arr = page.js.createArray(0); - const js_obj = js_arr.asObject(); + const js_arr = page.js.newArray(0); + const js_obj = js_arr.toObject(); self._adopted_style_sheets = try js_obj.persist(); return self._adopted_style_sheets.?; } diff --git a/src/browser/webapi/History.zig b/src/browser/webapi/History.zig index cef7b05b..7106be56 100644 --- a/src/browser/webapi/History.zig +++ b/src/browser/webapi/History.zig @@ -34,7 +34,7 @@ pub fn getLength(_: *const History, page: *Page) u32 { pub fn getState(_: *const History, page: *Page) !?js.Value { if (page._session.navigation.getCurrentEntry()._state.value) |state| { - const value = try js.Value.fromJson(page.js, state); + const value = try page.js.parseJSON(state); return value; } else return null; } diff --git a/src/browser/webapi/element/html/Custom.zig b/src/browser/webapi/element/html/Custom.zig index 3fc9071f..58f72984 100644 --- a/src/browser/webapi/element/html/Custom.zig +++ b/src/browser/webapi/element/html/Custom.zig @@ -44,7 +44,9 @@ pub fn asNode(self: *Custom) *Node { pub fn invokeConnectedCallback(self: *Custom, page: *Page) void { // Only invoke if we haven't already called it while connected - if (self._connected_callback_invoked) return; + if (self._connected_callback_invoked) { + return; + } self._connected_callback_invoked = true; self._disconnected_callback_invoked = false; @@ -158,11 +160,11 @@ pub fn invokeAttributeChangedCallbackOnElement(element: *Element, name: []const fn invokeCallbackOnElement(element: *Element, definition: *CustomElementDefinition, comptime callback_name: [:0]const u8, args: anytype, page: *Page) void { _ = definition; - const context = page.js; + const ctx = page.js; // Get the JS element object - const js_val = context.zigValueToJs(element, .{}) catch return; - const js_element = context.createObject(js_val); + const js_val = ctx.zigValueToJs(element, .{}) catch return; + const js_element = js_val.toObject(); // Call the callback method if it exists js_element.callMethod(void, callback_name, args) catch return; @@ -205,10 +207,10 @@ fn invokeCallback(self: *Custom, comptime callback_name: [:0]const u8, args: any return; } - const context = page.js; + const ctx = page.js; - const js_val = context.zigValueToJs(self, .{}) catch return; - const js_element = context.createObject(js_val); + const js_val = ctx.zigValueToJs(self, .{}) catch return; + const js_element = js_val.toObject(); js_element.callMethod(void, callback_name, args) catch return; } diff --git a/src/browser/webapi/event/PopStateEvent.zig b/src/browser/webapi/event/PopStateEvent.zig index f6a00615..4d4c710f 100644 --- a/src/browser/webapi/event/PopStateEvent.zig +++ b/src/browser/webapi/event/PopStateEvent.zig @@ -60,10 +60,8 @@ pub fn asEvent(self: *PopStateEvent) *Event { } pub fn getState(self: *PopStateEvent, page: *Page) !?js.Value { - if (self._state == null) return null; - - const value = try js.Value.fromJson(page.js, self._state.?); - return value; + const s = self._state orelse return null; + return try page.js.parseJSON(s); } pub fn hasUAVisualTransition(_: *PopStateEvent) bool { diff --git a/src/browser/webapi/navigation/NavigationHistoryEntry.zig b/src/browser/webapi/navigation/NavigationHistoryEntry.zig index 5e1a98a8..6d29df3a 100644 --- a/src/browser/webapi/navigation/NavigationHistoryEntry.zig +++ b/src/browser/webapi/navigation/NavigationHistoryEntry.zig @@ -81,7 +81,7 @@ pub const StateReturn = union(enum) { value: ?js.Value, undefined: void }; pub fn getState(self: *const NavigationHistoryEntry, page: *Page) !StateReturn { if (self._state.source == .navigation) { if (self._state.value) |value| { - return .{ .value = try js.Value.fromJson(page.js, value) }; + return .{ .value = try page.js.parseJSON(value) }; } } diff --git a/src/browser/webapi/net/Response.zig b/src/browser/webapi/net/Response.zig index 4995e600..2fd38e38 100644 --- a/src/browser/webapi/net/Response.zig +++ b/src/browser/webapi/net/Response.zig @@ -120,11 +120,10 @@ pub fn getText(self: *const Response, page: *Page) !js.Promise { pub fn getJson(self: *Response, page: *Page) !js.Promise { const body = self._body orelse ""; - const value = js.Value.fromJson(page.js, body) catch |err| { + const value = page.js.parseJSON(body) catch |err| { return page.js.rejectPromise(.{@errorName(err)}); }; - const pvalue = try value.persist(); - return page.js.resolvePromise(pvalue); + return page.js.resolvePromise(try value.persist()); } pub const JsApi = struct { diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 93ee323f..954ad8fa 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -254,9 +254,8 @@ pub fn getResponse(self: *XMLHttpRequest, page: *Page) !?Response { const res: Response = switch (self._response_type) { .text => .{ .text = data }, .json => blk: { - const value = try js.Value.fromJson(page.js, data); - const pvalue = try value.persist(); - break :blk .{ .json = pvalue }; + const value = try page.js.parseJSON(data); + break :blk .{ .json = try value.persist() }; }, .document => blk: { const document = try page._factory.node(Node.Document{ ._proto = undefined, ._type = .generic }); From 8438b7d561bad96cf3fcce7886f527a7e6a4abe6 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 2 Jan 2026 16:07:23 +0800 Subject: [PATCH 14/26] remove remaining direct v8 references --- src/browser/js/Context.zig | 46 ++++----- src/browser/js/Env.zig | 16 +-- src/browser/js/ExecutionWorld.zig | 33 ++++--- src/browser/js/Inspector.zig | 34 +++---- src/browser/js/bridge.zig | 156 ++++++++++++++++-------------- src/browser/js/js.zig | 6 +- src/cdp/domains/dom.zig | 6 +- src/cdp/domains/page.zig | 3 +- 8 files changed, 152 insertions(+), 148 deletions(-) diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index b139871f..d67fb60d 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -42,9 +42,10 @@ page: *Page, isolate: js.Isolate, // This context is a persistent object. The persistent needs to be recovered and reset. handle: *const v8.c.Context, + handle_scope: ?js.HandleScope, -cpu_profiler: ?v8.CpuProfiler = null, +cpu_profiler: ?*v8.c.CpuProfiler = null, // references Env.templates templates: []*const v8.c.FunctionTemplate, @@ -114,7 +115,7 @@ const ModuleEntry = struct { resolver_promise: ?js.Promise = null, }; -pub fn fromC(c_context: *const v8.C_Context) *Context { +pub fn fromC(c_context: *const v8.c.Context) *Context { const data = v8.c.v8__Context__GetEmbedderData(c_context, 1).?; const big_int = js.BigInt{ .handle = @ptrCast(data) }; return @ptrFromInt(big_int.getUint64()); @@ -168,9 +169,6 @@ pub fn deinit(self: *Context) void { scope.deinit(); v8.c.v8__Context__Exit(self.handle); } - const v8_context = v8.Context{ .handle = self.handle }; - var presistent_context = v8.Persistent(v8.Context).recoverCast(v8_context); - presistent_context.deinit(); } // == Executors == @@ -372,6 +370,10 @@ pub fn throw(self: *Context, err: []const u8) js.Exception { }; } +pub fn debugContextId(self: *const Context) i32 { + return v8.c.v8__Context__DebugContextId(self.handle); +} + pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !js.Value { const isolate = self.isolate; @@ -611,7 +613,6 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_handle: ?*const v8.c.Object, va } pub fn jsValueToZig(self: *Context, comptime T: type, js_value: js.Value) !T { - const v8_context = v8.Context{ .handle = self.handle }; switch (@typeInfo(T)) { .optional => |o| { // If type type is a ?js.Value or a ?js.Object, then we want to pass @@ -757,7 +758,7 @@ pub fn jsValueToZig(self: *Context, comptime T: type, js_value: js.Value) !T { return std.meta.stringToEnum(T, try self.valueToString(js_value, .{})) orelse return error.InvalidArgument; } switch (@typeInfo(e.tag_type)) { - .int => return std.meta.intToEnum(T, try jsIntToZig(e.tag_type, js_value, v8_context)), + .int => return std.meta.intToEnum(T, try jsIntToZig(e.tag_type, js_value)), else => @compileError("unsupported enum parameter type: " ++ @typeName(T)), } }, @@ -1151,11 +1152,11 @@ pub fn createPromiseResolver(self: *Context) js.PromiseResolver { // Callback from V8, asking us to load a module. The "specifier" is // the src of the module to load. fn resolveModuleCallback( - c_context: ?*const v8.C_Context, - c_specifier: ?*const v8.C_String, - import_attributes: ?*const v8.C_FixedArray, - c_referrer: ?*const v8.C_Module, -) callconv(.c) ?*const v8.C_Module { + c_context: ?*const v8.c.Context, + c_specifier: ?*const v8.c.String, + import_attributes: ?*const v8.c.FixedArray, + c_referrer: ?*const v8.c.Module, +) callconv(.c) ?*const v8.c.Module { _ = import_attributes; const self = fromC(c_context.?); @@ -1215,7 +1216,7 @@ pub fn dynamicModuleCallback( return @constCast(promise.handle); } -pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.c) void { +pub fn metaObjectCallback(c_context: ?*v8.c.Context, c_module: ?*v8.c.Module, c_meta: ?*v8.c.Value) callconv(.c) void { const self = fromC(c_context.?); const m = js.Module{ .ctx = self, .handle = c_module.? }; const meta = js.Object{ .ctx = self, .handle = @ptrCast(c_meta.?) }; @@ -1236,7 +1237,7 @@ pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_ } } -fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]const u8) !?*const v8.C_Module { +fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]const u8) !?*const v8.c.Module { const referrer_path = self.module_identifier.get(referrer.getIdentityHash()) orelse { // Shouldn't be possible. return error.UnknownModuleReferrer; @@ -1997,16 +1998,17 @@ pub fn startCpuProfiler(self: *Context) void { } std.debug.assert(self.cpu_profiler == null); - v8.CpuProfiler.useDetailedSourcePositionsForProfiling(self.isolate); - const cpu_profiler = v8.CpuProfiler.init(self.isolate); - const title = self.isolate.initStringUtf8("v8_cpu_profile"); - cpu_profiler.startProfiling(title); + v8.c.v8__CpuProfiler__UseDetailedSourcePositionsForProfiling(self.isolate.handle); + + const cpu_profiler = v8.c.v8__CpuProfiler__Get(self.isolate.handle).?; + const title = self.isolate.initStringHandle("v8_cpu_profile"); + v8.c.v8__CpuProfiler__StartProfiling(cpu_profiler, title); self.cpu_profiler = cpu_profiler; } pub fn stopCpuProfiler(self: *Context) ![]const u8 { - const title = self.isolate.initStringUtf8("v8_cpu_profile"); - const profile = self.cpu_profiler.?.stopProfiling(title) orelse unreachable; - const serialized = profile.serialize(self.isolate).?; - return self.jsStringToZig(serialized, .{}); + const title = self.isolate.initStringHandle("v8_cpu_profile"); + const handle = v8.c.v8__CpuProfiler__StopProfiling(self.cpu_profiler.?, title) orelse return error.NoProfiles; + const string_handle = v8.c.v8__CpuProfile__Serialize(handle, self.isolate.handle) orelse return error.NoProfile; + return self.jsStringToZig(string_handle, .{}); } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index fcbff781..6a11834a 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -65,8 +65,8 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot v8.c.v8__Isolate__CreateParams__CONSTRUCT(params); params.snapshot_blob = @ptrCast(&snapshot.startup_data); - params.array_buffer_allocator = v8.createDefaultArrayBufferAllocator(); - errdefer v8.destroyArrayBufferAllocator(params.array_buffer_allocator.?); + params.array_buffer_allocator = v8.c.v8__ArrayBuffer__Allocator__NewDefaultAllocator().?; + errdefer v8.c.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); params.external_references = &snapshot.external_references; @@ -130,7 +130,7 @@ pub fn deinit(self: *Env) void { self.isolate.exit(); self.isolate.deinit(); - v8.destroyArrayBufferAllocator(self.isolate_params.array_buffer_allocator.?); + v8.c.v8__ArrayBuffer__Allocator__DELETE(self.isolate_params.array_buffer_allocator.?); self.allocator.destroy(self.isolate_params); } @@ -191,15 +191,15 @@ pub fn dumpMemoryStats(self: *Env) void { , .{ stats.total_heap_size, stats.total_heap_size_executable, stats.total_physical_size, stats.total_available_size, stats.used_heap_size, stats.heap_size_limit, stats.malloced_memory, stats.external_memory, stats.peak_malloced_memory, stats.number_of_native_contexts, stats.number_of_detached_contexts, stats.total_global_handles_size, stats.used_global_handles_size, stats.does_zap_garbage }); } -fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void { - const msg = v8.PromiseRejectMessage.initFromC(v8_msg); - const isolate_handle = v8.c.v8__Object__GetIsolate(@ptrCast(msg.getPromise().handle)).?; +fn promiseRejectCallback(message_handle: v8.c.PromiseRejectMessage) callconv(.c) void { + const promise_handle = v8.c.v8__PromiseRejectMessage__GetPromise(&message_handle).?; + const isolate_handle = v8.c.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; const js_isolate = js.Isolate{ .handle = isolate_handle }; const context = Context.fromIsolate(js_isolate); const value = - if (msg.getValue()) |v8_value| - context.valueToString(js.Value{ .ctx = context, .handle = v8_value.handle }, .{}) catch |err| @errorName(err) + if (v8.c.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value| + context.valueToString(.{ .ctx = context, .handle = v8_value }, .{}) catch |err| @errorName(err) else "no value"; diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index 2c534788..3ed14760 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -52,6 +52,7 @@ context_arena: ArenaAllocator, // does all the work, but having all page-specific data structures // grouped together helps keep things clean. context: ?Context = null, +persisted_context: ?js.Global(Context) = null, // no init, must be initialized via env.newExecutionWorld() @@ -59,7 +60,6 @@ pub fn deinit(self: *ExecutionWorld) void { if (self.context != null) { self.removeContext(); } - self.context_arena.deinit(); } @@ -76,7 +76,7 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context const isolate = env.isolate; const arena = self.context_arena.allocator(); - const context_handle: *const v8.c.Context = blk: { + const persisted_context: js.Global(Context) = blk: { var temp_scope: js.HandleScope = undefined; temp_scope.init(isolate); defer temp_scope.deinit(); @@ -98,24 +98,22 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context }; v8.c.v8__ObjectTemplate__SetNamedHandler(global_template, &configuration); - const context_local = isolate.createContextHandle(null, null); - // Make the context persistent so it survives beyond this handle scope - var persistent_handle: *v8.c.Data = undefined; - v8.c.v8__Persistent__New(isolate.handle, @ptrCast(context_local), @ptrCast(&persistent_handle)); - break :blk @ptrCast(persistent_handle); + const context_handle = isolate.createContextHandle(null, null); + break :blk js.Global(Context).init(isolate.handle, context_handle); }; // For a Page we only create one HandleScope, it is stored in the main World (enter==true). A page can have multple contexts, 1 for each World. // The main Context that enters and holds the HandleScope should therefore always be created first. Following other worlds for this page // like isolated Worlds, will thereby place their objects on the main page's HandleScope. Note: In the furure the number of context will multiply multiple frames support + const v8_context = persisted_context.local(); var handle_scope: ?js.HandleScope = null; if (enter) { handle_scope = @as(js.HandleScope, undefined); handle_scope.?.init(isolate); - v8.c.v8__Context__Enter(context_handle); + v8.c.v8__Context__Enter(v8_context); } errdefer if (enter) { - v8.c.v8__Context__Exit(context_handle); + v8.c.v8__Context__Exit(v8_context); handle_scope.?.deinit(); }; @@ -126,19 +124,20 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context .page = page, .id = context_id, .isolate = isolate, - .handle = context_handle, + .handle = v8_context, .templates = env.templates, .handle_scope = handle_scope, .script_manager = &page._script_manager, .call_arena = page.call_arena, .arena = arena, }; + self.persisted_context = persisted_context; var context = &self.context.?; // Store a pointer to our context inside the v8 context so that, given // a v8 context, we can get our context out const data = isolate.initBigInt(@intFromPtr(context)); - v8.c.v8__Context__SetEmbedderData(context_handle, 1, @ptrCast(data.handle)); + v8.c.v8__Context__SetEmbedderData(context.handle, 1, @ptrCast(data.handle)); try context.setupGlobal(); return context; @@ -151,8 +150,13 @@ pub fn removeContext(self: *ExecutionWorld) void { // the queue. Running them later could lead to invalid memory accesses. self.env.runMicrotasks(); - self.context.?.deinit(); + var context = &(self.context orelse return); + context.deinit(); self.context = null; + + self.persisted_context.?.deinit(); + self.persisted_context = null; + _ = self.context_arena.reset(.{ .retain_with_limit = CONTEXT_ARENA_RETAIN }); } @@ -164,7 +168,7 @@ pub fn resumeExecution(self: *const ExecutionWorld) void { self.env.isolate.cancelTerminateExecution(); } -pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { +pub fn unknownPropertyCallback(c_name: ?*const v8.c.Name, raw_info: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { const isolate_handle = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; const context = Context.fromIsolate(.{ .handle = isolate_handle }); @@ -216,5 +220,6 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C } } - return v8.Intercepted.No; + // not intercepted + return 0; } diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index 3126f62d..83bd7822 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -25,7 +25,7 @@ const Context = @import("Context.zig"); const Allocator = std.mem.Allocator; const RndGen = std.Random.DefaultPrng; -const CONTEST_GROUP_ID = 1; +const CONTEXT_GROUP_ID = 1; const CLIENT_TRUST_LEVEL = 1; const Inspector = @This(); @@ -36,7 +36,7 @@ client: Client, channel: Channel, session: Session, rnd: RndGen = RndGen.init(0), -ctx_handle: ?*const v8.c.Context = null, +default_context: ?*const v8.c.Context = null, // We expect allocator to be an arena // Note: This initializes the pre-allocated inspector in-place @@ -59,7 +59,7 @@ pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { .client = undefined, .channel = undefined, .rnd = RndGen.init(0), - .ctx_handle = null, + .default_context = null, .session = undefined, }; @@ -88,7 +88,7 @@ pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { // Create the session const session_handle = v8.c.v8_inspector__Inspector__Connect( handle, - CONTEST_GROUP_ID, + CONTEXT_GROUP_ID, channel.handle, CLIENT_TRUST_LEVEL, ).?; @@ -127,34 +127,28 @@ pub fn send(self: *const Inspector, msg: []const u8) void { // {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} // - is_default_context: Whether the execution context is default, should match the auxData pub fn contextCreated( - self: *const Inspector, + self: *Inspector, context: *const Context, name: []const u8, origin: []const u8, - aux_data: ?[]const u8, + aux_data: []const u8, is_default_context: bool, ) void { - _ = is_default_context; // TODO: Should this be passed to the C API? - var auxData_ptr: [*c]const u8 = undefined; - var auxData_len: usize = undefined; - if (aux_data) |data| { - auxData_ptr = data.ptr; - auxData_len = data.len; - } else { - auxData_ptr = null; - auxData_len = 0; - } v8.c.v8_inspector__Inspector__ContextCreated( self.handle, name.ptr, name.len, origin.ptr, origin.len, - auxData_ptr, - auxData_len, - CONTEST_GROUP_ID, + aux_data.ptr, + aux_data.len, + CONTEXT_GROUP_ID, context.handle, ); + + if (is_default_context) { + self.default_context = context.handle; + } } // Retrieves the RemoteObject for a given value. @@ -475,7 +469,7 @@ pub export fn v8_inspector__Client__IMPL__ensureDefaultContextInGroup( data: *anyopaque, ) callconv(.c) ?*const v8.c.Context { const inspector: *Inspector = @ptrCast(@alignCast(data)); - return inspector.ctx_handle; + return inspector.default_context; } pub export fn v8_inspector__Channel__IMPL__sendResponse( diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index fc69f37e..223e3fc2 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -57,44 +57,44 @@ const Name = struct { handle: *const v8.c.Name, }; -const CallbackInfo = struct { - raw: *const v8.c.FunctionCallbackInfo, +const FunctionCallbackInfo = struct { + handle: *const v8.c.FunctionCallbackInfo, - fn length(self: CallbackInfo) u32 { - return @intCast(v8.c.v8__FunctionCallbackInfo__Length(self.raw)); + fn length(self: FunctionCallbackInfo) u32 { + return @intCast(v8.c.v8__FunctionCallbackInfo__Length(self.handle)); } - fn getArg(self: CallbackInfo, index: u32) Value { - return .{ .handle = v8.c.v8__FunctionCallbackInfo__INDEX(self.raw, @intCast(index)).? }; + fn getArg(self: FunctionCallbackInfo, index: u32) Value { + return .{ .handle = v8.c.v8__FunctionCallbackInfo__INDEX(self.handle, @intCast(index)).? }; } - fn getThis(self: CallbackInfo) *const v8.c.Object { - return v8.c.v8__FunctionCallbackInfo__This(self.raw).?; + fn getThis(self: FunctionCallbackInfo) *const v8.c.Object { + return v8.c.v8__FunctionCallbackInfo__This(self.handle).?; } - fn getReturnValue(self: CallbackInfo) ReturnValue { + fn getReturnValue(self: FunctionCallbackInfo) ReturnValue { var rv: v8.c.ReturnValue = undefined; - v8.c.v8__FunctionCallbackInfo__GetReturnValue(self.raw, &rv); - return .{ .raw = rv }; + v8.c.v8__FunctionCallbackInfo__GetReturnValue(self.handle, &rv); + return .{ .handle = rv }; } }; const PropertyCallbackInfo = struct { - raw: *const v8.c.PropertyCallbackInfo, + handle: *const v8.c.PropertyCallbackInfo, fn getThis(self: PropertyCallbackInfo) *const v8.c.Object { - return v8.c.v8__PropertyCallbackInfo__This(self.raw).?; + return v8.c.v8__PropertyCallbackInfo__This(self.handle).?; } fn getReturnValue(self: PropertyCallbackInfo) ReturnValue { var rv: v8.c.ReturnValue = undefined; - v8.c.v8__PropertyCallbackInfo__GetReturnValue(self.raw, &rv); - return .{ .raw = rv }; + v8.c.v8__PropertyCallbackInfo__GetReturnValue(self.handle, &rv); + return .{ .handle = rv }; } }; const ReturnValue = struct { - raw: v8.c.ReturnValue, + handle: v8.c.ReturnValue, fn set(self: ReturnValue, value: anytype) void { const T = @TypeOf(value); @@ -112,7 +112,7 @@ const ReturnValue = struct { } fn setValueHandle(self: ReturnValue, handle: *const v8.c.Value) void { - v8.c.v8__ReturnValue__Set(self.raw, handle); + v8.c.v8__ReturnValue__Set(self.handle, handle); } }; @@ -170,13 +170,13 @@ pub const Caller = struct { as_typed_array: bool = false, }; - pub fn constructor(self: *Caller, comptime T: type, func: anytype, info: CallbackInfo, comptime opts: CallOpts) void { + pub fn constructor(self: *Caller, comptime T: type, func: anytype, info: FunctionCallbackInfo, comptime opts: CallOpts) void { self._constructor(func, info) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); }; } - fn _constructor(self: *Caller, func: anytype, info: CallbackInfo) !void { + fn _constructor(self: *Caller, func: anytype, info: FunctionCallbackInfo) !void { const F = @TypeOf(func); const args = try self.getArgs(F, 0, info); const res = @call(.auto, func, args); @@ -208,13 +208,13 @@ pub const Caller = struct { info.getReturnValue().set(this.handle); } - pub fn method(self: *Caller, comptime T: type, func: anytype, info: CallbackInfo, comptime opts: CallOpts) void { + pub fn method(self: *Caller, comptime T: type, func: anytype, info: FunctionCallbackInfo, comptime opts: CallOpts) void { self._method(T, func, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); }; } - fn _method(self: *Caller, comptime T: type, func: anytype, info: CallbackInfo, comptime opts: CallOpts) !void { + fn _method(self: *Caller, comptime T: type, func: anytype, info: FunctionCallbackInfo, comptime opts: CallOpts) !void { const F = @TypeOf(func); var handle_scope: js.HandleScope = undefined; handle_scope.init(self.isolate); @@ -226,13 +226,13 @@ pub const Caller = struct { info.getReturnValue().set(try self.context.zigValueToJs(res, opts)); } - pub fn function(self: *Caller, comptime T: type, func: anytype, info: CallbackInfo, comptime opts: CallOpts) void { + pub fn function(self: *Caller, comptime T: type, func: anytype, info: FunctionCallbackInfo, comptime opts: CallOpts) void { self._function(func, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); }; } - fn _function(self: *Caller, func: anytype, info: CallbackInfo, comptime opts: CallOpts) !void { + fn _function(self: *Caller, func: anytype, info: FunctionCallbackInfo, comptime opts: CallOpts) !void { const F = @TypeOf(func); const context = self.context; const args = try self.getArgs(F, 0, info); @@ -243,7 +243,8 @@ pub const Caller = struct { pub fn getIndex(self: *Caller, comptime T: type, func: anytype, idx: u32, info: PropertyCallbackInfo, comptime opts: CallOpts) u8 { return self._getIndex(T, func, idx, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; + // not intercepted + return 0; }; } @@ -259,7 +260,8 @@ pub const Caller = struct { pub fn getNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, info: PropertyCallbackInfo, comptime opts: CallOpts) u8 { return self._getNamedIndex(T, func, name, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; + // not intercepted + return 0; }; } @@ -275,7 +277,8 @@ pub const Caller = struct { pub fn setNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, js_value: Value, info: PropertyCallbackInfo, comptime opts: CallOpts) u8 { return self._setNamedIndex(T, func, name, js_value, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; + // not intercepted + return 0; }; } @@ -295,7 +298,7 @@ pub const Caller = struct { pub fn deleteNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, info: PropertyCallbackInfo, comptime opts: CallOpts) u8 { return self._deleteNamedIndex(T, func, name, info, opts) catch |err| { self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; + return 0; }; } @@ -322,11 +325,13 @@ pub const Caller = struct { // if error.NotHandled is part of the error set. if (isInErrorSet(error.NotHandled, eu.error_set)) { if (err == error.NotHandled) { - return v8.Intercepted.No; + // not intercepted + return 0; } } self.handleError(T, F, err, info, opts); - return v8.Intercepted.No; + // not intercepted + return 0; }; }, else => ret, @@ -335,7 +340,8 @@ pub const Caller = struct { if (comptime getter) { info.getReturnValue().set(try self.context.zigValueToJs(non_error_ret, opts)); } - return v8.Intercepted.Yes; + // intercepted + return 1; } fn isInErrorSet(err: anyerror, comptime T: type) bool { @@ -352,7 +358,7 @@ pub const Caller = struct { fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void { const isolate = self.isolate; - if (comptime @import("builtin").mode == .Debug and @TypeOf(info) == CallbackInfo) { + if (comptime @import("builtin").mode == .Debug and @TypeOf(info) == FunctionCallbackInfo) { if (log.enabled(.js, .warn)) { self.logFunctionCallError(@typeName(T), @typeName(F), err, info); } @@ -480,7 +486,7 @@ pub const Caller = struct { // This is extracted to speed up compilation. When left inlined in handleError, // this can add as much as 10 seconds of compilation time. - fn logFunctionCallError(self: *Caller, type_name: []const u8, func: []const u8, err: anyerror, info: CallbackInfo) void { + fn logFunctionCallError(self: *Caller, type_name: []const u8, func: []const u8, err: anyerror, info: FunctionCallbackInfo) void { const args_dump = self.serializeFunctionArgs(info) catch "failed to serialize args"; log.info(.js, "function call error", .{ .type = type_name, @@ -491,7 +497,7 @@ pub const Caller = struct { }); } - fn serializeFunctionArgs(self: *Caller, info: CallbackInfo) ![]const u8 { + fn serializeFunctionArgs(self: *Caller, info: FunctionCallbackInfo) ![]const u8 { const context = self.context; var buf = std.Io.Writer.Allocating.init(context.call_arena); @@ -618,7 +624,7 @@ pub fn Builder(comptime T: type) type { } pub const Constructor = struct { - func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { dom_exception: bool = false, @@ -626,12 +632,12 @@ pub const Constructor = struct { fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Constructor { return .{ .func = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; caller.constructor(T, func, info, .{ .dom_exception = opts.dom_exception, }); @@ -642,7 +648,7 @@ pub const Constructor = struct { pub const Function = struct { static: bool, - func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { static: bool = false, @@ -655,12 +661,12 @@ pub const Function = struct { return .{ .static = opts.static, .func = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; if (comptime opts.static) { caller.function(T, func, info, .{ .dom_exception = opts.dom_exception, @@ -682,8 +688,8 @@ pub const Function = struct { pub const Accessor = struct { static: bool = false, - getter: ?*const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void = null, - setter: ?*const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void = null, + getter: ?*const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void = null, + setter: ?*const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void = null, const Opts = struct { static: bool = false, @@ -699,12 +705,12 @@ pub const Accessor = struct { if (@typeInfo(@TypeOf(getter)) != .null) { accessor.getter = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; caller.method(T, getter, info, .{ .as_typed_array = opts.as_typed_array, .null_as_undefined = opts.null_as_undefined, @@ -715,12 +721,12 @@ pub const Accessor = struct { if (@typeInfo(@TypeOf(setter)) != .null) { accessor.setter = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; std.debug.assert(info.length() == 1); caller.method(T, setter, info, .{ @@ -736,7 +742,7 @@ pub const Accessor = struct { }; pub const Indexed = struct { - getter: *const fn (idx: u32, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8, + getter: *const fn (idx: u32, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8, const Opts = struct { as_typed_array: bool = false, @@ -745,12 +751,12 @@ pub const Indexed = struct { fn init(comptime T: type, comptime getter: anytype, comptime opts: Opts) Indexed { return .{ .getter = struct { - fn wrap(idx: u32, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; + fn wrap(idx: u32, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = PropertyCallbackInfo{ .raw = raw_info.? }; + const info = PropertyCallbackInfo{ .handle = handle.? }; return caller.getIndex(T, getter, idx, info, .{ .as_typed_array = opts.as_typed_array, .null_as_undefined = opts.null_as_undefined, @@ -761,9 +767,9 @@ pub const Indexed = struct { }; pub const NamedIndexed = struct { - getter: *const fn (c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8, - setter: ?*const fn (c_name: ?*const v8.C_Name, c_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 = null, - deleter: ?*const fn (c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 = null, + getter: *const fn (c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8, + setter: ?*const fn (c_name: ?*const v8.c.Name, c_value: ?*const v8.c.Value, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 = null, + deleter: ?*const fn (c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 = null, const Opts = struct { as_typed_array: bool = false, @@ -772,12 +778,12 @@ pub const NamedIndexed = struct { fn init(comptime T: type, comptime getter: anytype, setter: anytype, deleter: anytype, comptime opts: Opts) NamedIndexed { const getter_fn = struct { - fn wrap(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; + fn wrap(c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = PropertyCallbackInfo{ .raw = raw_info.? }; + const info = PropertyCallbackInfo{ .handle = handle.? }; return caller.getNamedIndex(T, getter, .{ .handle = c_name.? }, info, .{ .as_typed_array = opts.as_typed_array, .null_as_undefined = opts.null_as_undefined, @@ -786,12 +792,12 @@ pub const NamedIndexed = struct { }.wrap; const setter_fn = if (@typeInfo(@TypeOf(setter)) == .null) null else struct { - fn wrap(c_name: ?*const v8.C_Name, c_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; + fn wrap(c_name: ?*const v8.c.Name, c_value: ?*const v8.c.Value, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = PropertyCallbackInfo{ .raw = raw_info.? }; + const info = PropertyCallbackInfo{ .handle = handle.? }; return caller.setNamedIndex(T, setter, .{ .handle = c_name.? }, .{ .handle = c_value.? }, info, .{ .as_typed_array = opts.as_typed_array, .null_as_undefined = opts.null_as_undefined, @@ -800,12 +806,12 @@ pub const NamedIndexed = struct { }.wrap; const deleter_fn = if (@typeInfo(@TypeOf(deleter)) == .null) null else struct { - fn wrap(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; + fn wrap(c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = PropertyCallbackInfo{ .raw = raw_info.? }; + const info = PropertyCallbackInfo{ .handle = handle.? }; return caller.deleteNamedIndex(T, deleter, .{ .handle = c_name.? }, info, .{ .as_typed_array = opts.as_typed_array, .null_as_undefined = opts.null_as_undefined, @@ -822,7 +828,7 @@ pub const NamedIndexed = struct { }; pub const Iterator = struct { - func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, async: bool, const Opts = struct { @@ -835,8 +841,8 @@ pub const Iterator = struct { return .{ .async = opts.async, .func = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const info = v8.FunctionCallbackInfo.initFromV8(raw_info); + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const info = FunctionCallbackInfo{ .handle = handle.? }; info.getReturnValue().set(info.getThis()); } }.wrap, @@ -846,12 +852,12 @@ pub const Iterator = struct { return .{ .async = opts.async, .func = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; caller.method(T, struct_or_func, info, .{}); } }.wrap, @@ -860,7 +866,7 @@ pub const Iterator = struct { }; pub const Callable = struct { - func: *const fn (?*const v8.C_FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { null_as_undefined: bool = false, @@ -868,12 +874,12 @@ pub const Callable = struct { fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Callable { return .{ .func = struct { - fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?; + fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); - const info = CallbackInfo{ .raw = raw_info.? }; + const info = FunctionCallbackInfo{ .handle = handle.? }; caller.method(T, func, info, .{ .null_as_undefined = opts.null_as_undefined, }); diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index a5cd9cbd..6b895687 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -368,7 +368,7 @@ pub const PrototypeChainEntry = struct { // point of view, is an arbitrary string. pub export fn v8_inspector__Client__IMPL__valueSubtype( _: *v8.c.InspectorClientImpl, - c_value: *const v8.C_Value, + c_value: *const v8.c.Value, ) callconv(.c) [*c]const u8 { const external_entry = Inspector.getTaggedAnyOpaque(c_value) orelse return null; return if (external_entry.subtype) |st| @tagName(st) else null; @@ -380,8 +380,8 @@ pub export fn v8_inspector__Client__IMPL__valueSubtype( // put an empty description. pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype( _: *v8.c.InspectorClientImpl, - v8_context: *const v8.C_Context, - c_value: *const v8.C_Value, + v8_context: *const v8.c.Context, + c_value: *const v8.c.Value, ) callconv(.c) [*c]const u8 { _ = v8_context; diff --git a/src/cdp/domains/dom.zig b/src/cdp/domains/dom.zig index c6c78184..e11a98fa 100644 --- a/src/cdp/domains/dom.zig +++ b/src/cdp/domains/dom.zig @@ -275,12 +275,10 @@ fn resolveNode(cmd: anytype) !void { var js_context = page.js; if (params.executionContextId) |context_id| { - const v8_context = v8.Context{ .handle = js_context.handle }; - if (v8_context.debugContextId() != context_id) { + if (js_context.debugContextId() != context_id) { for (bc.isolated_worlds.items) |*isolated_world| { js_context = &(isolated_world.executor.context orelse return error.ContextNotFound); - const isolated_v8_context = v8.Context{ .handle = js_context.handle }; - if (isolated_v8_context.debugContextId() == context_id) { + if (js_context.debugContextId() == context_id) { break; } } else return error.ContextNotFound; diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index c861c317..9ba9db59 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -196,8 +196,7 @@ fn createIsolatedWorld(cmd: anytype) !void { const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{params.frameId}); bc.inspector.contextCreated(js_context, world.name, "", aux_data, false); - const v8_context = v8.Context{ .handle = js_context.handle }; - return cmd.sendResult(.{ .executionContextId = v8_context.debugContextId() }, .{}); + return cmd.sendResult(.{ .executionContextId = js_context.debugContextId() }, .{}); } fn navigate(cmd: anytype) !void { From f2a9125b997ec281b6c1b1b7a2b180ce11aaaacd Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 2 Jan 2026 16:10:07 +0800 Subject: [PATCH 15/26] js.v8 is not equal to js.v8.c This means the C funtions/types now sit in the root of v8. --- src/browser/js/Array.zig | 10 +- src/browser/js/BigInt.zig | 12 +- src/browser/js/Context.zig | 186 ++++++++++++++--------------- src/browser/js/Env.zig | 52 ++++---- src/browser/js/ExecutionWorld.zig | 22 ++-- src/browser/js/Function.zig | 24 ++-- src/browser/js/HandleScope.zig | 6 +- src/browser/js/Inspector.zig | 148 +++++++++++------------ src/browser/js/Integer.zig | 8 +- src/browser/js/Isolate.zig | 70 +++++------ src/browser/js/Module.zig | 48 ++++---- src/browser/js/Name.zig | 2 +- src/browser/js/Number.zig | 6 +- src/browser/js/Object.zig | 52 ++++---- src/browser/js/Platform.zig | 16 +-- src/browser/js/Promise.zig | 4 +- src/browser/js/PromiseResolver.zig | 14 +-- src/browser/js/Snapshot.zig | 169 +++++++++++++------------- src/browser/js/String.zig | 8 +- src/browser/js/TryCatch.zig | 20 ++-- src/browser/js/Value.zig | 84 ++++++------- src/browser/js/bridge.zig | 122 +++++++++---------- src/browser/js/global.zig | 10 +- src/browser/js/js.zig | 56 ++++----- 24 files changed, 575 insertions(+), 574 deletions(-) diff --git a/src/browser/js/Array.zig b/src/browser/js/Array.zig index 9596ca4c..98442468 100644 --- a/src/browser/js/Array.zig +++ b/src/browser/js/Array.zig @@ -23,17 +23,17 @@ const v8 = js.v8; const Array = @This(); ctx: *js.Context, -handle: *const v8.c.Array, +handle: *const v8.Array, pub fn len(self: Array) usize { - return v8.c.v8__Array__Length(self.handle); + return v8.v8__Array__Length(self.handle); } pub fn get(self: Array, index: u32) !js.Value { const ctx = self.ctx; const idx = js.Integer.init(ctx.isolate.handle, index); - const handle = v8.c.v8__Object__Get(@ptrCast(self.handle), ctx.handle, idx.handle) orelse { + const handle = v8.v8__Object__Get(@ptrCast(self.handle), ctx.handle, idx.handle) orelse { return error.JsException; }; @@ -48,8 +48,8 @@ pub fn set(self: Array, index: u32, value: anytype, comptime opts: js.bridge.Cal const js_value = try ctx.zigValueToJs(value, opts); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__SetAtIndex(@ptrCast(self.handle), ctx.handle, index, js_value.handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Object__SetAtIndex(@ptrCast(self.handle), ctx.handle, index, js_value.handle, &out); return out.has_value; } diff --git a/src/browser/js/BigInt.zig b/src/browser/js/BigInt.zig index 16bab38d..5443d69a 100644 --- a/src/browser/js/BigInt.zig +++ b/src/browser/js/BigInt.zig @@ -21,23 +21,23 @@ const v8 = js.v8; const BigInt = @This(); -handle: *const v8.c.Integer, +handle: *const v8.Integer, -pub fn init(isolate: *v8.c.Isolate, val: anytype) BigInt { +pub fn init(isolate: *v8.Isolate, val: anytype) BigInt { const handle = switch (@TypeOf(val)) { - i8, i16, i32, i64, isize => v8.c.v8__BigInt__New(isolate, val).?, - u8, u16, u32, u64, usize => v8.c.v8__BigInt__NewFromUnsigned(isolate, val).?, + i8, i16, i32, i64, isize => v8.v8__BigInt__New(isolate, val).?, + u8, u16, u32, u64, usize => v8.v8__BigInt__NewFromUnsigned(isolate, val).?, else => |T| @compileError("cannot create v8::BigInt from: " ++ @typeName(T)), }; return .{ .handle = handle }; } pub fn getInt64(self: BigInt) i64 { - return v8.c.v8__BigInt__Int64Value(self.handle, null); + return v8.v8__BigInt__Int64Value(self.handle, null); } pub fn getUint64(self: BigInt) u64 { - return v8.c.v8__BigInt__Uint64Value(self.handle, null); + return v8.v8__BigInt__Uint64Value(self.handle, null); } pub fn toValue(self: BigInt) js.Value { diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index d67fb60d..d55b62d2 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -41,14 +41,14 @@ id: usize, page: *Page, isolate: js.Isolate, // This context is a persistent object. The persistent needs to be recovered and reset. -handle: *const v8.c.Context, +handle: *const v8.Context, handle_scope: ?js.HandleScope, -cpu_profiler: ?*v8.c.CpuProfiler = null, +cpu_profiler: ?*v8.CpuProfiler = null, // references Env.templates -templates: []*const v8.c.FunctionTemplate, +templates: []*const v8.FunctionTemplate, // Arena for the lifetime of the context arena: Allocator, @@ -115,21 +115,21 @@ const ModuleEntry = struct { resolver_promise: ?js.Promise = null, }; -pub fn fromC(c_context: *const v8.c.Context) *Context { - const data = v8.c.v8__Context__GetEmbedderData(c_context, 1).?; +pub fn fromC(c_context: *const v8.Context) *Context { + const data = v8.v8__Context__GetEmbedderData(c_context, 1).?; const big_int = js.BigInt{ .handle = @ptrCast(data) }; return @ptrFromInt(big_int.getUint64()); } pub fn fromIsolate(isolate: js.Isolate) *Context { - const v8_context = v8.c.v8__Isolate__GetCurrentContext(isolate.handle).?; - const data = v8.c.v8__Context__GetEmbedderData(v8_context, 1).?; + const v8_context = v8.v8__Isolate__GetCurrentContext(isolate.handle).?; + const data = v8.v8__Context__GetEmbedderData(v8_context, 1).?; const big_int = js.BigInt{ .handle = @ptrCast(data) }; return @ptrFromInt(big_int.getUint64()); } pub fn setupGlobal(self: *Context) !void { - const global = v8.c.v8__Context__Global(self.handle).?; + const global = v8.v8__Context__Global(self.handle).?; _ = try self.mapZigInstanceToJs(global, self.page.window); } @@ -167,7 +167,7 @@ pub fn deinit(self: *Context) void { if (self.handle_scope) |*scope| { scope.deinit(); - v8.c.v8__Context__Exit(self.handle); + v8.v8__Context__Exit(self.handle); } } @@ -333,20 +333,20 @@ pub fn newString(self: *Context, str: []const u8) js.String { pub fn newObject(self: *Context) js.Object { return .{ .ctx = self, - .handle = v8.c.v8__Object__New(self.isolate.handle).?, + .handle = v8.v8__Object__New(self.isolate.handle).?, }; } pub fn newArray(self: *Context, len: u32) js.Array { return .{ .ctx = self, - .handle = v8.c.v8__Array__New(self.isolate.handle, @intCast(len)).?, + .handle = v8.v8__Array__New(self.isolate.handle, @intCast(len)).?, }; } -fn newFunctionWithData(self: *Context, comptime callback: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, data: *anyopaque) js.Function { +fn newFunctionWithData(self: *Context, comptime callback: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, data: *anyopaque) js.Function { const external = self.isolate.createExternal(data); - const handle = v8.c.v8__Function__New__DEFAULT2(self.handle, callback, @ptrCast(external)).?; + const handle = v8.v8__Function__New__DEFAULT2(self.handle, callback, @ptrCast(external)).?; return .{ .ctx = self, .handle = handle, @@ -355,7 +355,7 @@ fn newFunctionWithData(self: *Context, comptime callback: *const fn (?*const v8. pub fn parseJSON(self: *Context, json: []const u8) !js.Value { const string_handle = self.isolate.initStringHandle(json); - const value_handle = v8.c.v8__JSON__Parse(self.handle, string_handle) orelse return error.JsException; + const value_handle = v8.v8__JSON__Parse(self.handle, string_handle) orelse return error.JsException; return .{ .ctx = self, .handle = value_handle, @@ -371,7 +371,7 @@ pub fn throw(self: *Context, err: []const u8) js.Exception { } pub fn debugContextId(self: *const Context) i32 { - return v8.c.v8__Context__DebugContextId(self.handle); + return v8.v8__Context__DebugContextId(self.handle); } pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !js.Value { @@ -538,7 +538,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp // 4 - Store our TaggedAnyOpaque into the persistent object // 5 - Update our identity_map (so that, if we return this same instance again, // we can just grab it from the identity_map) -pub fn mapZigInstanceToJs(self: *Context, js_obj_handle: ?*const v8.c.Object, value: anytype) !js.Object { +pub fn mapZigInstanceToJs(self: *Context, js_obj_handle: ?*const v8.Object, value: anytype) !js.Object { const arena = self.arena; const T = @TypeOf(value); @@ -572,8 +572,8 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_handle: ?*const v8.c.Object, va .ctx = self, .handle = js_obj_handle orelse blk: { const function_template_handle = self.templates[resolved.class_id]; - const object_template_handle = v8.c.v8__FunctionTemplate__InstanceTemplate(function_template_handle).?; - break :blk v8.c.v8__ObjectTemplate__NewInstance(object_template_handle, self.handle).?; + const object_template_handle = v8.v8__FunctionTemplate__InstanceTemplate(function_template_handle).?; + break :blk v8.v8__ObjectTemplate__NewInstance(object_template_handle, self.handle).?; }, }; @@ -592,7 +592,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_handle: ?*const v8.c.Object, va // Skip setting internal field for the global object (Window) // Window accessors get the instance from context.page.window instead if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) { - v8.c.v8__Object__SetInternalField(js_obj.handle, 0, isolate.createExternal(tao)); + v8.v8__Object__SetInternalField(js_obj.handle, 0, isolate.createExternal(tao)); } } else { // If the struct is empty, we don't need to do all @@ -835,31 +835,31 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: js.Value) !?T { fn jsValueToTypedArray(_: *Context, comptime T: type, js_value: js.Value) !?[]T { var force_u8 = false; - var array_buffer: ?*const v8.c.ArrayBuffer = null; + var array_buffer: ?*const v8.ArrayBuffer = null; var byte_len: usize = undefined; var byte_offset: usize = undefined; if (js_value.isTypedArray()) { - const buffer_handle: *const v8.c.ArrayBufferView = @ptrCast(js_value.handle); - byte_len = v8.c.v8__ArrayBufferView__ByteLength(buffer_handle); - byte_offset = v8.c.v8__ArrayBufferView__ByteOffset(buffer_handle); - array_buffer = v8.c.v8__ArrayBufferView__Buffer(buffer_handle).?; + const buffer_handle: *const v8.ArrayBufferView = @ptrCast(js_value.handle); + byte_len = v8.v8__ArrayBufferView__ByteLength(buffer_handle); + byte_offset = v8.v8__ArrayBufferView__ByteOffset(buffer_handle); + array_buffer = v8.v8__ArrayBufferView__Buffer(buffer_handle).?; } else if (js_value.isArrayBufferView()) { force_u8 = true; - const buffer_handle: *const v8.c.ArrayBufferView = @ptrCast(js_value.handle); - byte_len = v8.c.v8__ArrayBufferView__ByteLength(buffer_handle); - byte_offset = v8.c.v8__ArrayBufferView__ByteOffset(buffer_handle); - array_buffer = v8.c.v8__ArrayBufferView__Buffer(buffer_handle).?; + const buffer_handle: *const v8.ArrayBufferView = @ptrCast(js_value.handle); + byte_len = v8.v8__ArrayBufferView__ByteLength(buffer_handle); + byte_offset = v8.v8__ArrayBufferView__ByteOffset(buffer_handle); + array_buffer = v8.v8__ArrayBufferView__Buffer(buffer_handle).?; } else if (js_value.isArrayBuffer()) { force_u8 = true; array_buffer = @ptrCast(js_value.handle); - byte_len = v8.c.v8__ArrayBuffer__ByteLength(array_buffer); + byte_len = v8.v8__ArrayBuffer__ByteLength(array_buffer); byte_offset = 0; } - const backing_store_ptr = v8.c.v8__ArrayBuffer__GetBackingStore(array_buffer orelse return null); - const backing_store_handle = v8.c.std__shared_ptr__v8__BackingStore__get(&backing_store_ptr).?; - const data = v8.c.v8__BackingStore__Data(backing_store_handle); + const backing_store_ptr = v8.v8__ArrayBuffer__GetBackingStore(array_buffer orelse return null); + const backing_store_handle = v8.std__shared_ptr__v8__BackingStore__get(&backing_store_ptr).?; + const data = v8.v8__BackingStore__Data(backing_store_handle); switch (T) { u8 => { @@ -979,11 +979,11 @@ pub fn valueToStringZ(self: *Context, js_val: js.Value, opts: ToStringOpts) ![:0 fn _valueToString(self: *Context, comptime null_terminate: bool, js_val: js.Value, opts: ToStringOpts) !(if (null_terminate) [:0]u8 else []u8) { if (js_val.isSymbol()) { - const symbol_handle = v8.c.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?; + const symbol_handle = v8.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?; return self._valueToString(null_terminate, .{ .ctx = self, .handle = symbol_handle }, opts); } - const string_handle = v8.c.v8__Value__ToString(js_val.handle, self.handle) orelse { + const string_handle = v8.v8__Value__ToString(js_val.handle, self.handle) orelse { return error.JsException; }; @@ -1002,10 +1002,10 @@ pub fn jsStringToZigZ(self: *const Context, str: anytype, opts: ToStringOpts) ![ fn _jsStringToZig(self: *const Context, comptime null_terminate: bool, str: anytype, opts: ToStringOpts) !(if (null_terminate) [:0]u8 else []u8) { const handle = if (@TypeOf(str) == js.String) str.handle else str; - const len = v8.c.v8__String__Utf8Length(handle, self.isolate.handle); + const len = v8.v8__String__Utf8Length(handle, self.isolate.handle); const allocator = opts.allocator orelse self.call_arena; const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(len), 0) else allocator.alloc(u8, @intCast(len))); - const n = v8.c.v8__String__WriteUtf8(handle, self.isolate.handle, buf.ptr, buf.len, v8.c.NO_NULL_TERMINATION | v8.c.REPLACE_INVALID_UTF8); + const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); std.debug.assert(n == len); return buf; @@ -1037,7 +1037,7 @@ fn _debugValue(self: *Context, js_val: js.Value, seen: *std.AutoHashMapUnmanaged } if (js_val.isSymbol()) { - const symbol_handle = v8.c.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?; + const symbol_handle = v8.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?; const js_sym_str = try self.valueToString(.{ .ctx = self, .handle = symbol_handle }, .{}); return writer.print("{s} (symbol)", .{js_sym_str}); } @@ -1108,20 +1108,20 @@ pub fn stackTrace(self: *const Context) !?[]const u8 { var buf: std.ArrayList(u8) = .empty; var writer = buf.writer(self.call_arena); - const stack_trace_handle = v8.c.v8__StackTrace__CurrentStackTrace__STATIC(isolate.handle, 30).?; - const frame_count = v8.c.v8__StackTrace__GetFrameCount(stack_trace_handle); + const stack_trace_handle = v8.v8__StackTrace__CurrentStackTrace__STATIC(isolate.handle, 30).?; + const frame_count = v8.v8__StackTrace__GetFrameCount(stack_trace_handle); - if (v8.c.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| { + if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| { try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) }); } for (0..@intCast(frame_count)) |i| { - const frame_handle = v8.c.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?; - if (v8.c.v8__StackFrame__GetFunctionName(frame_handle)) |name| { + const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?; + if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| { const script = try self.jsStringToZig(name, .{}); - try writer.print("{s}{s}:{d}", .{ separator, script, v8.c.v8__StackFrame__GetLineNumber(frame_handle) }); + try writer.print("{s}{s}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) }); } else { - try writer.print("{s}:{d}", .{ separator, v8.c.v8__StackFrame__GetLineNumber(frame_handle) }); + try writer.print("{s}:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) }); } } return buf.items; @@ -1152,11 +1152,11 @@ pub fn createPromiseResolver(self: *Context) js.PromiseResolver { // Callback from V8, asking us to load a module. The "specifier" is // the src of the module to load. fn resolveModuleCallback( - c_context: ?*const v8.c.Context, - c_specifier: ?*const v8.c.String, - import_attributes: ?*const v8.c.FixedArray, - c_referrer: ?*const v8.c.Module, -) callconv(.c) ?*const v8.c.Module { + c_context: ?*const v8.Context, + c_specifier: ?*const v8.String, + import_attributes: ?*const v8.FixedArray, + c_referrer: ?*const v8.Module, +) callconv(.c) ?*const v8.Module { _ = import_attributes; const self = fromC(c_context.?); @@ -1177,12 +1177,12 @@ fn resolveModuleCallback( } pub fn dynamicModuleCallback( - c_context: ?*const v8.c.Context, - host_defined_options: ?*const v8.c.Data, - resource_name: ?*const v8.c.Value, - v8_specifier: ?*const v8.c.String, - import_attrs: ?*const v8.c.FixedArray, -) callconv(.c) ?*v8.c.Promise { + c_context: ?*const v8.Context, + host_defined_options: ?*const v8.Data, + resource_name: ?*const v8.Value, + v8_specifier: ?*const v8.String, + import_attrs: ?*const v8.FixedArray, +) callconv(.c) ?*v8.Promise { _ = host_defined_options; _ = import_attrs; @@ -1216,7 +1216,7 @@ pub fn dynamicModuleCallback( return @constCast(promise.handle); } -pub fn metaObjectCallback(c_context: ?*v8.c.Context, c_module: ?*v8.c.Module, c_meta: ?*v8.c.Value) callconv(.c) void { +pub fn metaObjectCallback(c_context: ?*v8.Context, c_module: ?*v8.Module, c_meta: ?*v8.Value) callconv(.c) void { const self = fromC(c_context.?); const m = js.Module{ .ctx = self, .handle = c_module.? }; const meta = js.Object{ .ctx = self, .handle = @ptrCast(c_meta.?) }; @@ -1237,7 +1237,7 @@ pub fn metaObjectCallback(c_context: ?*v8.c.Context, c_module: ?*v8.c.Module, c_ } } -fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]const u8) !?*const v8.c.Module { +fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]const u8) !?*const v8.Module { const referrer_path = self.module_identifier.get(referrer.getIdentityHash()) orelse { // Shouldn't be possible. return error.UnknownModuleReferrer; @@ -1420,13 +1420,13 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul // resolve to the module's namespace. const then_callback = self.newFunctionWithData(struct { - pub fn callback(callback_handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(callback_handle).?; + pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const isolate = v8.v8__FunctionCallbackInfo__GetIsolate(callback_handle).?; var caller = Caller.init(isolate); defer caller.deinit(); - const info_data = v8.c.v8__FunctionCallbackInfo__Data(callback_handle).?; - const s: *DynamicModuleResolveState = @ptrCast(@alignCast(v8.c.v8__External__Value(@ptrCast(info_data)))); + const info_data = v8.v8__FunctionCallbackInfo__Data(callback_handle).?; + const s: *DynamicModuleResolveState = @ptrCast(@alignCast(v8.v8__External__Value(@ptrCast(info_data)))); if (s.context_id != caller.context.id) { // The microtask is tied to the isolate, not the context @@ -1444,13 +1444,13 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul }.callback, @ptrCast(state)); const catch_callback = self.newFunctionWithData(struct { - pub fn callback(callback_handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(callback_handle).?; + pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const isolate = v8.v8__FunctionCallbackInfo__GetIsolate(callback_handle).?; var caller = Caller.init(isolate); defer caller.deinit(); - const info_data = v8.c.v8__FunctionCallbackInfo__Data(callback_handle).?; - const s: *DynamicModuleResolveState = @ptrCast(@alignCast(v8.c.v8__External__Value(@ptrCast(info_data)))); + const info_data = v8.v8__FunctionCallbackInfo__Data(callback_handle).?; + const s: *DynamicModuleResolveState = @ptrCast(@alignCast(v8.v8__External__Value(@ptrCast(info_data)))); const ctx = caller.context; if (s.context_id != ctx.id) { @@ -1460,7 +1460,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul defer ctx.runMicrotasks(); _ = s.resolver.reject("catch callback", js.Value{ .ctx = ctx, - .handle = v8.c.v8__FunctionCallbackInfo__Data(callback_handle).?, + .handle = v8.v8__FunctionCallbackInfo__Data(callback_handle).?, }); } }.callback, @ptrCast(state)); @@ -1478,7 +1478,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul // Reverses the mapZigInstanceToJs, making sure that our TaggedAnyOpaque // contains a ptr to the correct type. -pub fn typeTaggedAnyOpaque(comptime R: type, js_obj_handle: *const v8.c.Object) !R { +pub fn typeTaggedAnyOpaque(comptime R: type, js_obj_handle: *const v8.Object) !R { const ti = @typeInfo(R); if (ti != .pointer) { @compileError("non-pointer Zig parameter type: " ++ @typeName(R)); @@ -1494,14 +1494,14 @@ pub fn typeTaggedAnyOpaque(comptime R: type, js_obj_handle: *const v8.c.Object) return @constCast(@as(*const T, &.{})); } - const internal_field_count = v8.c.v8__Object__InternalFieldCount(js_obj_handle); + const internal_field_count = v8.v8__Object__InternalFieldCount(js_obj_handle); // Special case for Window: the global object doesn't have internal fields // Window instance is stored in context.page.window instead if (internal_field_count == 0) { // Normally, this would be an error. All JsObject that map to a Zig type // are either `empty_with_no_proto` (handled above) or have an // interalFieldCount. The only exception to that is the Window... - const isolate = v8.c.v8__Object__GetIsolate(js_obj_handle).?; + const isolate = v8.v8__Object__GetIsolate(js_obj_handle).?; const context = fromIsolate(.{ .handle = isolate }); const Window = @import("../webapi/Window.zig"); @@ -1533,8 +1533,8 @@ pub fn typeTaggedAnyOpaque(comptime R: type, js_obj_handle: *const v8.c.Object) @compileError("unknown Zig type: " ++ @typeName(R)); } - const internal_field_handle = v8.c.v8__Object__GetInternalField(js_obj_handle, 0).?; - const tao: *TaggedAnyOpaque = @ptrCast(@alignCast(v8.c.v8__External__Value(internal_field_handle))); + const internal_field_handle = v8.v8__Object__GetInternalField(js_obj_handle, 0).?; + const tao: *TaggedAnyOpaque = @ptrCast(@alignCast(v8.v8__External__Value(internal_field_handle))); const expected_type_index = bridge.JsApiLookup.getId(JsApi); const prototype_chain = tao.prototype_chain[0..tao.prototype_len]; @@ -1821,30 +1821,30 @@ fn compileAndRun(self: *Context, src: []const u8, name: ?[]const u8) !js.Value { const script_source = self.isolate.initStringHandle(src); // Create ScriptOrigin - var origin: v8.c.ScriptOrigin = undefined; - v8.c.v8__ScriptOrigin__CONSTRUCT(&origin, @ptrCast(script_name)); + var origin: v8.ScriptOrigin = undefined; + v8.v8__ScriptOrigin__CONSTRUCT(&origin, @ptrCast(script_name)); // Create ScriptCompilerSource - var script_comp_source: v8.c.ScriptCompilerSource = undefined; - v8.c.v8__ScriptCompiler__Source__CONSTRUCT2(script_source, &origin, null, &script_comp_source); - defer v8.c.v8__ScriptCompiler__Source__DESTRUCT(&script_comp_source); + var script_comp_source: v8.ScriptCompilerSource = undefined; + v8.v8__ScriptCompiler__Source__CONSTRUCT2(script_source, &origin, null, &script_comp_source); + defer v8.v8__ScriptCompiler__Source__DESTRUCT(&script_comp_source); // Compile the script - const v8_script = v8.c.v8__ScriptCompiler__Compile( + const v8_script = v8.v8__ScriptCompiler__Compile( self.handle, &script_comp_source, - v8.c.kNoCompileOptions, - v8.c.kNoCacheNoReason, + v8.kNoCompileOptions, + v8.kNoCacheNoReason, ) orelse return error.CompilationError; // Run the script - const result = v8.c.v8__Script__Run(v8_script, self.handle) orelse return error.ExecutionError; + const result = v8.v8__Script__Run(v8_script, self.handle) orelse return error.ExecutionError; return .{ .ctx = self, .handle = result }; } fn compileModule(self: *Context, src: []const u8, name: []const u8) !js.Module { - var origin_handle: v8.c.ScriptOrigin = undefined; - v8.c.v8__ScriptOrigin__CONSTRUCT2( + var origin_handle: v8.ScriptOrigin = undefined; + v8.v8__ScriptOrigin__CONSTRUCT2( &origin_handle, self.isolate.initStringHandle(name), 0, // resource_line_offset @@ -1858,21 +1858,21 @@ fn compileModule(self: *Context, src: []const u8, name: []const u8) !js.Module { null, // host_defined_options ); - var source_handle: v8.c.ScriptCompilerSource = undefined; - v8.c.v8__ScriptCompiler__Source__CONSTRUCT2( + var source_handle: v8.ScriptCompilerSource = undefined; + v8.v8__ScriptCompiler__Source__CONSTRUCT2( self.isolate.initStringHandle(src), &origin_handle, null, // cached data &source_handle, ); - defer v8.c.v8__ScriptCompiler__Source__DESTRUCT(&source_handle); + defer v8.v8__ScriptCompiler__Source__DESTRUCT(&source_handle); - const module_handle = v8.c.v8__ScriptCompiler__CompileModule( + const module_handle = v8.v8__ScriptCompiler__CompileModule( self.isolate.handle, &source_handle, - v8.c.kNoCompileOptions, - v8.c.kNoCacheNoReason, + v8.kNoCompileOptions, + v8.kNoCacheNoReason, ) orelse { return error.JsException; }; @@ -1998,17 +1998,17 @@ pub fn startCpuProfiler(self: *Context) void { } std.debug.assert(self.cpu_profiler == null); - v8.c.v8__CpuProfiler__UseDetailedSourcePositionsForProfiling(self.isolate.handle); + v8.v8__CpuProfiler__UseDetailedSourcePositionsForProfiling(self.isolate.handle); - const cpu_profiler = v8.c.v8__CpuProfiler__Get(self.isolate.handle).?; + const cpu_profiler = v8.v8__CpuProfiler__Get(self.isolate.handle).?; const title = self.isolate.initStringHandle("v8_cpu_profile"); - v8.c.v8__CpuProfiler__StartProfiling(cpu_profiler, title); + v8.v8__CpuProfiler__StartProfiling(cpu_profiler, title); self.cpu_profiler = cpu_profiler; } pub fn stopCpuProfiler(self: *Context) ![]const u8 { const title = self.isolate.initStringHandle("v8_cpu_profile"); - const handle = v8.c.v8__CpuProfiler__StopProfiling(self.cpu_profiler.?, title) orelse return error.NoProfiles; - const string_handle = v8.c.v8__CpuProfile__Serialize(handle, self.isolate.handle) orelse return error.NoProfile; + const handle = v8.v8__CpuProfiler__StopProfiling(self.cpu_profiler.?, title) orelse return error.NoProfiles; + const string_handle = v8.v8__CpuProfile__Serialize(handle, self.isolate.handle) orelse return error.NoProfile; return self.jsStringToZig(string_handle, .{}); } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 6a11834a..7962e66d 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -49,44 +49,44 @@ platform: *const Platform, isolate: js.Isolate, // just kept around because we need to free it on deinit -isolate_params: *v8.c.CreateParams, +isolate_params: *v8.CreateParams, context_id: usize, // Global handles that need to be freed on deinit -globals: []v8.c.Global, +globals: []v8.Global, // Dynamic slice to avoid circular dependency on JsApis.len at comptime -templates: []*const v8.c.FunctionTemplate, +templates: []*const v8.FunctionTemplate, pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot) !Env { - var params = try allocator.create(v8.c.CreateParams); + var params = try allocator.create(v8.CreateParams); errdefer allocator.destroy(params); - v8.c.v8__Isolate__CreateParams__CONSTRUCT(params); + v8.v8__Isolate__CreateParams__CONSTRUCT(params); params.snapshot_blob = @ptrCast(&snapshot.startup_data); - params.array_buffer_allocator = v8.c.v8__ArrayBuffer__Allocator__NewDefaultAllocator().?; - errdefer v8.c.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); + params.array_buffer_allocator = v8.v8__ArrayBuffer__Allocator__NewDefaultAllocator().?; + errdefer v8.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); params.external_references = &snapshot.external_references; var isolate = js.Isolate.init(params); errdefer isolate.deinit(); - v8.c.v8__Isolate__SetHostImportModuleDynamicallyCallback(isolate.handle, Context.dynamicModuleCallback); - v8.c.v8__Isolate__SetPromiseRejectCallback(isolate.handle, promiseRejectCallback); - v8.c.v8__Isolate__SetMicrotasksPolicy(isolate.handle, v8.c.kExplicit); + v8.v8__Isolate__SetHostImportModuleDynamicallyCallback(isolate.handle, Context.dynamicModuleCallback); + v8.v8__Isolate__SetPromiseRejectCallback(isolate.handle, promiseRejectCallback); + v8.v8__Isolate__SetMicrotasksPolicy(isolate.handle, v8.kExplicit); isolate.enter(); errdefer isolate.exit(); - v8.c.v8__Isolate__SetHostInitializeImportMetaObjectCallback(isolate.handle, Context.metaObjectCallback); + v8.v8__Isolate__SetHostInitializeImportMetaObjectCallback(isolate.handle, Context.metaObjectCallback); // Allocate arrays dynamically to avoid comptime dependency on JsApis.len - const globals = try allocator.alloc(v8.c.Global, JsApis.len); + const globals = try allocator.alloc(v8.Global, JsApis.len); errdefer allocator.free(globals); - const templates = try allocator.alloc(*const v8.c.FunctionTemplate, JsApis.len); + const templates = try allocator.alloc(*const v8.FunctionTemplate, JsApis.len); errdefer allocator.free(templates); { @@ -95,15 +95,15 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot defer temp_scope.deinit(); const context_handle = isolate.createContextHandle(null, null); - v8.c.v8__Context__Enter(context_handle); - defer v8.c.v8__Context__Exit(context_handle); + v8.v8__Context__Enter(context_handle); + defer v8.v8__Context__Exit(context_handle); inline for (JsApis, 0..) |JsApi, i| { JsApi.Meta.class_id = i; - const data = v8.c.v8__Context__GetDataFromSnapshotOnce(context_handle, snapshot.data_start + i); - const function_handle: *const v8.c.FunctionTemplate = @ptrCast(data); + const data = v8.v8__Context__GetDataFromSnapshotOnce(context_handle, snapshot.data_start + i); + const function_handle: *const v8.FunctionTemplate = @ptrCast(data); // Make function template global/persistent - v8.c.v8__Global__New(isolate.handle, @ptrCast(function_handle), &globals[i]); + v8.v8__Global__New(isolate.handle, @ptrCast(function_handle), &globals[i]); // Extract the local handle from the global for easy access templates[i] = @ptrCast(@alignCast(@as(*const anyopaque, @ptrFromInt(globals[i].data_ptr)))); } @@ -123,14 +123,14 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot pub fn deinit(self: *Env) void { // Free global handles before destroying the isolate for (self.globals) |*global| { - v8.c.v8__Global__Reset(global); + v8.v8__Global__Reset(global); } self.allocator.free(self.globals); self.allocator.free(self.templates); self.isolate.exit(); self.isolate.deinit(); - v8.c.v8__ArrayBuffer__Allocator__DELETE(self.isolate_params.array_buffer_allocator.?); + v8.v8__ArrayBuffer__Allocator__DELETE(self.isolate_params.array_buffer_allocator.?); self.allocator.destroy(self.isolate_params); } @@ -145,11 +145,11 @@ pub fn runMicrotasks(self: *const Env) void { } pub fn pumpMessageLoop(self: *const Env) bool { - return v8.c.v8__Platform__PumpMessageLoop(self.platform.handle, self.isolate.handle, false); + return v8.v8__Platform__PumpMessageLoop(self.platform.handle, self.isolate.handle, false); } pub fn runIdleTasks(self: *const Env) void { - v8.c.v8__Platform__RunIdleTasks(self.platform.handle, self.isolate.handle, 1); + v8.v8__Platform__RunIdleTasks(self.platform.handle, self.isolate.handle, 1); } pub fn newExecutionWorld(self: *Env) !ExecutionWorld { return .{ @@ -191,14 +191,14 @@ pub fn dumpMemoryStats(self: *Env) void { , .{ stats.total_heap_size, stats.total_heap_size_executable, stats.total_physical_size, stats.total_available_size, stats.used_heap_size, stats.heap_size_limit, stats.malloced_memory, stats.external_memory, stats.peak_malloced_memory, stats.number_of_native_contexts, stats.number_of_detached_contexts, stats.total_global_handles_size, stats.used_global_handles_size, stats.does_zap_garbage }); } -fn promiseRejectCallback(message_handle: v8.c.PromiseRejectMessage) callconv(.c) void { - const promise_handle = v8.c.v8__PromiseRejectMessage__GetPromise(&message_handle).?; - const isolate_handle = v8.c.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; +fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) void { + const promise_handle = v8.v8__PromiseRejectMessage__GetPromise(&message_handle).?; + const isolate_handle = v8.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; const js_isolate = js.Isolate{ .handle = isolate_handle }; const context = Context.fromIsolate(js_isolate); const value = - if (v8.c.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value| + if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value| context.valueToString(.{ .ctx = context, .handle = v8_value }, .{}) catch |err| @errorName(err) else "no value"; diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index 3ed14760..b8bbbf02 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -81,11 +81,12 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context temp_scope.init(isolate); defer temp_scope.deinit(); + // Getting this into the snapshot is tricky (anything involving the - // global is tricky). Easier to do here. + // global is tricky). Easier to do here const func_tmpl_handle = isolate.createFunctionTemplateHandle(); - const global_template = v8.c.v8__FunctionTemplate__InstanceTemplate(func_tmpl_handle).?; - var configuration: v8.c.NamedPropertyHandlerConfiguration = .{ + const global_template = v8.v8__FunctionTemplate__InstanceTemplate(func_tmpl_handle).?; + var configuration: v8.NamedPropertyHandlerConfiguration = .{ .getter = unknownPropertyCallback, .setter = null, .query = null, @@ -94,9 +95,9 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context .definer = null, .descriptor = null, .data = null, - .flags = v8.c.kOnlyInterceptStrings | v8.c.kNonMasking, + .flags = v8.kOnlyInterceptStrings | v8.kNonMasking, }; - v8.c.v8__ObjectTemplate__SetNamedHandler(global_template, &configuration); + v8.v8__ObjectTemplate__SetNamedHandler(global_template, &configuration); const context_handle = isolate.createContextHandle(null, null); break :blk js.Global(Context).init(isolate.handle, context_handle); @@ -110,10 +111,10 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context if (enter) { handle_scope = @as(js.HandleScope, undefined); handle_scope.?.init(isolate); - v8.c.v8__Context__Enter(v8_context); + v8.v8__Context__Enter(v8_context); } errdefer if (enter) { - v8.c.v8__Context__Exit(v8_context); + v8.v8__Context__Exit(v8_context); handle_scope.?.deinit(); }; @@ -137,7 +138,7 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context // Store a pointer to our context inside the v8 context so that, given // a v8 context, we can get our context out const data = isolate.initBigInt(@intFromPtr(context)); - v8.c.v8__Context__SetEmbedderData(context.handle, 1, @ptrCast(data.handle)); + v8.v8__Context__SetEmbedderData(context.handle, 1, @ptrCast(data.handle)); try context.setupGlobal(); return context; @@ -168,8 +169,9 @@ pub fn resumeExecution(self: *const ExecutionWorld) void { self.env.isolate.cancelTerminateExecution(); } -pub fn unknownPropertyCallback(c_name: ?*const v8.c.Name, raw_info: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { - const isolate_handle = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; + +pub fn unknownPropertyCallback(c_name: ?*const v8.Name, raw_info: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const isolate_handle = v8.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; const context = Context.fromIsolate(.{ .handle = isolate_handle }); const property: ?[]u8 = context.valueToString(.{ .ctx = context, .handle = c_name.? }, .{}) catch { diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index 73efcb64..a08222e8 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -25,8 +25,8 @@ const Allocator = std.mem.Allocator; const Function = @This(); ctx: *js.Context, -this: ?*const v8.c.Object = null, -handle: *const v8.c.Function, +this: ?*const v8.Object = null, +handle: *const v8.Function, pub const Result = struct { stack: ?[]const u8, @@ -34,7 +34,7 @@ pub const Result = struct { }; pub fn id(self: *const Function) u32 { - return @as(u32, @bitCast(v8.c.v8__Object__GetIdentityHash(@ptrCast(self.handle)))); + return @as(u32, @bitCast(v8.v8__Object__GetIdentityHash(@ptrCast(self.handle)))); } pub fn withThis(self: *const Function, value: anytype) !Function { @@ -59,7 +59,7 @@ pub fn newInstance(self: *const Function, result: *Result) !js.Object { // This creates a new instance using this Function as a constructor. // const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{})); - const handle = v8.c.v8__Function__NewInstance(self.handle, ctx.handle, 0, null) orelse { + const handle = v8.v8__Function__NewInstance(self.handle, ctx.handle, 0, null) orelse { if (try_catch.hasCaught()) { const allocator = ctx.call_arena; result.stack = try_catch.stack(allocator) catch null; @@ -129,18 +129,18 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args const aargs = if (comptime @typeInfo(@TypeOf(args)) == .null) struct {}{} else args; - const js_args: []const *const v8.c.Value = switch (@typeInfo(@TypeOf(aargs))) { + const js_args: []const *const v8.Value = switch (@typeInfo(@TypeOf(aargs))) { .@"struct" => |s| blk: { const fields = s.fields; - var js_args: [fields.len]*const v8.c.Value = undefined; + var js_args: [fields.len]*const v8.Value = undefined; inline for (fields, 0..) |f, i| { js_args[i] = (try ctx.zigValueToJs(@field(aargs, f.name), .{})).handle; } - const cargs: [fields.len]*const v8.c.Value = js_args; + const cargs: [fields.len]*const v8.Value = js_args; break :blk &cargs; }, .pointer => blk: { - var values = try ctx.call_arena.alloc(*const v8.c.Value, args.len); + var values = try ctx.call_arena.alloc(*const v8.Value, args.len); for (args, 0..) |a, i| { values[i] = (try ctx.zigValueToJs(a, .{})).handle; } @@ -149,8 +149,8 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args else => @compileError("JS Function called with invalid paremter type"), }; - const c_args = @as(?[*]const ?*v8.c.Value, @ptrCast(js_args.ptr)); - const handle = v8.c.v8__Function__Call(self.handle, ctx.handle, js_this.handle, @as(c_int, @intCast(js_args.len)), c_args) orelse { + const c_args = @as(?[*]const ?*v8.Value, @ptrCast(js_args.ptr)); + const handle = v8.v8__Function__Call(self.handle, ctx.handle, js_this.handle, @as(c_int, @intCast(js_args.len)), c_args) orelse { // std.debug.print("CB ERR: {s}\n", .{self.src() catch "???"}); return error.JSExecCallback; }; @@ -162,7 +162,7 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args } fn getThis(self: *const Function) js.Object { - const handle = if (self.this) |t| t else v8.c.v8__Context__Global(self.ctx.handle).?; + const handle = if (self.this) |t| t else v8.v8__Context__Global(self.ctx.handle).?; return .{ .ctx = self.ctx, .handle = handle, @@ -176,7 +176,7 @@ pub fn src(self: *const Function) ![]const u8 { pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value { const ctx = self.ctx; const key = ctx.isolate.initStringHandle(name); - const handle = v8.c.v8__Object__Get(self.handle, ctx.handle, key) orelse { + const handle = v8.v8__Object__Get(self.handle, ctx.handle, key) orelse { return error.JsException; }; diff --git a/src/browser/js/HandleScope.zig b/src/browser/js/HandleScope.zig index 4c47d805..37f7e549 100644 --- a/src/browser/js/HandleScope.zig +++ b/src/browser/js/HandleScope.zig @@ -21,12 +21,12 @@ const v8 = js.v8; const HandleScope = @This(); -handle: v8.c.HandleScope, +handle: v8.HandleScope, pub fn init(self: *HandleScope, isolate: js.Isolate) void { - v8.c.v8__HandleScope__CONSTRUCT(&self.handle, isolate.handle); + v8.v8__HandleScope__CONSTRUCT(&self.handle, isolate.handle); } pub fn deinit(self: *HandleScope) void { - v8.c.v8__HandleScope__DESTRUCT(&self.handle); + v8.v8__HandleScope__DESTRUCT(&self.handle); } diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index 83bd7822..71d54b87 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -30,17 +30,17 @@ const CLIENT_TRUST_LEVEL = 1; const Inspector = @This(); -handle: *v8.c.Inspector, -isolate: *v8.c.Isolate, +handle: *v8.Inspector, +isolate: *v8.Isolate, client: Client, channel: Channel, session: Session, rnd: RndGen = RndGen.init(0), -default_context: ?*const v8.c.Context = null, +default_context: ?*const v8.Context = null, // We expect allocator to be an arena // Note: This initializes the pre-allocated inspector in-place -pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { +pub fn init(self: *Inspector, isolate: *v8.Isolate, ctx: anytype) !void { const ContextT = @TypeOf(ctx); const Container = switch (@typeInfo(ContextT)) { @@ -70,7 +70,7 @@ pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { client.setInspector(self); // Now create the inspector - generateUniqueId will work because data is set - const handle = v8.c.v8_inspector__Inspector__Create(isolate, client.handle).?; + const handle = v8.v8_inspector__Inspector__Create(isolate, client.handle).?; self.handle = handle; // Create the channel @@ -86,7 +86,7 @@ pub fn init(self: *Inspector, isolate: *v8.c.Isolate, ctx: anytype) !void { channel.setInspector(self); // Create the session - const session_handle = v8.c.v8_inspector__Inspector__Connect( + const session_handle = v8.v8_inspector__Inspector__Connect( handle, CONTEXT_GROUP_ID, channel.handle, @@ -103,7 +103,7 @@ pub fn deinit(self: *const Inspector) void { self.session.deinit(); self.client.deinit(); self.channel.deinit(); - v8.c.v8_inspector__Inspector__DELETE(self.handle); + v8.v8_inspector__Inspector__DELETE(self.handle); } pub fn send(self: *const Inspector, msg: []const u8) void { @@ -111,9 +111,9 @@ pub fn send(self: *const Inspector, msg: []const u8) void { // available when doing this. Pages (and thus the HandleScope) // comes and goes, but CDP can keep sending messages. const isolate = self.isolate; - var temp_scope: v8.c.HandleScope = undefined; - v8.c.v8__HandleScope__CONSTRUCT(&temp_scope, isolate); - defer v8.c.v8__HandleScope__DESTRUCT(&temp_scope); + var temp_scope: v8.HandleScope = undefined; + v8.v8__HandleScope__CONSTRUCT(&temp_scope, isolate); + defer v8.v8__HandleScope__DESTRUCT(&temp_scope); self.session.dispatchProtocolMessage(isolate, msg); } @@ -134,7 +134,7 @@ pub fn contextCreated( aux_data: []const u8, is_default_context: bool, ) void { - v8.c.v8_inspector__Inspector__ContextCreated( + v8.v8_inspector__Inspector__ContextCreated( self.handle, name.ptr, name.len, @@ -185,71 +185,71 @@ pub fn getNodePtr(self: *const Inspector, allocator: Allocator, object_id: []con const unwrapped = try self.session.unwrapObject(allocator, object_id); // The values context and groupId are not used here const js_val = unwrapped.value; - if (!v8.c.v8__Value__IsObject(js_val)) { + if (!v8.v8__Value__IsObject(js_val)) { return error.ObjectIdIsNotANode; } const Node = @import("../webapi/Node.zig"); - // Cast to *const v8.c.Object for typeTaggedAnyOpaque + // Cast to *const v8.Object for typeTaggedAnyOpaque return Context.typeTaggedAnyOpaque(*Node, @ptrCast(js_val)) catch { return error.ObjectIdIsNotANode; }; } pub const RemoteObject = struct { - handle: *v8.c.RemoteObject, + handle: *v8.RemoteObject, pub fn deinit(self: RemoteObject) void { - v8.c.v8_inspector__RemoteObject__DELETE(self.handle); + v8.v8_inspector__RemoteObject__DELETE(self.handle); } pub fn getType(self: RemoteObject, allocator: Allocator) ![]const u8 { - var ctype_: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - if (!v8.c.v8_inspector__RemoteObject__getType(self.handle, &allocator, &ctype_)) return error.V8AllocFailed; + var ctype_: v8.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.v8_inspector__RemoteObject__getType(self.handle, &allocator, &ctype_)) return error.V8AllocFailed; return cZigStringToString(ctype_) orelse return error.InvalidType; } pub fn getSubtype(self: RemoteObject, allocator: Allocator) !?[]const u8 { - if (!v8.c.v8_inspector__RemoteObject__hasSubtype(self.handle)) return null; + if (!v8.v8_inspector__RemoteObject__hasSubtype(self.handle)) return null; - var csubtype: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - if (!v8.c.v8_inspector__RemoteObject__getSubtype(self.handle, &allocator, &csubtype)) return error.V8AllocFailed; + var csubtype: v8.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.v8_inspector__RemoteObject__getSubtype(self.handle, &allocator, &csubtype)) return error.V8AllocFailed; return cZigStringToString(csubtype); } pub fn getClassName(self: RemoteObject, allocator: Allocator) !?[]const u8 { - if (!v8.c.v8_inspector__RemoteObject__hasClassName(self.handle)) return null; + if (!v8.v8_inspector__RemoteObject__hasClassName(self.handle)) return null; - var cclass_name: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - if (!v8.c.v8_inspector__RemoteObject__getClassName(self.handle, &allocator, &cclass_name)) return error.V8AllocFailed; + var cclass_name: v8.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.v8_inspector__RemoteObject__getClassName(self.handle, &allocator, &cclass_name)) return error.V8AllocFailed; return cZigStringToString(cclass_name); } pub fn getDescription(self: RemoteObject, allocator: Allocator) !?[]const u8 { - if (!v8.c.v8_inspector__RemoteObject__hasDescription(self.handle)) return null; + if (!v8.v8_inspector__RemoteObject__hasDescription(self.handle)) return null; - var description: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - if (!v8.c.v8_inspector__RemoteObject__getDescription(self.handle, &allocator, &description)) return error.V8AllocFailed; + var description: v8.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.v8_inspector__RemoteObject__getDescription(self.handle, &allocator, &description)) return error.V8AllocFailed; return cZigStringToString(description); } pub fn getObjectId(self: RemoteObject, allocator: Allocator) !?[]const u8 { - if (!v8.c.v8_inspector__RemoteObject__hasObjectId(self.handle)) return null; + if (!v8.v8_inspector__RemoteObject__hasObjectId(self.handle)) return null; - var cobject_id: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - if (!v8.c.v8_inspector__RemoteObject__getObjectId(self.handle, &allocator, &cobject_id)) return error.V8AllocFailed; + var cobject_id: v8.CZigString = .{ .ptr = null, .len = 0 }; + if (!v8.v8_inspector__RemoteObject__getObjectId(self.handle, &allocator, &cobject_id)) return error.V8AllocFailed; return cZigStringToString(cobject_id); } }; const Session = struct { - handle: *v8.c.InspectorSession, + handle: *v8.InspectorSession, fn deinit(self: Session) void { - v8.c.v8_inspector__Session__DELETE(self.handle); + v8.v8_inspector__Session__DELETE(self.handle); } - fn dispatchProtocolMessage(self: Session, isolate: *v8.c.Isolate, msg: []const u8) void { - v8.c.v8_inspector__Session__dispatchProtocolMessage( + fn dispatchProtocolMessage(self: Session, isolate: *v8.Isolate, msg: []const u8) void { + v8.v8_inspector__Session__dispatchProtocolMessage( self.handle, isolate, msg.ptr, @@ -259,13 +259,13 @@ const Session = struct { fn wrapObject( self: Session, - isolate: *v8.c.Isolate, - ctx: *const v8.c.Context, - val: *const v8.c.Value, + isolate: *v8.Isolate, + ctx: *const v8.Context, + val: *const v8.Value, grpname: []const u8, generatepreview: bool, ) !RemoteObject { - const remote_object = v8.c.v8_inspector__Session__wrapObject( + const remote_object = v8.v8_inspector__Session__wrapObject( self.handle, isolate, ctx, @@ -282,16 +282,16 @@ const Session = struct { allocator: Allocator, object_id: []const u8, ) !UnwrappedObject { - const in_object_id = v8.c.CZigString{ + const in_object_id = v8.CZigString{ .ptr = object_id.ptr, .len = object_id.len, }; - var out_error: v8.c.CZigString = .{ .ptr = null, .len = 0 }; - var out_value_handle: ?*v8.c.Value = null; - var out_context_handle: ?*v8.c.Context = null; - var out_object_group: v8.c.CZigString = .{ .ptr = null, .len = 0 }; + var out_error: v8.CZigString = .{ .ptr = null, .len = 0 }; + var out_value_handle: ?*v8.Value = null; + var out_context_handle: ?*v8.Context = null; + var out_object_group: v8.CZigString = .{ .ptr = null, .len = 0 }; - const result = v8.c.v8_inspector__Session__unwrapObject( + const result = v8.v8_inspector__Session__unwrapObject( self.handle, &allocator, &out_error, @@ -316,13 +316,13 @@ const Session = struct { }; const UnwrappedObject = struct { - value: *const v8.c.Value, - context: *const v8.c.Context, + value: *const v8.Value, + context: *const v8.Context, object_group: ?[]const u8, }; const Channel = struct { - handle: *v8.c.InspectorChannelImpl, + handle: *v8.InspectorChannelImpl, // callbacks ctx: *anyopaque, @@ -342,9 +342,9 @@ const Channel = struct { onNotif: onNotifFn, onRunMessageLoopOnPause: onRunMessageLoopOnPauseFn, onQuitMessageLoopOnPause: onQuitMessageLoopOnPauseFn, - isolate: *v8.c.Isolate, + isolate: *v8.Isolate, ) Channel { - const handle = v8.c.v8_inspector__Channel__IMPL__CREATE(isolate); + const handle = v8.v8_inspector__Channel__IMPL__CREATE(isolate); return .{ .handle = handle, .ctx = ctx, @@ -356,11 +356,11 @@ const Channel = struct { } fn deinit(self: Channel) void { - v8.c.v8_inspector__Channel__IMPL__DELETE(self.handle); + v8.v8_inspector__Channel__IMPL__DELETE(self.handle); } fn setInspector(self: Channel, inspector: *anyopaque) void { - v8.c.v8_inspector__Channel__IMPL__SET_DATA(self.handle, inspector); + v8.v8_inspector__Channel__IMPL__SET_DATA(self.handle, inspector); } fn resp(self: Channel, call_id: u32, msg: []const u8) void { @@ -373,18 +373,18 @@ const Channel = struct { }; const Client = struct { - handle: *v8.c.InspectorClientImpl, + handle: *v8.InspectorClientImpl, fn init() Client { - return .{ .handle = v8.c.v8_inspector__Client__IMPL__CREATE() }; + return .{ .handle = v8.v8_inspector__Client__IMPL__CREATE() }; } fn deinit(self: Client) void { - v8.c.v8_inspector__Client__IMPL__DELETE(self.handle); + v8.v8_inspector__Client__IMPL__DELETE(self.handle); } fn setInspector(self: Client, inspector: *anyopaque) void { - v8.c.v8_inspector__Client__IMPL__SET_DATA(self.handle, inspector); + v8.v8_inspector__Client__IMPL__SET_DATA(self.handle, inspector); } }; @@ -399,28 +399,28 @@ fn fromData(data: *anyopaque) *Inspector { return @ptrCast(@alignCast(data)); } -pub fn getTaggedAnyOpaque(value: *const v8.c.Value) ?*js.TaggedAnyOpaque { - if (!v8.c.v8__Value__IsObject(value)) { +pub fn getTaggedAnyOpaque(value: *const v8.Value) ?*js.TaggedAnyOpaque { + if (!v8.v8__Value__IsObject(value)) { return null; } - const internal_field_count = v8.c.v8__Object__InternalFieldCount(value); + const internal_field_count = v8.v8__Object__InternalFieldCount(value); if (internal_field_count == 0) { return null; } - const external_value = v8.c.v8__Object__GetInternalField(value, 0).?; - const external_data = v8.c.v8__External__Value(external_value).?; + const external_value = v8.v8__Object__GetInternalField(value, 0).?; + const external_data = v8.v8__External__Value(external_value).?; return @ptrCast(@alignCast(external_data)); } -fn cZigStringToString(s: v8.c.CZigString) ?[]const u8 { +fn cZigStringToString(s: v8.CZigString) ?[]const u8 { if (s.ptr == null) return null; return s.ptr[0..s.len]; } // C export functions for Inspector callbacks pub export fn v8_inspector__Client__IMPL__generateUniqueId( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, data: *anyopaque, ) callconv(.c) i64 { const inspector: *Inspector = @ptrCast(@alignCast(data)); @@ -428,7 +428,7 @@ pub export fn v8_inspector__Client__IMPL__generateUniqueId( } pub export fn v8_inspector__Client__IMPL__runMessageLoopOnPause( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, data: *anyopaque, ctx_group_id: c_int, ) callconv(.c) void { @@ -437,7 +437,7 @@ pub export fn v8_inspector__Client__IMPL__runMessageLoopOnPause( } pub export fn v8_inspector__Client__IMPL__quitMessageLoopOnPause( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, data: *anyopaque, ) callconv(.c) void { const inspector: *Inspector = @ptrCast(@alignCast(data)); @@ -445,7 +445,7 @@ pub export fn v8_inspector__Client__IMPL__quitMessageLoopOnPause( } pub export fn v8_inspector__Client__IMPL__runIfWaitingForDebugger( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, _: *anyopaque, _: c_int, ) callconv(.c) void { @@ -453,27 +453,27 @@ pub export fn v8_inspector__Client__IMPL__runIfWaitingForDebugger( } pub export fn v8_inspector__Client__IMPL__consoleAPIMessage( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, _: *anyopaque, _: c_int, - _: v8.c.MessageErrorLevel, - _: *v8.c.StringView, - _: *v8.c.StringView, + _: v8.MessageErrorLevel, + _: *v8.StringView, + _: *v8.StringView, _: c_uint, _: c_uint, - _: *v8.c.StackTrace, + _: *v8.StackTrace, ) callconv(.c) void {} pub export fn v8_inspector__Client__IMPL__ensureDefaultContextInGroup( - _: *v8.c.InspectorClientImpl, + _: *v8.InspectorClientImpl, data: *anyopaque, -) callconv(.c) ?*const v8.c.Context { +) callconv(.c) ?*const v8.Context { const inspector: *Inspector = @ptrCast(@alignCast(data)); return inspector.default_context; } pub export fn v8_inspector__Channel__IMPL__sendResponse( - _: *v8.c.InspectorChannelImpl, + _: *v8.InspectorChannelImpl, data: *anyopaque, call_id: c_int, msg: [*c]u8, @@ -484,7 +484,7 @@ pub export fn v8_inspector__Channel__IMPL__sendResponse( } pub export fn v8_inspector__Channel__IMPL__sendNotification( - _: *v8.c.InspectorChannelImpl, + _: *v8.InspectorChannelImpl, data: *anyopaque, msg: [*c]u8, length: usize, @@ -494,7 +494,7 @@ pub export fn v8_inspector__Channel__IMPL__sendNotification( } pub export fn v8_inspector__Channel__IMPL__flushProtocolNotifications( - _: *v8.c.InspectorChannelImpl, + _: *v8.InspectorChannelImpl, _: *anyopaque, ) callconv(.c) void { // TODO diff --git a/src/browser/js/Integer.zig b/src/browser/js/Integer.zig index 5d7cef5b..e0383fbf 100644 --- a/src/browser/js/Integer.zig +++ b/src/browser/js/Integer.zig @@ -23,12 +23,12 @@ const v8 = js.v8; const Integer = @This(); -handle: *const v8.c.Integer, +handle: *const v8.Integer, -pub fn init(isolate: *v8.c.Isolate, value: anytype) Integer { +pub fn init(isolate: *v8.Isolate, value: anytype) Integer { const handle = switch (@TypeOf(value)) { - i8, i16, i32 => v8.c.v8__Integer__New(isolate, value).?, - u8, u16, u32 => v8.c.v8__Integer__NewFromUnsigned(isolate, value).?, + i8, i16, i32 => v8.v8__Integer__New(isolate, value).?, + u8, u16, u32 => v8.v8__Integer__NewFromUnsigned(isolate, value).?, else => |T| @compileError("cannot create v8::Integer from: " ++ @typeName(T)), }; return .{ .handle = handle }; diff --git a/src/browser/js/Isolate.zig b/src/browser/js/Isolate.zig index c142ad91..9b4ae1d1 100644 --- a/src/browser/js/Isolate.zig +++ b/src/browser/js/Isolate.zig @@ -21,80 +21,80 @@ const v8 = js.v8; const Isolate = @This(); -handle: *v8.c.Isolate, +handle: *v8.Isolate, -pub fn init(params: *v8.c.CreateParams) Isolate { +pub fn init(params: *v8.CreateParams) Isolate { return .{ - .handle = v8.c.v8__Isolate__New(params).?, + .handle = v8.v8__Isolate__New(params).?, }; } pub fn deinit(self: Isolate) void { - v8.c.v8__Isolate__Dispose(self.handle); + v8.v8__Isolate__Dispose(self.handle); } pub fn enter(self: Isolate) void { - v8.c.v8__Isolate__Enter(self.handle); + v8.v8__Isolate__Enter(self.handle); } pub fn exit(self: Isolate) void { - v8.c.v8__Isolate__Exit(self.handle); + v8.v8__Isolate__Exit(self.handle); } pub fn performMicrotasksCheckpoint(self: Isolate) void { - v8.c.v8__Isolate__PerformMicrotaskCheckpoint(self.handle); + v8.v8__Isolate__PerformMicrotaskCheckpoint(self.handle); } pub fn enqueueMicrotask(self: Isolate, callback: anytype, data: anytype) void { - v8.c.v8__Isolate__EnqueueMicrotask(self.handle, callback, data); + v8.v8__Isolate__EnqueueMicrotask(self.handle, callback, data); } pub fn enqueueMicrotaskFunc(self: Isolate, function: js.Function) void { - v8.c.v8__Isolate__EnqueueMicrotaskFunc(self.handle, function.handle); + v8.v8__Isolate__EnqueueMicrotaskFunc(self.handle, function.handle); } pub fn lowMemoryNotification(self: Isolate) void { - v8.c.v8__Isolate__LowMemoryNotification(self.handle); + v8.v8__Isolate__LowMemoryNotification(self.handle); } -pub fn getHeapStatistics(self: Isolate) v8.c.HeapStatistics { - var res: v8.c.HeapStatistics = undefined; - v8.c.v8__Isolate__GetHeapStatistics(self.handle, &res); +pub fn getHeapStatistics(self: Isolate) v8.HeapStatistics { + var res: v8.HeapStatistics = undefined; + v8.v8__Isolate__GetHeapStatistics(self.handle, &res); return res; } -pub fn throwException(self: Isolate, value: *const v8.c.Value) *const v8.c.Value { - return v8.c.v8__Isolate__ThrowException(self.handle, value).?; +pub fn throwException(self: Isolate, value: *const v8.Value) *const v8.Value { + return v8.v8__Isolate__ThrowException(self.handle, value).?; } -pub fn initStringHandle(self: Isolate, str: []const u8) *const v8.c.String { - return v8.c.v8__String__NewFromUtf8(self.handle, str.ptr, v8.c.kNormal, @as(c_int, @intCast(str.len))).?; +pub fn initStringHandle(self: Isolate, str: []const u8) *const v8.String { + return v8.v8__String__NewFromUtf8(self.handle, str.ptr, v8.kNormal, @as(c_int, @intCast(str.len))).?; } -pub fn createError(self: Isolate, msg: []const u8) *const v8.c.Value { +pub fn createError(self: Isolate, msg: []const u8) *const v8.Value { const message = self.initStringHandle(msg); - return v8.c.v8__Exception__Error(message).?; + return v8.v8__Exception__Error(message).?; } -pub fn createTypeError(self: Isolate, msg: []const u8) *const v8.c.Value { +pub fn createTypeError(self: Isolate, msg: []const u8) *const v8.Value { const message = self.initStringHandle(msg); - return v8.c.v8__Exception__TypeError(message).?; + return v8.v8__Exception__TypeError(message).?; } -pub fn initNull(self: Isolate) *const v8.c.Value { - return v8.c.v8__Null(self.handle).?; +pub fn initNull(self: Isolate) *const v8.Value { + return v8.v8__Null(self.handle).?; } -pub fn initUndefined(self: Isolate) *const v8.c.Value { - return v8.c.v8__Undefined(self.handle).?; +pub fn initUndefined(self: Isolate) *const v8.Value { + return v8.v8__Undefined(self.handle).?; } -pub fn initFalse(self: Isolate) *const v8.c.Value { - return v8.c.v8__False(self.handle).?; +pub fn initFalse(self: Isolate) *const v8.Value { + return v8.v8__False(self.handle).?; } -pub fn initTrue(self: Isolate) *const v8.c.Value { - return v8.c.v8__True(self.handle).?; +pub fn initTrue(self: Isolate) *const v8.Value { + return v8.v8__True(self.handle).?; } pub fn initInteger(self: Isolate, val: anytype) js.Integer { @@ -109,14 +109,14 @@ pub fn initNumber(self: Isolate, val: anytype) js.Number { return js.Number.init(self.handle, val); } -pub fn createContextHandle(self: Isolate, global_tmpl: ?*const v8.c.ObjectTemplate, global_obj: ?*const v8.c.Value) *const v8.c.Context { - return v8.c.v8__Context__New(self.handle, global_tmpl, global_obj).?; +pub fn createContextHandle(self: Isolate, global_tmpl: ?*const v8.ObjectTemplate, global_obj: ?*const v8.Value) *const v8.Context { + return v8.v8__Context__New(self.handle, global_tmpl, global_obj).?; } -pub fn createFunctionTemplateHandle(self: Isolate) *const v8.c.FunctionTemplate { - return v8.c.v8__FunctionTemplate__New__DEFAULT(self.handle).?; +pub fn createFunctionTemplateHandle(self: Isolate) *const v8.FunctionTemplate { + return v8.v8__FunctionTemplate__New__DEFAULT(self.handle).?; } -pub fn createExternal(self: Isolate, val: *anyopaque) *const v8.c.External { - return v8.c.v8__External__New(self.handle, val).?; +pub fn createExternal(self: Isolate, val: *anyopaque) *const v8.External { + return v8.v8__External__New(self.handle, val).?; } diff --git a/src/browser/js/Module.zig b/src/browser/js/Module.zig index 571def25..ef8dec57 100644 --- a/src/browser/js/Module.zig +++ b/src/browser/js/Module.zig @@ -22,38 +22,38 @@ const v8 = js.v8; const Module = @This(); ctx: *js.Context, -handle: *const v8.c.Module, +handle: *const v8.Module, pub const Status = enum(u32) { - kUninstantiated = v8.c.kUninstantiated, - kInstantiating = v8.c.kInstantiating, - kInstantiated = v8.c.kInstantiated, - kEvaluating = v8.c.kEvaluating, - kEvaluated = v8.c.kEvaluated, - kErrored = v8.c.kErrored, + kUninstantiated = v8.kUninstantiated, + kInstantiating = v8.kInstantiating, + kInstantiated = v8.kInstantiated, + kEvaluating = v8.kEvaluating, + kEvaluated = v8.kEvaluated, + kErrored = v8.kErrored, }; pub fn getStatus(self: Module) Status { - return @enumFromInt(v8.c.v8__Module__GetStatus(self.handle)); + return @enumFromInt(v8.v8__Module__GetStatus(self.handle)); } pub fn getException(self: Module) js.Value { return .{ .ctx = self.ctx, - .handle = v8.c.v8__Module__GetException(self.handle).?, + .handle = v8.v8__Module__GetException(self.handle).?, }; } pub fn getModuleRequests(self: Module) Requests { return .{ .ctx = self.ctx.handle, - .handle = v8.c.v8__Module__GetModuleRequests(self.handle).?, + .handle = v8.v8__Module__GetModuleRequests(self.handle).?, }; } -pub fn instantiate(self: Module, cb: v8.c.ResolveModuleCallback) !bool { - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Module__InstantiateModule(self.handle, self.ctx.handle, cb, &out); +pub fn instantiate(self: Module, cb: v8.ResolveModuleCallback) !bool { + var out: v8.MaybeBool = undefined; + v8.v8__Module__InstantiateModule(self.handle, self.ctx.handle, cb, &out); if (out.has_value) { return out.value; } @@ -62,7 +62,7 @@ pub fn instantiate(self: Module, cb: v8.c.ResolveModuleCallback) !bool { pub fn evaluate(self: Module) !js.Value { const ctx = self.ctx; - const res = v8.c.v8__Module__Evaluate(self.handle, ctx.handle) orelse return error.JsException; + const res = v8.v8__Module__Evaluate(self.handle, ctx.handle) orelse return error.JsException; if (self.getStatus() == .kErrored) { return error.JsException; @@ -75,18 +75,18 @@ pub fn evaluate(self: Module) !js.Value { } pub fn getIdentityHash(self: Module) u32 { - return @bitCast(v8.c.v8__Module__GetIdentityHash(self.handle)); + return @bitCast(v8.v8__Module__GetIdentityHash(self.handle)); } pub fn getModuleNamespace(self: Module) js.Value { return .{ .ctx = self.ctx, - .handle = v8.c.v8__Module__GetModuleNamespace(self.handle).?, + .handle = v8.v8__Module__GetModuleNamespace(self.handle).?, }; } pub fn getScriptId(self: Module) u32 { - return @intCast(v8.c.v8__Module__ScriptId(self.handle)); + return @intCast(v8.v8__Module__ScriptId(self.handle)); } pub fn persist(self: Module) !Module { @@ -102,22 +102,22 @@ pub fn persist(self: Module) !Module { } const Requests = struct { - ctx: *const v8.c.Context, - handle: *const v8.c.FixedArray, + ctx: *const v8.Context, + handle: *const v8.FixedArray, pub fn len(self: Requests) usize { - return @intCast(v8.c.v8__FixedArray__Length(self.handle)); + return @intCast(v8.v8__FixedArray__Length(self.handle)); } pub fn get(self: Requests, idx: usize) Request { - return .{ .handle = v8.c.v8__FixedArray__Get(self.handle, self.ctx, @intCast(idx)).? }; + return .{ .handle = v8.v8__FixedArray__Get(self.handle, self.ctx, @intCast(idx)).? }; } }; const Request = struct { - handle: *const v8.c.ModuleRequest, + handle: *const v8.ModuleRequest, - pub fn specifier(self: Request) *const v8.c.String { - return v8.c.v8__ModuleRequest__GetSpecifier(self.handle).?; + pub fn specifier(self: Request) *const v8.String { + return v8.v8__ModuleRequest__GetSpecifier(self.handle).?; } }; diff --git a/src/browser/js/Name.zig b/src/browser/js/Name.zig index 17b04173..90def5cb 100644 --- a/src/browser/js/Name.zig +++ b/src/browser/js/Name.zig @@ -21,7 +21,7 @@ const v8 = js.v8; const Name = @This(); -handle: *const v8.c.Name, +handle: *const v8.Name, pub fn toValue(self: Name) js.Value { return .{ diff --git a/src/browser/js/Number.zig b/src/browser/js/Number.zig index 0bba90ea..1676632b 100644 --- a/src/browser/js/Number.zig +++ b/src/browser/js/Number.zig @@ -23,9 +23,9 @@ const v8 = js.v8; const Number = @This(); -handle: *const v8.c.Number, +handle: *const v8.Number, -pub fn init(isolate: *v8.c.Isolate, value: anytype) Number { - const handle = v8.c.v8__Number__New(isolate, value).?; +pub fn init(isolate: *v8.Isolate, value: anytype) Number { + const handle = v8.v8__Number__New(isolate, value).?; return .{ .handle = handle }; } diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index e68cb445..082f135f 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -30,18 +30,18 @@ const Allocator = std.mem.Allocator; const Object = @This(); ctx: *js.Context, -handle: *const v8.c.Object, +handle: *const v8.Object, pub fn getId(self: Object) u32 { - return @bitCast(v8.c.v8__Object__GetIdentityHash(self.handle)); + return @bitCast(v8.v8__Object__GetIdentityHash(self.handle)); } pub fn has(self: Object, key: anytype) bool { const ctx = self.ctx; - const key_handle = if (@TypeOf(key) == *const v8.c.String) key else ctx.isolate.initStringHandle(key); + const key_handle = if (@TypeOf(key) == *const v8.String) key else ctx.isolate.initStringHandle(key); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Has(self.handle, self.ctx.handle, key_handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Object__Has(self.handle, self.ctx.handle, key_handle, &out); if (out.has_value) { return out.value; } @@ -51,8 +51,8 @@ pub fn has(self: Object, key: anytype) bool { pub fn get(self: Object, key: anytype) !js.Value { const ctx = self.ctx; - const key_handle = if (@TypeOf(key) == *const v8.c.String) key else ctx.isolate.initStringHandle(key); - const js_val_handle = v8.c.v8__Object__Get(self.handle, ctx.handle, key_handle) orelse return error.JsException; + const key_handle = if (@TypeOf(key) == *const v8.String) key else ctx.isolate.initStringHandle(key); + const js_val_handle = v8.v8__Object__Get(self.handle, ctx.handle, key_handle) orelse return error.JsException; return .{ .ctx = ctx, @@ -64,10 +64,10 @@ pub fn set(self: Object, key: anytype, value: anytype, comptime opts: js.bridge. const ctx = self.ctx; const js_value = try ctx.zigValueToJs(value, opts); - const key_handle = if (@TypeOf(key) == *const v8.c.String) key else ctx.isolate.initStringHandle(key); + const key_handle = if (@TypeOf(key) == *const v8.String) key else ctx.isolate.initStringHandle(key); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Set(self.handle, ctx.handle, key_handle, js_value.handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Object__Set(self.handle, ctx.handle, key_handle, js_value.handle, &out); return out.has_value; } @@ -76,16 +76,18 @@ pub fn setIndex(self: Object, key: u32, value: anytype, comptime opts: js.bridge const js_value = try ctx.zigValueToJs(value, opts); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__SetAtIndex(self.handle, ctx.handle, key, js_value.handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Object__SetAtIndex(self.handle, ctx.handle, key, js_value.handle, &out); return out.has_value; } -pub fn defineOwnProperty(self: Object, name: []const u8, value: js.Value, attr: v8.c.PropertyAttribute) ?bool { +pub fn defineOwnProperty(self: Object, name: []const u8, value: js.Value, attr: v8.PropertyAttribute) ?bool { const ctx = self.ctx; const name_handle = ctx.isolate.initStringHandle(name); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__DefineOwnProperty(self.handle, ctx.handle, @ptrCast(name_handle), value.handle, attr, &out); + + var out: v8.MaybeBool = undefined; + v8.v8__Object__DefineOwnProperty(self.handle, ctx.handle, @ptrCast(name_handle), value.handle, attr, &out); + if (out.has_value) { return out.value; } else { @@ -113,7 +115,7 @@ pub fn format(self: Object, writer: *std.Io.Writer) !void { } pub fn toJson(self: Object, allocator: Allocator) ![]u8 { - const json_str_handle = v8.c.v8__JSON__Stringify(self.ctx.handle, @ptrCast(self.handle), null) orelse return error.JsException; + const json_str_handle = v8.v8__JSON__Stringify(self.ctx.handle, @ptrCast(self.handle), null) orelse return error.JsException; return self.ctx.jsStringToZig(json_str_handle, .{ .allocator = allocator }); } @@ -136,9 +138,9 @@ pub fn getFunction(self: Object, name: []const u8) !?js.Function { const ctx = self.ctx; const js_name = ctx.isolate.initStringHandle(name); - const js_val_handle = v8.c.v8__Object__Get(self.handle, ctx.handle, js_name) orelse return error.JsException; + const js_val_handle = v8.v8__Object__Get(self.handle, ctx.handle, js_name) orelse return error.JsException; - if (v8.c.v8__Value__IsFunction(js_val_handle) == false) { + if (v8.v8__Value__IsFunction(js_val_handle) == false) { return null; } return .{ @@ -153,11 +155,11 @@ pub fn callMethod(self: Object, comptime T: type, method_name: []const u8, args: } pub fn isNullOrUndefined(self: Object) bool { - return v8.c.v8__Value__IsNullOrUndefined(@ptrCast(self.handle)); + return v8.v8__Value__IsNullOrUndefined(@ptrCast(self.handle)); } pub fn getOwnPropertyNames(self: Object) js.Array { - const handle = v8.c.v8__Object__GetOwnPropertyNames(self.handle, self.ctx.handle).?; + const handle = v8.v8__Object__GetOwnPropertyNames(self.handle, self.ctx.handle).?; return .{ .ctx = self.ctx, .handle = handle, @@ -165,7 +167,7 @@ pub fn getOwnPropertyNames(self: Object) js.Array { } pub fn getPropertyNames(self: Object) js.Array { - const handle = v8.c.v8__Object__GetPropertyNames(self.handle, self.ctx.handle).?; + const handle = v8.v8__Object__GetPropertyNames(self.handle, self.ctx.handle).?; return .{ .ctx = self.ctx, .handle = handle, @@ -175,8 +177,8 @@ pub fn getPropertyNames(self: Object) js.Array { pub fn nameIterator(self: Object) NameIterator { const ctx = self.ctx; - const handle = v8.c.v8__Object__GetPropertyNames(self.handle, ctx.handle).?; - const count = v8.c.v8__Array__Length(handle); + const handle = v8.v8__Object__GetPropertyNames(self.handle, ctx.handle).?; + const count = v8.v8__Array__Length(handle); return .{ .ctx = ctx, @@ -194,7 +196,7 @@ pub const NameIterator = struct { count: u32, idx: u32 = 0, ctx: *Context, - handle: *const v8.c.Array, + handle: *const v8.Array, pub fn next(self: *NameIterator) !?[]const u8 { const idx = self.idx; @@ -203,7 +205,7 @@ pub const NameIterator = struct { } self.idx += 1; - const js_val_handle = v8.c.v8__Object__GetIndex(@ptrCast(self.handle), self.ctx.handle, idx) orelse return error.JsException; + const js_val_handle = v8.v8__Object__GetIndex(@ptrCast(self.handle), self.ctx.handle, idx) orelse return error.JsException; const js_val = js.Value{ .ctx = self.ctx, .handle = js_val_handle }; return try self.ctx.valueToString(js_val, .{}); } diff --git a/src/browser/js/Platform.zig b/src/browser/js/Platform.zig index 6390abad..0a173331 100644 --- a/src/browser/js/Platform.zig +++ b/src/browser/js/Platform.zig @@ -20,22 +20,22 @@ const js = @import("js.zig"); const v8 = js.v8; const Platform = @This(); -handle: *v8.c.Platform, +handle: *v8.Platform, pub fn init() !Platform { - if (v8.c.v8__V8__InitializeICU() == false) { + if (v8.v8__V8__InitializeICU() == false) { return error.FailedToInitializeICU; } // 0 - threadpool size, 0 == let v8 decide // 1 - idle_task_support, 1 == enabled - const handle = v8.c.v8__Platform__NewDefaultPlatform(0, 1).?; - v8.c.v8__V8__InitializePlatform(handle); - v8.c.v8__V8__Initialize(); + const handle = v8.v8__Platform__NewDefaultPlatform(0, 1).?; + v8.v8__V8__InitializePlatform(handle); + v8.v8__V8__Initialize(); return .{ .handle = handle }; } pub fn deinit(self: Platform) void { - _ = v8.c.v8__V8__Dispose(); - v8.c.v8__V8__DisposePlatform(); - v8.c.v8__Platform__DELETE(self.handle); + _ = v8.v8__V8__Dispose(); + v8.v8__V8__DisposePlatform(); + v8.v8__Platform__DELETE(self.handle); } diff --git a/src/browser/js/Promise.zig b/src/browser/js/Promise.zig index 6cbac804..f520a9d6 100644 --- a/src/browser/js/Promise.zig +++ b/src/browser/js/Promise.zig @@ -22,7 +22,7 @@ const v8 = js.v8; const Promise = @This(); ctx: *js.Context, -handle: *const v8.c.Promise, +handle: *const v8.Promise, pub fn toObject(self: Promise) js.Object { return .{ @@ -39,7 +39,7 @@ pub fn toValue(self: Promise) js.Value { } pub fn thenAndCatch(self: Promise, on_fulfilled: js.Function, on_rejected: js.Function) !Promise { - if (v8.c.v8__Promise__Then2(self.handle, self.ctx.handle, on_fulfilled.handle, on_rejected.handle)) |handle| { + if (v8.v8__Promise__Then2(self.handle, self.ctx.handle, on_fulfilled.handle, on_rejected.handle)) |handle| { return .{ .ctx = self.ctx, .handle = handle, diff --git a/src/browser/js/PromiseResolver.zig b/src/browser/js/PromiseResolver.zig index 117d0978..a12d3a26 100644 --- a/src/browser/js/PromiseResolver.zig +++ b/src/browser/js/PromiseResolver.zig @@ -23,19 +23,19 @@ const log = @import("../../log.zig"); const PromiseResolver = @This(); ctx: *js.Context, -handle: *const v8.c.PromiseResolver, +handle: *const v8.PromiseResolver, pub fn init(ctx: *js.Context) PromiseResolver { return .{ .ctx = ctx, - .handle = v8.c.v8__Promise__Resolver__New(ctx.handle).?, + .handle = v8.v8__Promise__Resolver__New(ctx.handle).?, }; } pub fn promise(self: PromiseResolver) js.Promise { return .{ .ctx = self.ctx, - .handle = v8.c.v8__Promise__Resolver__GetPromise(self.handle).?, + .handle = v8.v8__Promise__Resolver__GetPromise(self.handle).?, }; } @@ -49,8 +49,8 @@ fn _resolve(self: PromiseResolver, value: anytype) !void { const ctx: *js.Context = @constCast(self.ctx); const js_value = try ctx.zigValueToJs(value, .{}); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Promise__Resolver__Resolve(self.handle, self.ctx.handle, js_value.handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Promise__Resolver__Resolve(self.handle, self.ctx.handle, js_value.handle, &out); if (!out.has_value or !out.value) { return error.FailedToResolvePromise; } @@ -67,8 +67,8 @@ fn _reject(self: PromiseResolver, value: anytype) !void { const ctx = self.ctx; const js_value = try ctx.zigValueToJs(value, .{}); - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Promise__Resolver__Reject(self.handle, ctx.handle, js_value.handle, &out); + var out: v8.MaybeBool = undefined; + v8.v8__Promise__Resolver__Reject(self.handle, ctx.handle, js_value.handle, &out); if (!out.has_value or !out.value) { return error.FailedToRejectPromise; } diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index af7cb324..bab594da 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -41,8 +41,8 @@ const embedded_snapshot_blob = if (@import("build_config").snapshot_path) |path| // sequence data_start: usize, -// The snapshot data (v8.c.StartupData is a ptr to the data and len). -startup_data: v8.c.StartupData, +// The snapshot data (v8.StartupData is a ptr to the data and len). +startup_data: v8.StartupData, // V8 doesn't know how to serialize external references, and pretty much any hook // into Zig is an external reference (e.g. every accessor and function callback). @@ -74,8 +74,8 @@ fn loadEmbedded() ?Snapshot { const data_start = std.mem.readInt(usize, embedded_snapshot_blob[0..@sizeOf(usize)], .little); const blob = embedded_snapshot_blob[@sizeOf(usize)..]; - const startup_data = v8.c.StartupData{ .data = blob.ptr, .raw_size = @intCast(blob.len) }; - if (!v8.c.v8__StartupData__IsValid(startup_data)) { + const startup_data = v8.StartupData{ .data = blob.ptr, .raw_size = @intCast(blob.len) }; + if (!v8.v8__StartupData__IsValid(startup_data)) { return null; } @@ -110,7 +110,7 @@ pub fn fromEmbedded(self: Snapshot) bool { } fn isValid(self: Snapshot) bool { - return v8.c.v8__StartupData__IsValid(self.startup_data); + return v8.v8__StartupData__IsValid(self.startup_data); } pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionTemplate) v8.ObjectTemplate { @@ -130,28 +130,28 @@ pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionT pub fn create(allocator: Allocator) !Snapshot { var external_references = collectExternalReferences(); - var params: v8.c.CreateParams = undefined; - v8.c.v8__Isolate__CreateParams__CONSTRUCT(¶ms); - params.array_buffer_allocator = v8.c.v8__ArrayBuffer__Allocator__NewDefaultAllocator(); - defer v8.c.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); + var params: v8.CreateParams = undefined; + v8.v8__Isolate__CreateParams__CONSTRUCT(¶ms); + params.array_buffer_allocator = v8.v8__ArrayBuffer__Allocator__NewDefaultAllocator(); + defer v8.v8__ArrayBuffer__Allocator__DELETE(params.array_buffer_allocator.?); params.external_references = @ptrCast(&external_references); - const snapshot_creator = v8.c.v8__SnapshotCreator__CREATE(¶ms); - defer v8.c.v8__SnapshotCreator__DESTRUCT(snapshot_creator); + const snapshot_creator = v8.v8__SnapshotCreator__CREATE(¶ms); + defer v8.v8__SnapshotCreator__DESTRUCT(snapshot_creator); var data_start: usize = 0; - const isolate = v8.c.v8__SnapshotCreator__getIsolate(snapshot_creator).?; + const isolate = v8.v8__SnapshotCreator__getIsolate(snapshot_creator).?; { // CreateBlob, which we'll call once everything is setup, MUST NOT // be called from an active HandleScope. Hence we have this scope to // clean it up before we call CreateBlob - var handle_scope: v8.c.HandleScope = undefined; - v8.c.v8__HandleScope__CONSTRUCT(&handle_scope, isolate); - defer v8.c.v8__HandleScope__DESTRUCT(&handle_scope); + var handle_scope: v8.HandleScope = undefined; + v8.v8__HandleScope__CONSTRUCT(&handle_scope, isolate); + defer v8.v8__HandleScope__DESTRUCT(&handle_scope); // Create templates (constructors only) FIRST - var templates: [JsApis.len]*v8.c.FunctionTemplate = undefined; + var templates: [JsApis.len]*v8.FunctionTemplate = undefined; inline for (JsApis, 0..) |JsApi, i| { @setEvalBranchQuota(10_000); templates[i] = generateConstructor(JsApi, isolate); @@ -162,23 +162,22 @@ pub fn create(allocator: Allocator) !Snapshot { // This must come before attachClass so inheritance is set up first inline for (JsApis, 0..) |JsApi, i| { if (comptime protoIndexLookup(JsApi)) |proto_index| { - v8.c.v8__FunctionTemplate__Inherit(templates[i], templates[proto_index]); + v8.v8__FunctionTemplate__Inherit(templates[i], templates[proto_index]); } } // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance - const global_template = createGlobalTemplate(isolate, templates[0..]); - const context = v8.c.v8__Context__New(isolate, global_template, null); - v8.c.v8__Context__Enter(context); - defer v8.c.v8__Context__Exit(context); + const context = v8.v8__Context__New(isolate, global_template, null); + v8.v8__Context__Enter(context); + defer v8.v8__Context__Exit(context); // Add templates to context snapshot var last_data_index: usize = 0; inline for (JsApis, 0..) |_, i| { @setEvalBranchQuota(10_000); - const data_index = v8.c.v8__SnapshotCreator__AddData2(snapshot_creator, context, @ptrCast(templates[i])); + const data_index = v8.v8__SnapshotCreator__AddData2(snapshot_creator, context, @ptrCast(templates[i])); if (i == 0) { data_start = data_index; last_data_index = data_index; @@ -196,18 +195,18 @@ pub fn create(allocator: Allocator) !Snapshot { } // Realize all templates by getting their functions and attaching to global - const global_obj = v8.c.v8__Context__Global(context); + const global_obj = v8.v8__Context__Global(context); inline for (JsApis, 0..) |JsApi, i| { - const func = v8.c.v8__FunctionTemplate__GetFunction(templates[i], context); + const func = v8.v8__FunctionTemplate__GetFunction(templates[i], context); // Attach to global if it has a name if (@hasDecl(JsApi.Meta, "name")) { if (@hasDecl(JsApi.Meta, "constructor_alias")) { const alias = JsApi.Meta.constructor_alias; - const v8_class_name = v8.c.v8__String__NewFromUtf8(isolate, alias.ptr, v8.c.kNormal, @intCast(alias.len)); - var maybe_result: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); + const v8_class_name = v8.v8__String__NewFromUtf8(isolate, alias.ptr, v8.kNormal, @intCast(alias.len)); + var maybe_result: v8.MaybeBool = undefined; + v8.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); // @TODO: This is wrong. This name should be registered with the // illegalConstructorCallback. I.e. new Image() is OK, but @@ -216,14 +215,14 @@ pub fn create(allocator: Allocator) !Snapshot { // has to be registered so, for now, instead of creating another // template, we just hook it into the constructor. const name = JsApi.Meta.name; - const illegal_class_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); - var maybe_result2: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Set(global_obj, context, illegal_class_name, func, &maybe_result2); + const illegal_class_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len)); + var maybe_result2: v8.MaybeBool = undefined; + v8.v8__Object__Set(global_obj, context, illegal_class_name, func, &maybe_result2); } else { const name = JsApi.Meta.name; - const v8_class_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); - var maybe_result: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); + const v8_class_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len)); + var maybe_result: v8.MaybeBool = undefined; + v8.v8__Object__Set(global_obj, context, v8_class_name, func, &maybe_result); } } } @@ -231,9 +230,9 @@ pub fn create(allocator: Allocator) !Snapshot { { // If we want to overwrite the built-in console, we have to // delete the built-in one. - const console_key = v8.c.v8__String__NewFromUtf8(isolate, "console", v8.c.kNormal, 7); - var maybe_deleted: v8.c.MaybeBool = undefined; - v8.c.v8__Object__Delete(global_obj, context, console_key, &maybe_deleted); + const console_key = v8.v8__String__NewFromUtf8(isolate, "console", v8.kNormal, 7); + var maybe_deleted: v8.MaybeBool = undefined; + v8.v8__Object__Delete(global_obj, context, console_key, &maybe_deleted); if (maybe_deleted.value == false) { return error.ConsoleDeleteError; } @@ -244,14 +243,14 @@ pub fn create(allocator: Allocator) !Snapshot { // TODO: see if newer V8 engines have a way around this. inline for (JsApis, 0..) |JsApi, i| { if (comptime protoIndexLookup(JsApi)) |proto_index| { - const proto_func = v8.c.v8__FunctionTemplate__GetFunction(templates[proto_index], context); - const proto_obj: *const v8.c.Object = @ptrCast(proto_func); + const proto_func = v8.v8__FunctionTemplate__GetFunction(templates[proto_index], context); + const proto_obj: *const v8.Object = @ptrCast(proto_func); - const self_func = v8.c.v8__FunctionTemplate__GetFunction(templates[i], context); - const self_obj: *const v8.c.Object = @ptrCast(self_func); + const self_func = v8.v8__FunctionTemplate__GetFunction(templates[i], context); + const self_obj: *const v8.Object = @ptrCast(self_func); - var maybe_result: v8.c.MaybeBool = undefined; - v8.c.v8__Object__SetPrototype(self_obj, context, proto_obj, &maybe_result); + var maybe_result: v8.MaybeBool = undefined; + v8.v8__Object__SetPrototype(self_obj, context, proto_obj, &maybe_result); } } @@ -259,15 +258,15 @@ pub fn create(allocator: Allocator) !Snapshot { // Custom exception // TODO: this is an horrible hack, I can't figure out how to do this cleanly. const code_str = "DOMException.prototype.__proto__ = Error.prototype"; - const code = v8.c.v8__String__NewFromUtf8(isolate, code_str.ptr, v8.c.kNormal, @intCast(code_str.len)); - const script = v8.c.v8__Script__Compile(context, code, null) orelse return error.ScriptCompileFailed; - _ = v8.c.v8__Script__Run(script, context) orelse return error.ScriptRunFailed; + const code = v8.v8__String__NewFromUtf8(isolate, code_str.ptr, v8.kNormal, @intCast(code_str.len)); + const script = v8.v8__Script__Compile(context, code, null) orelse return error.ScriptCompileFailed; + _ = v8.v8__Script__Run(script, context) orelse return error.ScriptRunFailed; } - v8.c.v8__SnapshotCreator__setDefaultContext(snapshot_creator, context); + v8.v8__SnapshotCreator__setDefaultContext(snapshot_creator, context); } - const blob = v8.c.v8__SnapshotCreator__createBlob(snapshot_creator, v8.c.kKeep); + const blob = v8.v8__SnapshotCreator__createBlob(snapshot_creator, v8.kKeep); const owned = try allocator.dupe(u8, blob.data[0..@intCast(blob.raw_size)]); return .{ @@ -383,7 +382,7 @@ fn collectExternalReferences() [countExternalReferences()]isize { // via `new ClassName()` - but they could, for example, be created in // Zig and returned from a function call, which is why we need the // FunctionTemplate. -fn generateConstructor(comptime JsApi: type, isolate: *v8.c.Isolate) *v8.c.FunctionTemplate { +fn generateConstructor(comptime JsApi: type, isolate: *v8.Isolate) *v8.FunctionTemplate { const callback = blk: { if (@hasDecl(JsApi, "constructor")) { break :blk JsApi.constructor.func; @@ -393,24 +392,23 @@ fn generateConstructor(comptime JsApi: type, isolate: *v8.c.Isolate) *v8.c.Funct break :blk illegalConstructorCallback; }; - const template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, callback).?); + const template = @constCast(v8.v8__FunctionTemplate__New__DEFAULT2(isolate, callback).?); if (!@hasDecl(JsApi.Meta, "empty_with_no_proto")) { - const instance_template = v8.c.v8__FunctionTemplate__InstanceTemplate(template); - v8.c.v8__ObjectTemplate__SetInternalFieldCount(instance_template, 1); + const instance_template = v8.v8__FunctionTemplate__InstanceTemplate(template); + v8.v8__ObjectTemplate__SetInternalFieldCount(instance_template, 1); } const name_str = if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi); - const class_name = v8.c.v8__String__NewFromUtf8(isolate, name_str.ptr, v8.c.kNormal, @intCast(name_str.len)); - v8.c.v8__FunctionTemplate__SetClassName(template, class_name); + const class_name = v8.v8__String__NewFromUtf8(isolate, name_str.ptr, v8.kNormal, @intCast(name_str.len)); + v8.v8__FunctionTemplate__SetClassName(template, class_name); return template; } // Attaches JsApi members to the prototype template (normal case) - fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.FunctionTemplate) void { const target = v8.c.v8__FunctionTemplate__PrototypeTemplate(template); const instance = v8.c.v8__FunctionTemplate__InstanceTemplate(template); - const declarations = @typeInfo(JsApi).@"struct".decls; + inline for (declarations) |d| { const name: [:0]const u8 = d.name; const value = @field(JsApi, name); @@ -418,31 +416,31 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio switch (definition) { bridge.Accessor => { - const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); - const getter_callback = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.getter).?); + const js_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len)); + const getter_callback = @constCast(v8.v8__FunctionTemplate__New__DEFAULT2(isolate, value.getter).?); if (value.setter == null) { if (value.static) { - v8.c.v8__Template__SetAccessorProperty__DEFAULT(@ptrCast(template), js_name, getter_callback); + v8.v8__Template__SetAccessorProperty__DEFAULT(@ptrCast(template), js_name, getter_callback); } else { - v8.c.v8__ObjectTemplate__SetAccessorProperty__DEFAULT(target, js_name, getter_callback); + v8.v8__ObjectTemplate__SetAccessorProperty__DEFAULT(target, js_name, getter_callback); } } else { std.debug.assert(value.static == false); - const setter_callback = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.setter.?).?); - v8.c.v8__ObjectTemplate__SetAccessorProperty__DEFAULT2(target, js_name, getter_callback, setter_callback); + const setter_callback = @constCast(v8.v8__FunctionTemplate__New__DEFAULT2(isolate, value.setter.?).?); + v8.v8__ObjectTemplate__SetAccessorProperty__DEFAULT2(target, js_name, getter_callback, setter_callback); } }, bridge.Function => { - const function_template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); - const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); + const function_template = @constCast(v8.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); + const js_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len)); if (value.static) { - v8.c.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.c.None); + v8.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.None); } else { - v8.c.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.c.None); + v8.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.None); } }, bridge.Indexed => { - var configuration: v8.c.IndexedPropertyHandlerConfiguration = .{ + var configuration: v8.IndexedPropertyHandlerConfiguration = .{ .getter = value.getter, .setter = null, .query = null, @@ -453,11 +451,10 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio .data = null, .flags = 0, }; - instance.setIndexedProperty(configuration, null); + v8.v8__ObjectTemplate__SetIndexedHandler(instance, &configuration); }, bridge.NamedIndexed => { - const instance_template = v8.c.v8__FunctionTemplate__InstanceTemplate(template); - var configuration: v8.c.NamedPropertyHandlerConfiguration = .{ + var configuration: v8.NamedPropertyHandlerConfiguration = .{ .getter = value.getter, .setter = value.setter, .query = null, @@ -466,17 +463,17 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio .definer = null, .descriptor = null, .data = null, - .flags = v8.c.kOnlyInterceptStrings | v8.c.kNonMasking, + .flags = v8.kOnlyInterceptStrings | v8.kNonMasking, }; - v8.c.v8__ObjectTemplate__SetNamedHandler(instance, &configuration); + v8.v8__ObjectTemplate__SetNamedHandler(instance, &configuration); }, bridge.Iterator => { - const function_template = @constCast(v8.c.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); + const function_template = @constCast(v8.v8__FunctionTemplate__New__DEFAULT2(isolate, value.func).?); const js_name = if (value.async) - v8.c.v8__Symbol__GetAsyncIterator(isolate) + v8.v8__Symbol__GetAsyncIterator(isolate) else - v8.c.v8__Symbol__GetIterator(isolate); - v8.c.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.c.None); + v8.v8__Symbol__GetIterator(isolate); + v8.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.None); }, bridge.Property => { // simpleZigValueToJs now returns raw handle directly @@ -484,12 +481,12 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio .int => |v| js.simpleZigValueToJs(.{ .handle = isolate }, v, true, false), }; - const js_name = v8.c.v8__String__NewFromUtf8(isolate, name.ptr, v8.c.kNormal, @intCast(name.len)); + const js_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len)); // apply it both to the type itself - v8.c.v8__Template__Set(@ptrCast(template), js_name, js_value, v8.c.ReadOnly + v8.c.DontDelete); + v8.v8__Template__Set(@ptrCast(template), js_name, js_value, v8.ReadOnly + v8.DontDelete); // and to instances of the type - v8.c.v8__Template__Set(@ptrCast(target), js_name, js_value, v8.c.ReadOnly + v8.c.DontDelete); + v8.v8__Template__Set(@ptrCast(target), js_name, js_value, v8.ReadOnly + v8.DontDelete); }, bridge.Constructor => {}, // already handled in generateConstructor else => {}, @@ -521,15 +518,15 @@ fn protoIndexLookup(comptime JsApi: type) ?bridge.JsApiLookup.BackingInt { } // Shared illegal constructor callback for types without explicit constructors -fn illegalConstructorCallback(raw_info: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info); +fn illegalConstructorCallback(raw_info: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const isolate = v8.v8__FunctionCallbackInfo__GetIsolate(raw_info); log.warn(.js, "Illegal constructor call", .{}); - const message = v8.c.v8__String__NewFromUtf8(isolate, "Illegal Constructor", v8.c.kNormal, 19); - const js_exception = v8.c.v8__Exception__TypeError(message); + const message = v8.v8__String__NewFromUtf8(isolate, "Illegal Constructor", v8.kNormal, 19); + const js_exception = v8.v8__Exception__TypeError(message); - _ = v8.c.v8__Isolate__ThrowException(isolate, js_exception); - var return_value: v8.c.ReturnValue = undefined; - v8.c.v8__FunctionCallbackInfo__GetReturnValue(raw_info, &return_value); - v8.c.v8__ReturnValue__Set(return_value, js_exception); + _ = v8.v8__Isolate__ThrowException(isolate, js_exception); + var return_value: v8.ReturnValue = undefined; + v8.v8__FunctionCallbackInfo__GetReturnValue(raw_info, &return_value); + v8.v8__ReturnValue__Set(return_value, js_exception); } diff --git a/src/browser/js/String.zig b/src/browser/js/String.zig index a170e538..4fb9b395 100644 --- a/src/browser/js/String.zig +++ b/src/browser/js/String.zig @@ -26,7 +26,7 @@ const v8 = js.v8; const String = @This(); ctx: *js.Context, -handle: *const v8.c.String, +handle: *const v8.String, pub const ToZigOpts = struct { allocator: ?Allocator = null, @@ -43,11 +43,11 @@ pub fn toZigZ(self: String, opts: ToZigOpts) ![:0]u8 { fn _toZig(self: String, comptime null_terminate: bool, opts: ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) { const isolate = self.ctx.isolate.handle; const allocator = opts.allocator orelse self.ctx.call_arena; - const len: u32 = @intCast(v8.c.v8__String__Utf8Length(self.handle, isolate)); + const len: u32 = @intCast(v8.v8__String__Utf8Length(self.handle, isolate)); const buf = if (null_terminate) try allocator.allocSentinel(u8, len, 0) else try allocator.alloc(u8, len); - const options = v8.c.NO_NULL_TERMINATION | v8.c.REPLACE_INVALID_UTF8; - const n = v8.c.v8__String__WriteUtf8(self.handle, isolate, buf.ptr, buf.len, options); + const options = v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8; + const n = v8.v8__String__WriteUtf8(self.handle, isolate, buf.ptr, buf.len, options); std.debug.assert(n == len); return buf; } diff --git a/src/browser/js/TryCatch.zig b/src/browser/js/TryCatch.zig index 7fc49598..7009682b 100644 --- a/src/browser/js/TryCatch.zig +++ b/src/browser/js/TryCatch.zig @@ -25,20 +25,20 @@ const Allocator = std.mem.Allocator; const TryCatch = @This(); ctx: *js.Context, -handle: v8.c.TryCatch, +handle: v8.TryCatch, pub fn init(self: *TryCatch, ctx: *js.Context) void { self.ctx = ctx; - v8.c.v8__TryCatch__CONSTRUCT(&self.handle, ctx.isolate.handle); + v8.v8__TryCatch__CONSTRUCT(&self.handle, ctx.isolate.handle); } pub fn hasCaught(self: TryCatch) bool { - return v8.c.v8__TryCatch__HasCaught(&self.handle); + return v8.v8__TryCatch__HasCaught(&self.handle); } // the caller needs to deinit the string returned pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 { - const msg_value = v8.c.v8__TryCatch__Exception(&self.handle) orelse return null; + const msg_value = v8.v8__TryCatch__Exception(&self.handle) orelse return null; const msg = js.Value{ .ctx = self.ctx, .handle = msg_value }; return try self.ctx.valueToString(msg, .{ .allocator = allocator }); } @@ -46,7 +46,7 @@ pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 { // the caller needs to deinit the string returned pub fn stack(self: TryCatch, allocator: Allocator) !?[]const u8 { const ctx = self.ctx; - const s_value = v8.c.v8__TryCatch__StackTrace(&self.handle, ctx.handle) orelse return null; + const s_value = v8.v8__TryCatch__StackTrace(&self.handle, ctx.handle) orelse return null; const s = js.Value{ .ctx = ctx, .handle = s_value }; return try ctx.valueToString(s, .{ .allocator = allocator }); } @@ -54,15 +54,15 @@ pub fn stack(self: TryCatch, allocator: Allocator) !?[]const u8 { // the caller needs to deinit the string returned pub fn sourceLine(self: TryCatch, allocator: Allocator) !?[]const u8 { const ctx = self.ctx; - const msg = v8.c.v8__TryCatch__Message(&self.handle) orelse return null; - const source_line_handle = v8.c.v8__Message__GetSourceLine(msg, ctx.handle) orelse return null; + const msg = v8.v8__TryCatch__Message(&self.handle) orelse return null; + const source_line_handle = v8.v8__Message__GetSourceLine(msg, ctx.handle) orelse return null; return try ctx.jsStringToZig(source_line_handle, .{ .allocator = allocator }); } pub fn sourceLineNumber(self: TryCatch) ?u32 { const ctx = self.ctx; - const msg = v8.c.v8__TryCatch__Message(&self.handle) orelse return null; - const line = v8.c.v8__Message__GetLineNumber(msg, ctx.handle); + const msg = v8.v8__TryCatch__Message(&self.handle) orelse return null; + const line = v8.v8__Message__GetLineNumber(msg, ctx.handle); if (line < 0) { return null; } @@ -84,5 +84,5 @@ pub fn err(self: TryCatch, allocator: Allocator) !?[]const u8 { } pub fn deinit(self: *TryCatch) void { - v8.c.v8__TryCatch__DESTRUCT(&self.handle); + v8.v8__TryCatch__DESTRUCT(&self.handle); } diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index a08254d0..cce891a3 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -28,18 +28,18 @@ const Allocator = std.mem.Allocator; const Value = @This(); ctx: *js.Context, -handle: *const v8.c.Value, +handle: *const v8.Value, pub fn isObject(self: Value) bool { - return v8.c.v8__Value__IsObject(self.handle); + return v8.v8__Value__IsObject(self.handle); } pub fn isString(self: Value) bool { - return v8.c.v8__Value__IsString(self.handle); + return v8.v8__Value__IsString(self.handle); } pub fn isArray(self: Value) bool { - return v8.c.v8__Value__IsArray(self.handle); + return v8.v8__Value__IsArray(self.handle); } pub fn isNull(self: Value) bool { @@ -51,123 +51,123 @@ pub fn isUndefined(self: Value) bool { } pub fn isSymbol(self: Value) bool { - return v8.c.v8__Value__IsSymbol(self.handle); + return v8.v8__Value__IsSymbol(self.handle); } pub fn isFunction(self: Value) bool { - return v8.c.v8__Value__IsFunction(self.handle); + return v8.v8__Value__IsFunction(self.handle); } pub fn isNull(self: Value) bool { - return v8.c.v8__Value__IsNull(self.handle); + return v8.v8__Value__IsNull(self.handle); } pub fn isUndefined(self: Value) bool { - return v8.c.v8__Value__IsUndefined(self.handle); + return v8.v8__Value__IsUndefined(self.handle); } pub fn isNullOrUndefined(self: Value) bool { - return v8.c.v8__Value__IsNullOrUndefined(self.handle); + return v8.v8__Value__IsNullOrUndefined(self.handle); } pub fn isNumber(self: Value) bool { - return v8.c.v8__Value__IsNumber(self.handle); + return v8.v8__Value__IsNumber(self.handle); } pub fn isNumberObject(self: Value) bool { - return v8.c.v8__Value__IsNumberObject(self.handle); + return v8.v8__Value__IsNumberObject(self.handle); } pub fn isInt32(self: Value) bool { - return v8.c.v8__Value__IsInt32(self.handle); + return v8.v8__Value__IsInt32(self.handle); } pub fn isUint32(self: Value) bool { - return v8.c.v8__Value__IsUint32(self.handle); + return v8.v8__Value__IsUint32(self.handle); } pub fn isBigInt(self: Value) bool { - return v8.c.v8__Value__IsBigInt(self.handle); + return v8.v8__Value__IsBigInt(self.handle); } pub fn isBigIntObject(self: Value) bool { - return v8.c.v8__Value__IsBigIntObject(self.handle); + return v8.v8__Value__IsBigIntObject(self.handle); } pub fn isBoolean(self: Value) bool { - return v8.c.v8__Value__IsBoolean(self.handle); + return v8.v8__Value__IsBoolean(self.handle); } pub fn isBooleanObject(self: Value) bool { - return v8.c.v8__Value__IsBooleanObject(self.handle); + return v8.v8__Value__IsBooleanObject(self.handle); } pub fn isTrue(self: Value) bool { - return v8.c.v8__Value__IsTrue(self.handle); + return v8.v8__Value__IsTrue(self.handle); } pub fn isFalse(self: Value) bool { - return v8.c.v8__Value__IsFalse(self.handle); + return v8.v8__Value__IsFalse(self.handle); } pub fn isTypedArray(self: Value) bool { - return v8.c.v8__Value__IsTypedArray(self.handle); + return v8.v8__Value__IsTypedArray(self.handle); } pub fn isArrayBufferView(self: Value) bool { - return v8.c.v8__Value__IsArrayBufferView(self.handle); + return v8.v8__Value__IsArrayBufferView(self.handle); } pub fn isArrayBuffer(self: Value) bool { - return v8.c.v8__Value__IsArrayBuffer(self.handle); + return v8.v8__Value__IsArrayBuffer(self.handle); } pub fn isUint8Array(self: Value) bool { - return v8.c.v8__Value__IsUint8Array(self.handle); + return v8.v8__Value__IsUint8Array(self.handle); } pub fn isUint8ClampedArray(self: Value) bool { - return v8.c.v8__Value__IsUint8ClampedArray(self.handle); + return v8.v8__Value__IsUint8ClampedArray(self.handle); } pub fn isInt8Array(self: Value) bool { - return v8.c.v8__Value__IsInt8Array(self.handle); + return v8.v8__Value__IsInt8Array(self.handle); } pub fn isUint16Array(self: Value) bool { - return v8.c.v8__Value__IsUint16Array(self.handle); + return v8.v8__Value__IsUint16Array(self.handle); } pub fn isInt16Array(self: Value) bool { - return v8.c.v8__Value__IsInt16Array(self.handle); + return v8.v8__Value__IsInt16Array(self.handle); } pub fn isUint32Array(self: Value) bool { - return v8.c.v8__Value__IsUint32Array(self.handle); + return v8.v8__Value__IsUint32Array(self.handle); } pub fn isInt32Array(self: Value) bool { - return v8.c.v8__Value__IsInt32Array(self.handle); + return v8.v8__Value__IsInt32Array(self.handle); } pub fn isBigUint64Array(self: Value) bool { - return v8.c.v8__Value__IsBigUint64Array(self.handle); + return v8.v8__Value__IsBigUint64Array(self.handle); } pub fn isBigInt64Array(self: Value) bool { - return v8.c.v8__Value__IsBigInt64Array(self.handle); + return v8.v8__Value__IsBigInt64Array(self.handle); } pub fn isPromise(self: Value) bool { - return v8.c.v8__Value__IsPromise(self.handle); + return v8.v8__Value__IsPromise(self.handle); } pub fn toBool(self: Value) bool { - return v8.c.v8__Value__BooleanValue(self.handle, self.ctx.isolate.handle); + return v8.v8__Value__BooleanValue(self.handle, self.ctx.isolate.handle); } pub fn typeOf(self: Value) js.String { - const str_handle = v8.c.v8__Value__TypeOf(self.handle, self.ctx.isolate.handle).?; + const str_handle = v8.v8__Value__TypeOf(self.handle, self.ctx.isolate.handle).?; return js.String{ .ctx = @constCast(self.ctx), .handle = str_handle }; } @@ -176,8 +176,8 @@ pub fn toF32(self: Value) !f32 { } pub fn toF64(self: Value) !f64 { - var maybe: v8.c.MaybeF64 = undefined; - v8.c.v8__Value__NumberValue(self.handle, self.ctx.handle, &maybe); + var maybe: v8.MaybeF64 = undefined; + v8.v8__Value__NumberValue(self.handle, self.ctx.handle, &maybe); if (!maybe.has_value) { return error.JsException; } @@ -185,8 +185,8 @@ pub fn toF64(self: Value) !f64 { } pub fn toI32(self: Value) !i32 { - var maybe: v8.c.MaybeI32 = undefined; - v8.c.v8__Value__Int32Value(self.handle, self.ctx.handle, &maybe); + var maybe: v8.MaybeI32 = undefined; + v8.v8__Value__Int32Value(self.handle, self.ctx.handle, &maybe); if (!maybe.has_value) { return error.JsException; } @@ -194,8 +194,8 @@ pub fn toI32(self: Value) !i32 { } pub fn toU32(self: Value) !u32 { - var maybe: v8.c.MaybeU32 = undefined; - v8.c.v8__Value__Uint32Value(self.handle, self.ctx.handle, &maybe); + var maybe: v8.MaybeU32 = undefined; + v8.v8__Value__Uint32Value(self.handle, self.ctx.handle, &maybe); if (!maybe.has_value) { return error.JsException; } @@ -223,11 +223,11 @@ fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOp const ctx: *js.Context = @constCast(self.ctx); if (self.isSymbol()) { - const sym_handle = v8.c.v8__Symbol__Description(@ptrCast(self.handle), ctx.isolate.handle).?; + const sym_handle = v8.v8__Symbol__Description(@ptrCast(self.handle), ctx.isolate.handle).?; return _toString(.{ .handle = @ptrCast(sym_handle), .ctx = ctx }, null_terminate, opts); } - const str_handle = v8.c.v8__Value__ToString(self.handle, ctx.handle) orelse { + const str_handle = v8.v8__Value__ToString(self.handle, ctx.handle) orelse { return error.JsException; }; diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 223e3fc2..2d2adace 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -38,71 +38,71 @@ const IS_DEBUG = @import("builtin").mode == .Debug; // They are not exported - internal to this module only. const Value = struct { - handle: *const v8.c.Value, + handle: *const v8.Value, fn isArray(self: Value) bool { - return v8.c.v8__Value__IsArray(self.handle); + return v8.v8__Value__IsArray(self.handle); } fn isTypedArray(self: Value) bool { - return v8.c.v8__Value__IsTypedArray(self.handle); + return v8.v8__Value__IsTypedArray(self.handle); } fn isFunction(self: Value) bool { - return v8.c.v8__Value__IsFunction(self.handle); + return v8.v8__Value__IsFunction(self.handle); } }; const Name = struct { - handle: *const v8.c.Name, + handle: *const v8.Name, }; const FunctionCallbackInfo = struct { - handle: *const v8.c.FunctionCallbackInfo, + handle: *const v8.FunctionCallbackInfo, fn length(self: FunctionCallbackInfo) u32 { - return @intCast(v8.c.v8__FunctionCallbackInfo__Length(self.handle)); + return @intCast(v8.v8__FunctionCallbackInfo__Length(self.handle)); } fn getArg(self: FunctionCallbackInfo, index: u32) Value { - return .{ .handle = v8.c.v8__FunctionCallbackInfo__INDEX(self.handle, @intCast(index)).? }; + return .{ .handle = v8.v8__FunctionCallbackInfo__INDEX(self.handle, @intCast(index)).? }; } - fn getThis(self: FunctionCallbackInfo) *const v8.c.Object { - return v8.c.v8__FunctionCallbackInfo__This(self.handle).?; + fn getThis(self: FunctionCallbackInfo) *const v8.Object { + return v8.v8__FunctionCallbackInfo__This(self.handle).?; } fn getReturnValue(self: FunctionCallbackInfo) ReturnValue { - var rv: v8.c.ReturnValue = undefined; - v8.c.v8__FunctionCallbackInfo__GetReturnValue(self.handle, &rv); + var rv: v8.ReturnValue = undefined; + v8.v8__FunctionCallbackInfo__GetReturnValue(self.handle, &rv); return .{ .handle = rv }; } }; const PropertyCallbackInfo = struct { - handle: *const v8.c.PropertyCallbackInfo, + handle: *const v8.PropertyCallbackInfo, - fn getThis(self: PropertyCallbackInfo) *const v8.c.Object { - return v8.c.v8__PropertyCallbackInfo__This(self.handle).?; + fn getThis(self: PropertyCallbackInfo) *const v8.Object { + return v8.v8__PropertyCallbackInfo__This(self.handle).?; } fn getReturnValue(self: PropertyCallbackInfo) ReturnValue { - var rv: v8.c.ReturnValue = undefined; - v8.c.v8__PropertyCallbackInfo__GetReturnValue(self.handle, &rv); + var rv: v8.ReturnValue = undefined; + v8.v8__PropertyCallbackInfo__GetReturnValue(self.handle, &rv); return .{ .handle = rv }; } }; const ReturnValue = struct { - handle: v8.c.ReturnValue, + handle: v8.ReturnValue, fn set(self: ReturnValue, value: anytype) void { const T = @TypeOf(value); if (T == Value) { self.setValueHandle(value.handle); - } else if (T == *const v8.c.Object) { + } else if (T == *const v8.Object) { self.setValueHandle(@ptrCast(value)); - } else if (T == *const v8.c.Value) { + } else if (T == *const v8.Value) { self.setValueHandle(value); } else if (T == js.Value) { self.setValueHandle(value.handle); @@ -111,8 +111,8 @@ const ReturnValue = struct { } } - fn setValueHandle(self: ReturnValue, handle: *const v8.c.Value) void { - v8.c.v8__ReturnValue__Set(self.handle, handle); + fn setValueHandle(self: ReturnValue, handle: *const v8.Value) void { + v8.v8__ReturnValue__Set(self.handle, handle); } }; @@ -126,12 +126,12 @@ pub const Caller = struct { call_arena: Allocator, // Takes the raw v8 isolate and extracts the context from it. - pub fn init(v8_isolate: *v8.c.Isolate) Caller { + pub fn init(v8_isolate: *v8.Isolate) Caller { const isolate = js.Isolate{ .handle = v8_isolate }; - const v8_context_handle = v8.c.v8__Isolate__GetCurrentContext(v8_isolate); - const embedder_data = v8.c.v8__Context__GetEmbedderData(v8_context_handle, 1); + const v8_context_handle = v8.v8__Isolate__GetCurrentContext(v8_isolate); + const embedder_data = v8.v8__Context__GetEmbedderData(v8_context_handle, 1); var lossless: bool = undefined; - const context: *Context = @ptrFromInt(v8.c.v8__BigInt__Uint64Value(embedder_data, &lossless)); + const context: *Context = @ptrFromInt(v8.v8__BigInt__Uint64Value(embedder_data, &lossless)); context.call_depth += 1; return .{ @@ -197,9 +197,9 @@ pub const Caller = struct { // If we got back a different object (existing wrapper), copy the prototype // from new object. (this happens when we're upgrading an CustomElement) if (this.handle != new_this_handle) { - const prototype_handle = v8.c.v8__Object__GetPrototype(new_this_handle).?; - var out: v8.c.MaybeBool = undefined; - v8.c.v8__Object__SetPrototype(this.handle, self.context.handle, prototype_handle, &out); + const prototype_handle = v8.v8__Object__GetPrototype(new_this_handle).?; + var out: v8.MaybeBool = undefined; + v8.v8__Object__SetPrototype(this.handle, self.context.handle, prototype_handle, &out); if (comptime IS_DEBUG) { std.debug.assert(out.has_value and out.value); } @@ -364,7 +364,7 @@ pub const Caller = struct { } } - const js_err: *const v8.c.Value = switch (err) { + const js_err: *const v8.Value = switch (err) { error.InvalidArgument => isolate.createTypeError("invalid argument"), error.OutOfMemory => isolate.createError("out of memory"), error.IllegalConstructor => isolate.createError("Illegal Contructor"), @@ -624,7 +624,7 @@ pub fn Builder(comptime T: type) type { } pub const Constructor = struct { - func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { dom_exception: bool = false, @@ -632,8 +632,8 @@ pub const Constructor = struct { fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Constructor { return .{ .func = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -648,7 +648,7 @@ pub const Constructor = struct { pub const Function = struct { static: bool, - func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { static: bool = false, @@ -661,8 +661,8 @@ pub const Function = struct { return .{ .static = opts.static, .func = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -688,8 +688,8 @@ pub const Function = struct { pub const Accessor = struct { static: bool = false, - getter: ?*const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void = null, - setter: ?*const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void = null, + getter: ?*const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void = null, + setter: ?*const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void = null, const Opts = struct { static: bool = false, @@ -705,8 +705,8 @@ pub const Accessor = struct { if (@typeInfo(@TypeOf(getter)) != .null) { accessor.getter = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -721,8 +721,8 @@ pub const Accessor = struct { if (@typeInfo(@TypeOf(setter)) != .null) { accessor.setter = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -742,7 +742,7 @@ pub const Accessor = struct { }; pub const Indexed = struct { - getter: *const fn (idx: u32, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8, + getter: *const fn (idx: u32, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8, const Opts = struct { as_typed_array: bool = false, @@ -751,8 +751,8 @@ pub const Indexed = struct { fn init(comptime T: type, comptime getter: anytype, comptime opts: Opts) Indexed { return .{ .getter = struct { - fn wrap(idx: u32, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; + fn wrap(idx: u32, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -767,9 +767,9 @@ pub const Indexed = struct { }; pub const NamedIndexed = struct { - getter: *const fn (c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8, - setter: ?*const fn (c_name: ?*const v8.c.Name, c_value: ?*const v8.c.Value, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 = null, - deleter: ?*const fn (c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 = null, + getter: *const fn (c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8, + setter: ?*const fn (c_name: ?*const v8.Name, c_value: ?*const v8.Value, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 = null, + deleter: ?*const fn (c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 = null, const Opts = struct { as_typed_array: bool = false, @@ -778,8 +778,8 @@ pub const NamedIndexed = struct { fn init(comptime T: type, comptime getter: anytype, setter: anytype, deleter: anytype, comptime opts: Opts) NamedIndexed { const getter_fn = struct { - fn wrap(c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; + fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -792,8 +792,8 @@ pub const NamedIndexed = struct { }.wrap; const setter_fn = if (@typeInfo(@TypeOf(setter)) == .null) null else struct { - fn wrap(c_name: ?*const v8.c.Name, c_value: ?*const v8.c.Value, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; + fn wrap(c_name: ?*const v8.Name, c_value: ?*const v8.Value, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -806,8 +806,8 @@ pub const NamedIndexed = struct { }.wrap; const deleter_fn = if (@typeInfo(@TypeOf(deleter)) == .null) null else struct { - fn wrap(c_name: ?*const v8.c.Name, handle: ?*const v8.c.PropertyCallbackInfo) callconv(.c) u8 { - const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(handle).?; + fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -828,7 +828,7 @@ pub const NamedIndexed = struct { }; pub const Iterator = struct { - func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, async: bool, const Opts = struct { @@ -841,7 +841,7 @@ pub const Iterator = struct { return .{ .async = opts.async, .func = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { const info = FunctionCallbackInfo{ .handle = handle.? }; info.getReturnValue().set(info.getThis()); } @@ -852,8 +852,8 @@ pub const Iterator = struct { return .{ .async = opts.async, .func = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); @@ -866,7 +866,7 @@ pub const Iterator = struct { }; pub const Callable = struct { - func: *const fn (?*const v8.c.FunctionCallbackInfo) callconv(.c) void, + func: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, const Opts = struct { null_as_undefined: bool = false, @@ -874,8 +874,8 @@ pub const Callable = struct { fn init(comptime T: type, comptime func: anytype, comptime opts: Opts) Callable { return .{ .func = struct { - fn wrap(handle: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void { - const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(handle).?; + fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { + const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller = Caller.init(v8_isolate); defer caller.deinit(); diff --git a/src/browser/js/global.zig b/src/browser/js/global.zig index 82c2c55b..9bfe782d 100644 --- a/src/browser/js/global.zig +++ b/src/browser/js/global.zig @@ -25,20 +25,20 @@ pub fn Global(comptime T: type) type { const H = @FieldType(T, "handle"); return struct { - global: v8.c.Global, + global: v8.Global, const Self = @This(); - pub fn init(isolate: *v8.c.Isolate, handle: H) Self { - var global: v8.c.Global = undefined; - v8.c.v8__Global__New(isolate, handle, &global); + pub fn init(isolate: *v8.Isolate, handle: H) Self { + var global: v8.Global = undefined; + v8.v8__Global__New(isolate, handle, &global); return .{ .global = global, }; } pub fn deinit(self: *Self) void { - v8.c.v8__Global__Reset(&self.global); + v8.v8__Global__Reset(&self.global); } pub fn local(self: *const Self) H { diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 6b895687..bf7f3044 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -17,7 +17,7 @@ // along with this program. If not, see . const std = @import("std"); -pub const v8 = @import("v8"); +pub const v8 = @import("v8").c; const log = @import("../../log.zig"); @@ -79,7 +79,7 @@ pub const ArrayBuffer = struct { pub const Exception = struct { ctx: *const Context, - handle: *const v8.c.Value, + handle: *const v8.Value, pub fn exception(self: Exception, allocator: Allocator) ![]const u8 { return self.context.valueToString(self.inner, .{ .allocator = allocator }); @@ -167,7 +167,7 @@ pub fn isComplexAttributeType(ti: std.builtin.Type) bool { // These are simple types that we can convert to JS with only an isolate. This // is separated from the Caller's zigValueToJs to make it available when we // don't have a caller (i.e., when setting static attributes on types) -pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, comptime null_as_undefined: bool) if (fail) *const v8.c.Value else ?*const v8.c.Value { +pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, comptime null_as_undefined: bool) if (fail) *const v8.Value else ?*const v8.Value { switch (@typeInfo(@TypeOf(value))) { .void => return isolate.initUndefined(), .null => if (comptime null_as_undefined) return isolate.initUndefined() else return isolate.initNull(), @@ -214,11 +214,11 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, ArrayBuffer => { const values = value.values; const len = values.len; - const backing_store = v8.c.v8__ArrayBuffer__NewBackingStore(isolate.handle, len); - const data: [*]u8 = @ptrCast(@alignCast(v8.c.v8__BackingStore__Data(backing_store))); + const backing_store = v8.v8__ArrayBuffer__NewBackingStore(isolate.handle, len); + const data: [*]u8 = @ptrCast(@alignCast(v8.v8__BackingStore__Data(backing_store))); @memcpy(data[0..len], @as([]const u8, @ptrCast(values))[0..len]); - const backing_store_ptr = v8.c.v8__BackingStore__TO_SHARED_PTR(backing_store); - return @ptrCast(v8.c.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?); + const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); + return @ptrCast(v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?); }, // zig fmt: off TypedArray(u8), TypedArray(u16), TypedArray(u32), TypedArray(u64), @@ -235,38 +235,38 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, else => @compileError("Invalid TypeArray type: " ++ @typeName(value_type)), }; - var array_buffer: *const v8.c.ArrayBuffer = undefined; + var array_buffer: *const v8.ArrayBuffer = undefined; if (len == 0) { - array_buffer = v8.c.v8__ArrayBuffer__New(isolate.handle, 0).?; + array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; } else { const buffer_len = len * bits / 8; - const backing_store = v8.c.v8__ArrayBuffer__NewBackingStore(isolate.handle, buffer_len).?; - const data: [*]u8 = @ptrCast(@alignCast(v8.c.v8__BackingStore__Data(backing_store))); + const backing_store = v8.v8__ArrayBuffer__NewBackingStore(isolate.handle, buffer_len).?; + const data: [*]u8 = @ptrCast(@alignCast(v8.v8__BackingStore__Data(backing_store))); @memcpy(data[0..buffer_len], @as([]const u8, @ptrCast(values))[0..buffer_len]); - const backing_store_ptr = v8.c.v8__BackingStore__TO_SHARED_PTR(backing_store); - array_buffer = v8.c.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; + const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); + array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; } switch (@typeInfo(value_type)) { .int => |n| switch (n.signedness) { .unsigned => switch (n.bits) { - 8 => return @ptrCast(v8.c.v8__Uint8Array__New(array_buffer, 0, len).?), - 16 => return @ptrCast(v8.c.v8__Uint16Array__New(array_buffer, 0, len).?), - 32 => return @ptrCast(v8.c.v8__Uint32Array__New(array_buffer, 0, len).?), - 64 => return @ptrCast(v8.c.v8__BigUint64Array__New(array_buffer, 0, len).?), + 8 => return @ptrCast(v8.v8__Uint8Array__New(array_buffer, 0, len).?), + 16 => return @ptrCast(v8.v8__Uint16Array__New(array_buffer, 0, len).?), + 32 => return @ptrCast(v8.v8__Uint32Array__New(array_buffer, 0, len).?), + 64 => return @ptrCast(v8.v8__BigUint64Array__New(array_buffer, 0, len).?), else => {}, }, .signed => switch (n.bits) { - 8 => return @ptrCast(v8.c.v8__Int8Array__New(array_buffer, 0, len).?), - 16 => return @ptrCast(v8.c.v8__Int16Array__New(array_buffer, 0, len).?), - 32 => return @ptrCast(v8.c.v8__Int32Array__New(array_buffer, 0, len).?), - 64 => return @ptrCast(v8.c.v8__BigInt64Array__New(array_buffer, 0, len).?), + 8 => return @ptrCast(v8.v8__Int8Array__New(array_buffer, 0, len).?), + 16 => return @ptrCast(v8.v8__Int16Array__New(array_buffer, 0, len).?), + 32 => return @ptrCast(v8.v8__Int32Array__New(array_buffer, 0, len).?), + 64 => return @ptrCast(v8.v8__BigInt64Array__New(array_buffer, 0, len).?), else => {}, }, }, .float => |f| switch (f.bits) { - 32 => return @ptrCast(v8.c.v8__Float32Array__New(array_buffer, 0, len).?), - 64 => return @ptrCast(v8.c.v8__Float64Array__New(array_buffer, 0, len).?), + 32 => return @ptrCast(v8.v8__Float32Array__New(array_buffer, 0, len).?), + 64 => return @ptrCast(v8.v8__Float64Array__New(array_buffer, 0, len).?), else => {}, }, else => {}, @@ -367,8 +367,8 @@ pub const PrototypeChainEntry = struct { // it'll call this function to gets its [optional] subtype - which, from V8's // point of view, is an arbitrary string. pub export fn v8_inspector__Client__IMPL__valueSubtype( - _: *v8.c.InspectorClientImpl, - c_value: *const v8.c.Value, + _: *v8.InspectorClientImpl, + c_value: *const v8.Value, ) callconv(.c) [*c]const u8 { const external_entry = Inspector.getTaggedAnyOpaque(c_value) orelse return null; return if (external_entry.subtype) |st| @tagName(st) else null; @@ -379,9 +379,9 @@ pub export fn v8_inspector__Client__IMPL__valueSubtype( // present, even if it's empty. So if we have a subType for the value, we'll // put an empty description. pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype( - _: *v8.c.InspectorClientImpl, - v8_context: *const v8.c.Context, - c_value: *const v8.c.Value, + _: *v8.InspectorClientImpl, + v8_context: *const v8.Context, + c_value: *const v8.Value, ) callconv(.c) [*c]const u8 { _ = v8_context; From b4f134bff6d4dd91bd0de0bcd1b5c4a4fc658901 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 5 Jan 2026 10:29:00 +0800 Subject: [PATCH 16/26] Prefer js.Value over js.Object in History/Navigation Persist function callback in PerformanceObserver --- src/browser/js/Object.zig | 5 ----- src/browser/js/Value.zig | 5 +++++ src/browser/tests/history.html | 1 + src/browser/webapi/History.zig | 4 ++-- src/browser/webapi/PerformanceObserver.zig | 2 +- src/browser/webapi/navigation/Navigation.zig | 12 ++++++------ 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index 082f135f..c60fe59d 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -114,11 +114,6 @@ pub fn format(self: Object, writer: *std.Io.Writer) !void { return writer.writeAll(str); } -pub fn toJson(self: Object, allocator: Allocator) ![]u8 { - const json_str_handle = v8.v8__JSON__Stringify(self.ctx.handle, @ptrCast(self.handle), null) orelse return error.JsException; - return self.ctx.jsStringToZig(json_str_handle, .{ .allocator = allocator }); -} - pub fn persist(self: Object) !Object { var ctx = self.ctx; diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index cce891a3..a4e28fe4 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -219,6 +219,11 @@ pub fn toStringZ(self: Value, opts: js.String.ToZigOpts) ![:0]u8 { return self._toString(true, opts); } +pub fn toJson(self: Value, allocator: Allocator) ![]u8 { + const json_str_handle = v8.v8__JSON__Stringify(self.ctx.handle, self.handle, null) orelse return error.JsException; + return self.ctx.jsStringToZig(json_str_handle, .{ .allocator = allocator }); +} + fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) { const ctx: *js.Context = @constCast(self.ctx); diff --git a/src/browser/tests/history.html b/src/browser/tests/history.html index 2a6d3957..90df53bf 100644 --- a/src/browser/tests/history.html +++ b/src/browser/tests/history.html @@ -35,3 +35,4 @@ history.back(); + diff --git a/src/browser/webapi/History.zig b/src/browser/webapi/History.zig index 7106be56..124d57b0 100644 --- a/src/browser/webapi/History.zig +++ b/src/browser/webapi/History.zig @@ -49,7 +49,7 @@ pub fn setScrollRestoration(self: *History, str: []const u8) void { } } -pub fn pushState(_: *History, state: js.Object, _: []const u8, _url: ?[]const u8, page: *Page) !void { +pub fn pushState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { const arena = page._session.arena; const url = if (_url) |u| try arena.dupeZ(u8, u) else try arena.dupeZ(u8, page.url); @@ -57,7 +57,7 @@ pub fn pushState(_: *History, state: js.Object, _: []const u8, _url: ?[]const u8 _ = try page._session.navigation.pushEntry(url, .{ .source = .history, .value = json }, page, true); } -pub fn replaceState(_: *History, state: js.Object, _: []const u8, _url: ?[]const u8, page: *Page) !void { +pub fn replaceState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { const arena = page._session.arena; const url = if (_url) |u| try arena.dupeZ(u8, u) else try arena.dupeZ(u8, page.url); diff --git a/src/browser/webapi/PerformanceObserver.zig b/src/browser/webapi/PerformanceObserver.zig index 7e4d9c5d..0b8edd47 100644 --- a/src/browser/webapi/PerformanceObserver.zig +++ b/src/browser/webapi/PerformanceObserver.zig @@ -44,7 +44,7 @@ const DefaultDurationThreshold: f64 = 104; /// Creates a new PerformanceObserver object with the given observer callback. pub fn init(callback: js.Function, page: *Page) !*PerformanceObserver { return page._factory.create(PerformanceObserver{ - ._callback = callback, + ._callback = try callback.persist(), ._duration_threshold = DefaultDurationThreshold, ._interests = 0, ._entries = .{}, diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index e1afa334..c257d1d2 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -247,8 +247,8 @@ pub fn replaceEntry( } const NavigateOptions = struct { - state: ?js.Object = null, - info: ?js.Object = null, + state: ?js.Value = null, + info: ?js.Value = null, history: ?[]const u8 = null, }; @@ -346,8 +346,8 @@ pub fn navigate(self: *Navigation, _url: [:0]const u8, _opts: ?NavigateOptions, } pub const ReloadOptions = struct { - state: ?js.Object = null, - info: ?js.Object = null, + state: ?js.Value = null, + info: ?js.Value = null, }; pub fn reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !NavigationReturn { @@ -371,7 +371,7 @@ pub fn reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigation } pub const TraverseToOptions = struct { - info: ?js.Object = null, + info: ?js.Value = null, }; pub fn traverseTo(self: *Navigation, key: []const u8, _opts: ?TraverseToOptions, page: *Page) !NavigationReturn { @@ -389,7 +389,7 @@ pub fn traverseTo(self: *Navigation, key: []const u8, _opts: ?TraverseToOptions, } pub const UpdateCurrentEntryOptions = struct { - state: js.Object, + state: js.Value, }; pub fn updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions, page: *Page) !void { From 4720268426189213d91ba9fbca1f16559f0fcd41 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 5 Jan 2026 16:05:39 +0800 Subject: [PATCH 17/26] Don't dupe StartupData, use what v8 gives us directly. --- src/App.zig | 6 +++--- src/browser/js/Snapshot.zig | 15 ++++++++------- src/browser/js/js.zig | 7 ------- src/main_snapshot_creator.zig | 4 ++-- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/App.zig b/src/App.zig index 64e51482..38283e10 100644 --- a/src/App.zig +++ b/src/App.zig @@ -86,8 +86,8 @@ pub fn init(allocator: Allocator, config: Config) !*App { app.platform = try Platform.init(); errdefer app.platform.deinit(); - app.snapshot = try Snapshot.load(allocator); - errdefer app.snapshot.deinit(allocator); + app.snapshot = try Snapshot.load(); + errdefer app.snapshot.deinit(); app.app_dir_path = getAndMakeAppDir(allocator); @@ -112,7 +112,7 @@ pub fn deinit(self: *App) void { self.telemetry.deinit(); self.notification.deinit(); self.http.deinit(); - self.snapshot.deinit(allocator); + self.snapshot.deinit(); self.platform.deinit(); allocator.destroy(self); diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index bab594da..9a5112e7 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -56,11 +56,11 @@ external_references: [countExternalReferences()]isize, // If false, the data points into embedded_snapshot_blob and should not be freed owns_data: bool = false, -pub fn load(allocator: Allocator) !Snapshot { +pub fn load() !Snapshot { if (loadEmbedded()) |snapshot| { return snapshot; } - return create(allocator); + return create(); } fn loadEmbedded() ?Snapshot { @@ -87,10 +87,11 @@ fn loadEmbedded() ?Snapshot { }; } -pub fn deinit(self: Snapshot, allocator: Allocator) void { +pub fn deinit(self: Snapshot) void { // Only free if we own the data (was created in-process) if (self.owns_data) { - allocator.free(self.startup_data.data[0..@intCast(self.startup_data.raw_size)]); + // V8 allocated this with `new char[]`, so we need to use the C++ delete[] operator + v8.v8__StartupData__DELETE(self.startup_data.data); } } @@ -113,6 +114,7 @@ fn isValid(self: Snapshot) bool { return v8.v8__StartupData__IsValid(self.startup_data); } +<<<<<<< HEAD pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionTemplate) v8.ObjectTemplate { // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance @@ -127,7 +129,7 @@ pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionT return v8.c.v8__FunctionTemplate__InstanceTemplate(js_global); } -pub fn create(allocator: Allocator) !Snapshot { +pub fn create() !Snapshot { var external_references = collectExternalReferences(); var params: v8.CreateParams = undefined; @@ -267,13 +269,12 @@ pub fn create(allocator: Allocator) !Snapshot { } const blob = v8.v8__SnapshotCreator__createBlob(snapshot_creator, v8.kKeep); - const owned = try allocator.dupe(u8, blob.data[0..@intCast(blob.raw_size)]); return .{ .owns_data = true, .data_start = data_start, .external_references = external_references, - .startup_data = .{ .data = owned.ptr, .raw_size = @intCast(owned.len) }, + .startup_data = blob, }; } diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index bf7f3044..4561a4f3 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -391,13 +391,6 @@ pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype( return if (external_entry.subtype == null) null else ""; } -/// Enables C to allocate using the given Zig allocator -pub export fn zigAlloc(self: *anyopaque, bytes: usize) callconv(.c) ?[*]u8 { - const allocator: *Allocator = @ptrCast(@alignCast(self)); - const allocated_bytes = allocator.alloc(u8, bytes) catch return null; - return allocated_bytes.ptr; -} - test "TaggedAnyOpaque" { // If we grow this, fine, but it should be a conscious decision try std.testing.expectEqual(24, @sizeOf(TaggedAnyOpaque)); diff --git a/src/main_snapshot_creator.zig b/src/main_snapshot_creator.zig index 0a7fec59..57500fca 100644 --- a/src/main_snapshot_creator.zig +++ b/src/main_snapshot_creator.zig @@ -25,8 +25,8 @@ pub fn main() !void { var platform = try lp.js.Platform.init(); defer platform.deinit(); - const snapshot = try lp.js.Snapshot.create(allocator); - defer snapshot.deinit(allocator); + const snapshot = try lp.js.Snapshot.create(); + defer snapshot.deinit(); var is_stdout = true; var file = std.fs.File.stdout(); From 2322cb9b8337a11925ee42efe58e997d6cd82d73 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 5 Jan 2026 18:33:37 +0800 Subject: [PATCH 18/26] remove unused code, remove references to v8::Persistent --- build.zig | 1 - src/browser/js/BigInt.zig | 7 --- src/browser/js/Context.zig | 8 +-- src/browser/js/HandleScope.zig | 4 ++ src/browser/js/Inspector.zig | 2 +- src/browser/js/Name.zig | 7 --- src/browser/js/Object.zig | 11 ----- src/browser/js/Snapshot.zig | 4 +- src/browser/js/Value.zig | 22 +++++++-- src/browser/js/js.zig | 89 ---------------------------------- src/cdp/domains/network.zig | 30 ------------ 11 files changed, 28 insertions(+), 157 deletions(-) diff --git a/build.zig b/build.zig index b9cf6cd7..18e33961 100644 --- a/build.zig +++ b/build.zig @@ -117,7 +117,6 @@ pub fn build(b: *Build) !void { } { - // ZIGDOM // browser const exe = b.addExecutable(.{ .name = "legacy_test", diff --git a/src/browser/js/BigInt.zig b/src/browser/js/BigInt.zig index 5443d69a..6364dc65 100644 --- a/src/browser/js/BigInt.zig +++ b/src/browser/js/BigInt.zig @@ -39,10 +39,3 @@ pub fn getInt64(self: BigInt) i64 { pub fn getUint64(self: BigInt) u64 { return v8.v8__BigInt__Uint64Value(self.handle, null); } - -pub fn toValue(self: BigInt) js.Value { - return .{ - .ctx = undefined, // Will be set by caller if needed - .handle = @ptrCast(self.handle), - }; -} diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index d55b62d2..ee947957 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -72,7 +72,7 @@ identity_map: std.AutoHashMapUnmanaged(usize, js.Global(js.Object)) = .empty, // Some web APIs have to manage opaque values. Ideally, they use an // js.Object, but the js.Object has no lifetime guarantee beyond the // current call. They can call .persist() on their js.Object to get -// a `*PersistentObject()`. We need to track these to free them. +// a `Global(Object)`. We need to track these to free them. // This used to be a map and acted like identity_map; the key was // the @intFromPtr(js_obj.handle). But v8 can re-use address. Without // a reliable way to know if an object has already been persisted, @@ -526,15 +526,15 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp } // To turn a Zig instance into a v8 object, we need to do a number of things. -// First, if it's a struct, we need to put it on the heap +// First, if it's a struct, we need to put it on the heap. // Second, if we've already returned this instance, we should return // the same object. Hence, our executor maintains a map of Zig objects -// to v8.PersistentObject (the "identity_map"). +// to v8.Global(js.Object) (the "identity_map"). // Finally, if this is the first time we've seen this instance, we need to: // 1 - get the FunctionTemplate (from our templates slice) // 2 - Create the TaggedAnyOpaque so that, if needed, we can do the reverse // (i.e. js -> zig) -// 3 - Create a v8.PersistentObject (because Zig owns this object, not v8) +// 3 - Create a v8.Global(js.Object) (because Zig owns this object, not v8) // 4 - Store our TaggedAnyOpaque into the persistent object // 5 - Update our identity_map (so that, if we return this same instance again, // we can just grab it from the identity_map) diff --git a/src/browser/js/HandleScope.zig b/src/browser/js/HandleScope.zig index 37f7e549..b32eaf29 100644 --- a/src/browser/js/HandleScope.zig +++ b/src/browser/js/HandleScope.zig @@ -23,6 +23,10 @@ const HandleScope = @This(); handle: v8.HandleScope, +// V8 takes an address of the value that's passed in, so it needs to be stable. +// We can't create the v8.HandleScope here, pass it to v8 and then return the +// value, as v8 will then have taken the address of the function-scopped (and no +// longer valid) local. pub fn init(self: *HandleScope, isolate: js.Isolate) void { v8.v8__HandleScope__CONSTRUCT(&self.handle, isolate.handle); } diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index 71d54b87..67b06f64 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -154,7 +154,7 @@ pub fn contextCreated( // Retrieves the RemoteObject for a given value. // The value is loaded through the ExecutionWorld's mapZigInstanceToJs function, // just like a method return value. Therefore, if we've mapped this -// value before, we'll get the existing JS PersistedObject and if not +// value before, we'll get the existing js.Global(js.Object) and if not // we'll create it and track it for cleanup when the context ends. pub fn getRemoteObject( self: *const Inspector, diff --git a/src/browser/js/Name.zig b/src/browser/js/Name.zig index 90def5cb..45904d35 100644 --- a/src/browser/js/Name.zig +++ b/src/browser/js/Name.zig @@ -22,10 +22,3 @@ const v8 = js.v8; const Name = @This(); handle: *const v8.Name, - -pub fn toValue(self: Name) js.Value { - return .{ - .ctx = undefined, // Will be set by caller if needed - .handle = @ptrCast(self.handle), - }; -} diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index c60fe59d..2ff64b2a 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -23,7 +23,6 @@ const v8 = js.v8; const IS_DEBUG = @import("builtin").mode == .Debug; const Context = @import("Context.zig"); -const PersistentObject = v8.Persistent(v8.Object); const Allocator = std.mem.Allocator; @@ -71,16 +70,6 @@ pub fn set(self: Object, key: anytype, value: anytype, comptime opts: js.bridge. return out.has_value; } -pub fn setIndex(self: Object, key: u32, value: anytype, comptime opts: js.bridge.Caller.CallOpts) !bool { - const ctx = self.ctx; - - const js_value = try ctx.zigValueToJs(value, opts); - - var out: v8.MaybeBool = undefined; - v8.v8__Object__SetAtIndex(self.handle, ctx.handle, key, js_value.handle, &out); - return out.has_value; -} - pub fn defineOwnProperty(self: Object, name: []const u8, value: js.Value, attr: v8.PropertyAttribute) ?bool { const ctx = self.ctx; const name_handle = ctx.isolate.initStringHandle(name); diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index 9a5112e7..bd2280d9 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -53,7 +53,7 @@ startup_data: v8.StartupData, external_references: [countExternalReferences()]isize, // Track whether this snapshot owns its data (was created in-process) -// If false, the data points into embedded_snapshot_blob and should not be freed +// If false, the data points into embedded_snapshot_blob and will not be freed owns_data: bool = false, pub fn load() !Snapshot { @@ -106,7 +106,7 @@ pub fn write(self: Snapshot, writer: *std.Io.Writer) !void { pub fn fromEmbedded(self: Snapshot) bool { // if the snapshot comes from the embedFile, then it'll be flagged as not - // owneing (aka, not needing to free) the data. + // owning (aka, not needing to free) the data. return self.owns_data == false; } diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index a4e28fe4..2c81942d 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -168,7 +168,7 @@ pub fn toBool(self: Value) bool { pub fn typeOf(self: Value) js.String { const str_handle = v8.v8__Value__TypeOf(self.handle, self.ctx.isolate.handle).?; - return js.String{ .ctx = @constCast(self.ctx), .handle = str_handle }; + return js.String{ .ctx = self.ctx) .handle = str_handle }; } pub fn toF32(self: Value) !f32 { @@ -225,7 +225,7 @@ pub fn toJson(self: Value, allocator: Allocator) ![]u8 { } fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) { - const ctx: *js.Context = @constCast(self.ctx); + const ctx = self.ctx; if (self.isSymbol()) { const sym_handle = v8.v8__Symbol__Description(@ptrCast(self.handle), ctx.isolate.handle).?; @@ -253,7 +253,7 @@ pub fn (ctx: *js.Context, json: []const u8) !Value { } pub fn persist(self: Value) !Value { - var ctx: *js.Context = @constCast(self.ctx); + var ctx = self.ctx; const global = js.Global(Value).init(ctx.isolate.handle, self.handle); try ctx.global_values.append(ctx.arena, global); @@ -274,7 +274,7 @@ pub fn toObject(self: Value) js.Object { } return .{ - .ctx = @constCast(self.ctx), + .ctx = self.ctx, .handle = @ptrCast(self.handle), }; } @@ -285,7 +285,7 @@ pub fn toArray(self: Value) js.Array { } return .{ - .ctx = @constCast(self.ctx), + .ctx = self.ctx, .handle = @ptrCast(self.handle), }; } @@ -307,3 +307,15 @@ pub fn format(self: Value, writer: *std.Io.Writer) !void { const str = self.toString(.{}) catch return error.WriteFailed; return writer.writeAll(str); } + +pub fn persist(self: Value) !Value { + var ctx = self.ctx; + + const global = js.Global(Value).init(ctx.isolate.handle, self.handle); + try ctx.global_values.append(ctx.arena, global); + + return .{ + .ctx = ctx, + .handle = global.local(), + }; +} diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 4561a4f3..841d5642 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -86,84 +86,6 @@ pub const Exception = struct { } }; -pub fn UndefinedOr(comptime T: type) type { - return union(enum) { - undefined: void, - value: T, - }; -} - -// An interface for types that want to have their jsScopeEnd function be -// called when the call context ends -const CallScopeEndCallback = struct { - ptr: *anyopaque, - callScopeEndFn: *const fn (ptr: *anyopaque) void, - - fn init(ptr: anytype) CallScopeEndCallback { - const T = @TypeOf(ptr); - const ptr_info = @typeInfo(T); - - const gen = struct { - pub fn callScopeEnd(pointer: *anyopaque) void { - const self: T = @ptrCast(@alignCast(pointer)); - return ptr_info.pointer.child.jsCallScopeEnd(self); - } - }; - - return .{ - .ptr = ptr, - .callScopeEndFn = gen.callScopeEnd, - }; - } - - pub fn callScopeEnd(self: CallScopeEndCallback) void { - self.callScopeEndFn(self.ptr); - } -}; - -// Callback called on global's property missing. -// Return true to intercept the execution or false to let the call -// continue the chain. -pub const GlobalMissingCallback = struct { - ptr: *anyopaque, - missingFn: *const fn (ptr: *anyopaque, name: []const u8, ctx: *Context) bool, - - pub fn init(ptr: anytype) GlobalMissingCallback { - const T = @TypeOf(ptr); - const ptr_info = @typeInfo(T); - - const gen = struct { - pub fn missing(pointer: *anyopaque, name: []const u8, ctx: *Context) bool { - const self: T = @ptrCast(@alignCast(pointer)); - return ptr_info.pointer.child.missing(self, name, ctx); - } - }; - - return .{ - .ptr = ptr, - .missingFn = gen.missing, - }; - } - - pub fn missing(self: GlobalMissingCallback, name: []const u8, ctx: *Context) bool { - return self.missingFn(self.ptr, name, ctx); - } -}; - -// Attributes that return a primitive type are setup directly on the -// FunctionTemplate when the Env is setup. More complex types need a v8.Context -// and cannot be set directly on the FunctionTemplate. -// We default to saying types are primitives because that's mostly what -// we have. If we add a new complex type that isn't explictly handled here, -// we'll get a compiler error in simpleZigValueToJs, and can then explicitly -// add the type here. -pub fn isComplexAttributeType(ti: std.builtin.Type) bool { - return switch (ti) { - .array => true, - else => false, - }; -} - // These are simple types that we can convert to JS with only an isolate. This // is separated from the Caller's zigValueToJs to make it available when we // don't have a caller (i.e., when setting static attributes on types) @@ -293,17 +215,6 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, } return null; } - -pub fn classNameForStruct(comptime Struct: type) []const u8 { - if (@hasDecl(Struct, "js_name")) { - return Struct.js_name; - } - @setEvalBranchQuota(10_000); - const full_name = @typeName(Struct); - const last = std.mem.lastIndexOfScalar(u8, full_name, '.') orelse return full_name; - return full_name[last + 1 ..]; -} - // When we return a Zig object to V8, we put it on the heap and pass it into // v8 as an *anyopaque (i.e. void *). When V8 gives us back the value, say, as a // function parameter, we know what type it _should_ be. diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index 30f77234..51fbcf55 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -429,36 +429,6 @@ const TransferAsResponseWriter = struct { } }; -// @ZIGDOM - do we still need this? just send the full URL? -// const DocumentUrlWriter = struct { -// uri: *std.Uri, - -// fn init(uri: *std.Uri) DocumentUrlWriter { -// return .{ -// .uri = uri, -// }; -// } - -// pub fn jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { -// self._jsonStringify(jws) catch return error.WriteFailed; -// } -// fn _jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { -// const writer = jws.writer; - -// try jws.beginWriteRaw(); -// try writer.writeByte('\"'); -// try self.uri.writeToStream(writer, .{ -// .scheme = true, -// .authentication = true, -// .authority = true, -// .path = true, -// .query = true, -// }); -// try writer.writeByte('\"'); -// jws.endWriteRaw(); -// } -// }; - fn idFromRequestId(request_id: []const u8) !u64 { if (!std.mem.startsWith(u8, request_id, "REQ-")) { return error.InvalidParams; From 86f4ea108db8c3784b6a5477e5195399fdaa3762 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 5 Jan 2026 19:05:44 +0800 Subject: [PATCH 19/26] Store snapshot templates in isolate, not context. This lets us load the isolate without having to create a temp/dummy context just to get the templates. Call ContextDisposedNotification when a context is removed. Supposedly this can help/hint to the isolate about memory management. --- src/browser/js/Context.zig | 2 +- src/browser/js/Env.zig | 6 +----- src/browser/js/ExecutionWorld.zig | 1 + src/browser/js/Isolate.zig | 4 ++++ src/browser/js/Snapshot.zig | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index ee947957..69603345 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -166,8 +166,8 @@ pub fn deinit(self: *Context) void { } if (self.handle_scope) |*scope| { - scope.deinit(); v8.v8__Context__Exit(self.handle); + scope.deinit(); } } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 7962e66d..4effc3cd 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -93,14 +93,10 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot var temp_scope: js.HandleScope = undefined; temp_scope.init(isolate); defer temp_scope.deinit(); - const context_handle = isolate.createContextHandle(null, null); - - v8.v8__Context__Enter(context_handle); - defer v8.v8__Context__Exit(context_handle); inline for (JsApis, 0..) |JsApi, i| { JsApi.Meta.class_id = i; - const data = v8.v8__Context__GetDataFromSnapshotOnce(context_handle, snapshot.data_start + i); + const data = v8.v8__Isolate__GetDataFromSnapshotOnce(isolate.handle, snapshot.data_start + i); const function_handle: *const v8.FunctionTemplate = @ptrCast(data); // Make function template global/persistent v8.v8__Global__New(isolate.handle, @ptrCast(function_handle), &globals[i]); diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index b8bbbf02..bc016c00 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -158,6 +158,7 @@ pub fn removeContext(self: *ExecutionWorld) void { self.persisted_context.?.deinit(); self.persisted_context = null; + self.env.isolate.notifyContextDisposed(); _ = self.context_arena.reset(.{ .retain_with_limit = CONTEXT_ARENA_RETAIN }); } diff --git a/src/browser/js/Isolate.zig b/src/browser/js/Isolate.zig index 9b4ae1d1..dcfeb157 100644 --- a/src/browser/js/Isolate.zig +++ b/src/browser/js/Isolate.zig @@ -57,6 +57,10 @@ pub fn lowMemoryNotification(self: Isolate) void { v8.v8__Isolate__LowMemoryNotification(self.handle); } +pub fn notifyContextDisposed(self: Isolate) void { + _ = v8.v8__Isolate__ContextDisposedNotification(self.handle); +} + pub fn getHeapStatistics(self: Isolate) v8.HeapStatistics { var res: v8.HeapStatistics = undefined; v8.v8__Isolate__GetHeapStatistics(self.handle, &res); diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index bd2280d9..410f4771 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -179,7 +179,7 @@ pub fn create() !Snapshot { var last_data_index: usize = 0; inline for (JsApis, 0..) |_, i| { @setEvalBranchQuota(10_000); - const data_index = v8.v8__SnapshotCreator__AddData2(snapshot_creator, context, @ptrCast(templates[i])); + const data_index = v8.v8__SnapshotCreator__AddData(snapshot_creator, @ptrCast(templates[i])); if (i == 0) { data_start = data_index; last_data_index = data_index; From 1b0ea445196a73508b4ae46816c207ff6c199258 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 9 Jan 2026 18:02:18 +0800 Subject: [PATCH 20/26] merge main --- src/browser/js/Caller.zig | 519 ------------------------------ src/browser/js/ExecutionWorld.zig | 71 +--- src/browser/js/Function.zig | 10 +- src/browser/js/Inspector.zig | 4 +- src/browser/js/Isolate.zig | 8 - src/browser/js/Snapshot.zig | 27 +- src/browser/js/Value.zig | 24 +- src/browser/js/bridge.zig | 56 ++++ 8 files changed, 85 insertions(+), 634 deletions(-) delete mode 100644 src/browser/js/Caller.zig diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig deleted file mode 100644 index 63f24e22..00000000 --- a/src/browser/js/Caller.zig +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) -// -// Francis Bouvier -// Pierre Tachoire -// -// 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 . - -const std = @import("std"); - -const log = @import("../../log.zig"); - -const js = @import("js.zig"); -const v8 = js.v8; - -const Context = @import("Context.zig"); - -const Page = @import("../Page.zig"); - -const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; - -const CALL_ARENA_RETAIN = 1024 * 16; - -// Responsible for calling Zig functions from JS invocations. This could -// probably just contained in ExecutionWorld, but having this specific logic, which -// is somewhat repetitive between constructors, functions, getters, etc contained -// here does feel like it makes it cleaner. -const Caller = @This(); -context: *Context, -isolate: js.Isolate, -call_arena: Allocator, - -// info is a v8.PropertyCallbackInfo or a v8.FunctionCallback -// All we really want from it is the isolate. -// executor = Isolate -> getCurrentContext -> getEmbedderData() -pub fn init(info: anytype) Caller { - const v8_isolate = info.getIsolate(); - const isolate = js.Isolate{ .handle = v8_isolate.handle }; - const v8_context = v8_isolate.getCurrentContext(); - const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64()); - - context.call_depth += 1; - return .{ - .context = context, - .isolate = isolate, - .call_arena = context.call_arena, - }; -} - -pub fn deinit(self: *Caller) void { - const context = self.context; - const call_depth = context.call_depth - 1; - - // Because of callbacks, calls can be nested. Because of this, we - // can't clear the call_arena after _every_ call. Imagine we have - // arr.forEach((i) => { console.log(i); } - // - // First we call forEach. Inside of our forEach call, - // we call console.log. If we reset the call_arena after this call, - // it'll reset it for the `forEach` call after, which might still - // need the data. - // - // Therefore, we keep a call_depth, and only reset the call_arena - // when a top-level (call_depth == 0) function ends. - if (call_depth == 0) { - const arena: *ArenaAllocator = @ptrCast(@alignCast(context.call_arena.ptr)); - _ = arena.reset(.{ .retain_with_limit = CALL_ARENA_RETAIN }); - } - - context.call_depth = call_depth; -} - -pub const CallOpts = struct { - dom_exception: bool = false, - null_as_undefined: bool = false, - as_typed_array: bool = false, -}; - -pub fn constructor(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) void { - if (!info.isConstructCall()) { - self.handleError(T, @TypeOf(func), error.InvalidArgument, info, opts); - return; - } - self._constructor(func, info) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - }; -} -pub fn _constructor(self: *Caller, func: anytype, info: v8.FunctionCallbackInfo) !void { - const F = @TypeOf(func); - const args = try self.getArgs(F, 0, info); - const res = @call(.auto, func, args); - - const ReturnType = @typeInfo(F).@"fn".return_type orelse { - @compileError(@typeName(F) ++ " has a constructor without a return type"); - }; - - const new_this = info.getThis(); - var this = new_this; - if (@typeInfo(ReturnType) == .error_union) { - const non_error_res = res catch |err| return err; - this = (try self.context.mapZigInstanceToJs(this, non_error_res)).castToObject(); - } else { - this = (try self.context.mapZigInstanceToJs(this, res)).castToObject(); - } - - // If we got back a different object (existing wrapper), copy the prototype - // from new object. (this happens when we're upgrading an CustomElement) - if (this.handle != new_this.handle) { - const new_prototype = new_this.getPrototype(); - const v8_context = v8.Context{ .handle = self.context.handle }; - _ = this.setPrototype(v8_context, new_prototype.castTo(v8.Object)); - } - - info.getReturnValue().set(this); -} - -pub fn method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) void { - self._method(T, func, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - }; -} - -pub fn _method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void { - const F = @TypeOf(func); - var handle_scope: js.HandleScope = undefined; - handle_scope.init(self.isolate); - defer handle_scope.deinit(); - - var args = try self.getArgs(F, 1, info); - @field(args, "0") = try Context.typeTaggedAnyOpaque(*T, info.getThis()); - const res = @call(.auto, func, args); - info.getReturnValue().set(try self.context.zigValueToJs(res, opts)); -} - -pub fn function(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) void { - self._function(func, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - }; -} - -pub fn _function(self: *Caller, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void { - const F = @TypeOf(func); - const context = self.context; - const args = try self.getArgs(F, 0, info); - const res = @call(.auto, func, args); - info.getReturnValue().set(try context.zigValueToJs(res, opts)); -} - -pub fn getIndex(self: *Caller, comptime T: type, func: anytype, idx: u32, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) u8 { - return self._getIndex(T, func, idx, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; - }; -} - -pub fn _getIndex(self: *Caller, comptime T: type, func: anytype, idx: u32, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) !u8 { - const F = @TypeOf(func); - var args = try self.getArgs(F, 2, info); - @field(args, "0") = try Context.typeTaggedAnyOpaque(*T, info.getThis()); - @field(args, "1") = idx; - const ret = @call(.auto, func, args); - return self.handleIndexedReturn(T, F, true, ret, info, opts); -} - -pub fn getNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) u8 { - return self._getNamedIndex(T, func, name, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; - }; -} - -pub fn _getNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) !u8 { - const F = @TypeOf(func); - var args = try self.getArgs(F, 2, info); - @field(args, "0") = try Context.typeTaggedAnyOpaque(*T, info.getThis()); - @field(args, "1") = try self.nameToString(name); - const ret = @call(.auto, func, args); - return self.handleIndexedReturn(T, F, true, ret, info, opts); -} - -pub fn setNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, js_value: v8.Value, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) u8 { - return self._setNamedIndex(T, func, name, js_value, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; - }; -} - -pub fn _setNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, js_value: v8.Value, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) !u8 { - const F = @TypeOf(func); - var args: ParameterTypes(F) = undefined; - @field(args, "0") = try Context.typeTaggedAnyOpaque(*T, info.getThis()); - @field(args, "1") = try self.nameToString(name); - @field(args, "2") = try self.context.jsValueToZig(@TypeOf(@field(args, "2")), js_value); - if (@typeInfo(F).@"fn".params.len == 4) { - @field(args, "3") = self.context.page; - } - const ret = @call(.auto, func, args); - return self.handleIndexedReturn(T, F, false, ret, info, opts); -} - -pub fn deleteNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) u8 { - return self._deleteNamedIndex(T, func, name, info, opts) catch |err| { - self.handleError(T, @TypeOf(func), err, info, opts); - return v8.Intercepted.No; - }; -} - -pub fn _deleteNamedIndex(self: *Caller, comptime T: type, func: anytype, name: v8.Name, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) !u8 { - const F = @TypeOf(func); - var args: ParameterTypes(F) = undefined; - @field(args, "0") = try Context.typeTaggedAnyOpaque(*T, info.getThis()); - @field(args, "1") = try self.nameToString(name); - if (@typeInfo(F).@"fn".params.len == 3) { - @field(args, "2") = self.context.page; - } - const ret = @call(.auto, func, args); - return self.handleIndexedReturn(T, F, false, ret, info, opts); -} - -fn handleIndexedReturn(self: *Caller, comptime T: type, comptime F: type, comptime getter: bool, ret: anytype, info: v8.PropertyCallbackInfo, comptime opts: CallOpts) !u8 { - // need to unwrap this error immediately for when opts.null_as_undefined == true - // and we need to compare it to null; - const non_error_ret = switch (@typeInfo(@TypeOf(ret))) { - .error_union => |eu| blk: { - break :blk ret catch |err| { - // We can't compare err == error.NotHandled if error.NotHandled - // isn't part of the possible error set. So we first need to check - // if error.NotHandled is part of the error set. - if (isInErrorSet(error.NotHandled, eu.error_set)) { - if (err == error.NotHandled) { - return v8.Intercepted.No; - } - } - self.handleError(T, F, err, info, opts); - return v8.Intercepted.No; - }; - }, - else => ret, - }; - - if (comptime getter) { - info.getReturnValue().set(try self.context.zigValueToJs(non_error_ret, opts)); - } - return v8.Intercepted.Yes; -} - -fn isInErrorSet(err: anyerror, comptime T: type) bool { - inline for (@typeInfo(T).error_set.?) |e| { - if (err == @field(anyerror, e.name)) return true; - } - return false; -} - -fn namedSetOrDeleteCall(res: anytype, has_value: bool) !u8 { - if (@typeInfo(@TypeOf(res)) == .error_union) { - _ = try res; - } - if (has_value == false) { - return v8.Intercepted.No; - } - return v8.Intercepted.Yes; -} - -fn nameToString(self: *Caller, name: v8.Name) ![]const u8 { - return self.context.valueToString(.{ .handle = name.handle }, .{}); -} - -fn isSelfReceiver(comptime T: type, comptime F: type) bool { - return checkSelfReceiver(T, F, false); -} -fn assertSelfReceiver(comptime T: type, comptime F: type) void { - _ = checkSelfReceiver(T, F, true); -} -fn checkSelfReceiver(comptime T: type, comptime F: type, comptime fail: bool) bool { - const params = @typeInfo(F).@"fn".params; - if (params.len == 0) { - if (fail) { - @compileError(@typeName(F) ++ " must have a self parameter"); - } - return false; - } - - const first_param = params[0].type.?; - if (first_param != *T and first_param != *const T) { - if (fail) { - @compileError(std.fmt.comptimePrint("The first parameter to {s} must be a *{s} or *const {s}. Got: {s}", .{ - @typeName(F), - @typeName(T), - @typeName(T), - @typeName(first_param), - })); - } - return false; - } - return true; -} - -fn assertIsPageArg(comptime T: type, comptime F: type, index: comptime_int) void { - const param = @typeInfo(F).@"fn".params[index].type.?; - if (isPage(param)) { - return; - } - @compileError(std.fmt.comptimePrint("The {d} parameter of {s}.{s} must be a *Page or *const Page. Got: {s}", .{ index, @typeName(T), @typeName(F), @typeName(param) })); -} - -fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void { - const isolate = self.isolate; - const v8_isolate = v8.Isolate{ .handle = isolate.handle }; - - if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) { - if (log.enabled(.js, .warn)) { - self.logFunctionCallError(@typeName(T), @typeName(F), err, info); - } - } - - var js_err: ?v8.Value = switch (err) { - error.InvalidArgument => createTypeException(v8_isolate, "invalid argument"), - error.OutOfMemory => js._createException(v8_isolate, "out of memory"), - error.IllegalConstructor => js._createException(v8_isolate, "Illegal Contructor"), - else => blk: { - if (!comptime opts.dom_exception) { - break :blk null; - } - const DOMException = @import("../webapi/DOMException.zig"); - const ex = DOMException.fromError(err) orelse break :blk null; - break :blk self.context.zigValueToJs(ex, .{}) catch js._createException(v8_isolate, "internal error"); - }, - }; - - if (js_err == null) { - js_err = js._createException(v8_isolate, @errorName(err)); - } - const js_exception = isolate.throwException(js_err.?); - info.getReturnValue().setValueHandle(js_exception.handle); -} - -// If we call a method in javascript: cat.lives('nine'); -// -// Then we'd expect a Zig function with 2 parameters: a self and the string. -// In this case, offset == 1. Offset is always 1 for setters or methods. -// -// Offset is always 0 for constructors. -// -// For constructors, setters and methods, we can further increase offset + 1 -// if the first parameter is an instance of Page. -// -// Finally, if the JS function is called with _more_ parameters and -// the last parameter in Zig is an array, we'll try to slurp the additional -// parameters into the array. -fn getArgs(self: *const Caller, comptime F: type, comptime offset: usize, info: anytype) !ParameterTypes(F) { - const context = self.context; - var args: ParameterTypes(F) = undefined; - - const params = @typeInfo(F).@"fn".params[offset..]; - // Except for the constructor, the first parameter is always `self` - // This isn't something we'll bind from JS, so skip it. - const params_to_map = blk: { - if (params.len == 0) { - return args; - } - - // If the last parameter is the Page, set it, and exclude it - // from our params slice, because we don't want to bind it to - // a JS argument - if (comptime isPage(params[params.len - 1].type.?)) { - @field(args, tupleFieldName(params.len - 1 + offset)) = self.context.page; - break :blk params[0 .. params.len - 1]; - } - - // we have neither a Page nor a JsObject. All params must be - // bound to a JavaScript value. - break :blk params; - }; - - if (params_to_map.len == 0) { - return args; - } - - const js_parameter_count = info.length(); - const last_js_parameter = params_to_map.len - 1; - var is_variadic = false; - - { - // This is going to get complicated. If the last Zig parameter - // is a slice AND the corresponding javascript parameter is - // NOT an an array, then we'll treat it as a variadic. - - const last_parameter_type = params_to_map[params_to_map.len - 1].type.?; - const last_parameter_type_info = @typeInfo(last_parameter_type); - if (last_parameter_type_info == .pointer and last_parameter_type_info.pointer.size == .slice) { - const slice_type = last_parameter_type_info.pointer.child; - const corresponding_js_value = info.getArg(@as(u32, @intCast(last_js_parameter))); - if (corresponding_js_value.isArray() == false and corresponding_js_value.isTypedArray() == false and slice_type != u8) { - is_variadic = true; - if (js_parameter_count == 0) { - @field(args, tupleFieldName(params_to_map.len + offset - 1)) = &.{}; - } else if (js_parameter_count >= params_to_map.len) { - const arr = try self.call_arena.alloc(last_parameter_type_info.pointer.child, js_parameter_count - params_to_map.len + 1); - for (arr, last_js_parameter..) |*a, i| { - const js_value = info.getArg(@as(u32, @intCast(i))); - a.* = try context.jsValueToZig(slice_type, js_value); - } - @field(args, tupleFieldName(params_to_map.len + offset - 1)) = arr; - } else { - @field(args, tupleFieldName(params_to_map.len + offset - 1)) = &.{}; - } - } - } - } - - inline for (params_to_map, 0..) |param, i| { - const field_index = comptime i + offset; - if (comptime i == params_to_map.len - 1) { - if (is_variadic) { - break; - } - } - - if (comptime isPage(param.type.?)) { - @compileError("Page must be the last parameter (or 2nd last if there's a JsThis): " ++ @typeName(F)); - } else if (i >= js_parameter_count) { - if (@typeInfo(param.type.?) != .optional) { - return error.InvalidArgument; - } - @field(args, tupleFieldName(field_index)) = null; - } else { - const js_value = info.getArg(@as(u32, @intCast(i))); - @field(args, tupleFieldName(field_index)) = context.jsValueToZig(param.type.?, js_value) catch { - return error.InvalidArgument; - }; - } - } - - return args; -} - -// This is extracted to speed up compilation. When left inlined in handleError, -// this can add as much as 10 seconds of compilation time. -fn logFunctionCallError(self: *Caller, type_name: []const u8, func: []const u8, err: anyerror, info: v8.FunctionCallbackInfo) void { - const args_dump = self.serializeFunctionArgs(info) catch "failed to serialize args"; - log.info(.js, "function call error", .{ - .type = type_name, - .func = func, - .err = err, - .args = args_dump, - .stack = self.context.stackTrace() catch |err1| @errorName(err1), - }); -} - -fn serializeFunctionArgs(self: *Caller, info: v8.FunctionCallbackInfo) ![]const u8 { - const context = self.context; - var buf = std.Io.Writer.Allocating.init(context.call_arena); - - const separator = log.separator(); - for (0..info.length()) |i| { - try buf.writer.print("{s}{d} - ", .{ separator, i + 1 }); - try context.debugValue(info.getArg(@intCast(i)), &buf.writer); - } - return buf.written(); -} - -// Takes a function, and returns a tuple for its argument. Used when we -// @call a function -fn ParameterTypes(comptime F: type) type { - const params = @typeInfo(F).@"fn".params; - var fields: [params.len]std.builtin.Type.StructField = undefined; - - inline for (params, 0..) |param, i| { - fields[i] = .{ - .name = tupleFieldName(i), - .type = param.type.?, - .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(param.type.?), - }; - } - - return @Type(.{ .@"struct" = .{ - .layout = .auto, - .decls = &.{}, - .fields = &fields, - .is_tuple = true, - } }); -} - -fn tupleFieldName(comptime i: usize) [:0]const u8 { - return switch (i) { - 0 => "0", - 1 => "1", - 2 => "2", - 3 => "3", - 4 => "4", - 5 => "5", - 6 => "6", - 7 => "7", - 8 => "8", - 9 => "9", - else => std.fmt.comptimePrint("{d}", .{i}), - }; -} - -fn isPage(comptime T: type) bool { - return T == *Page or T == *const Page; -} - -fn createTypeException(isolate: v8.Isolate, msg: []const u8) v8.Value { - return v8.Exception.initTypeError(v8.String.initUtf8(isolate, msg)); -} diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index bc016c00..e6f08bf6 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -25,6 +25,7 @@ const js = @import("js.zig"); const v8 = js.v8; const Env = @import("Env.zig"); +const bridge = @import("bridge.zig"); const Context = @import("Context.zig"); const Page = @import("../Page.zig"); @@ -81,13 +82,11 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context temp_scope.init(isolate); defer temp_scope.deinit(); - // Getting this into the snapshot is tricky (anything involving the // global is tricky). Easier to do here - const func_tmpl_handle = isolate.createFunctionTemplateHandle(); - const global_template = v8.v8__FunctionTemplate__InstanceTemplate(func_tmpl_handle).?; - var configuration: v8.NamedPropertyHandlerConfiguration = .{ - .getter = unknownPropertyCallback, + const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate.handle, env.templates); + v8.v8__ObjectTemplate__SetNamedHandler(global_template, &.{ + .getter = bridge.unknownPropertyCallback, .setter = null, .query = null, .deleter = null, @@ -96,10 +95,9 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context .descriptor = null, .data = null, .flags = v8.kOnlyInterceptStrings | v8.kNonMasking, - }; - v8.v8__ObjectTemplate__SetNamedHandler(global_template, &configuration); + }); - const context_handle = isolate.createContextHandle(null, null); + const context_handle = v8.v8__Context__New(isolate.handle, global_template, null).?; break :blk js.Global(Context).init(isolate.handle, context_handle); }; @@ -169,60 +167,3 @@ pub fn terminateExecution(self: *const ExecutionWorld) void { pub fn resumeExecution(self: *const ExecutionWorld) void { self.env.isolate.cancelTerminateExecution(); } - - -pub fn unknownPropertyCallback(c_name: ?*const v8.Name, raw_info: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { - const isolate_handle = v8.v8__PropertyCallbackInfo__GetIsolate(raw_info).?; - const context = Context.fromIsolate(.{ .handle = isolate_handle }); - - const property: ?[]u8 = context.valueToString(.{ .ctx = context, .handle = c_name.? }, .{}) catch { - return v8.Intercepted.No; - }; - - const ignored = std.StaticStringMap(void).initComptime(.{ - .{ "process", {} }, - .{ "ShadyDOM", {} }, - .{ "ShadyCSS", {} }, - - .{ "litNonce", {} }, - .{ "litHtmlVersions", {} }, - .{ "litElementVersions", {} }, - .{ "litHtmlPolyfillSupport", {} }, - .{ "litElementHydrateSupport", {} }, - .{ "litElementPolyfillSupport", {} }, - .{ "reactiveElementVersions", {} }, - - .{ "recaptcha", {} }, - .{ "grecaptcha", {} }, - .{ "___grecaptcha_cfg", {} }, - .{ "__recaptcha_api", {} }, - .{ "__google_recaptcha_client", {} }, - - .{ "CLOSURE_FLAGS", {} }, - }); - - if (!ignored.has(property)) { - const page = context.page; - const document = page.document; - - if (document.getElementById(property, page)) |el| { - const js_value = context.zigValueToJs(el, .{}) catch { - return v8.Intercepted.No; - }; - - info.getReturnValue().set(js_value); - return v8.Intercepted.Yes; - } - - if (comptime IS_DEBUG) { - log.debug(.unknown_prop, "unknown global property", .{ - .info = "but the property can exist in pure JS", - .stack = context.stackTrace() catch "???", - .property = property, - }); - } - } - - // not intercepted - return 0; -} diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index a08222e8..61259fc9 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -116,15 +116,15 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args // need to increase the call_depth so that the call_arena remains valid for // the duration of the function call. If we don't do this, the call_arena // will be reset after each statement of the function which executes Zig code. - const call_depth = context.call_depth; - context.call_depth = call_depth + 1; - defer context.call_depth = call_depth; + const call_depth = ctx.call_depth; + ctx.call_depth = call_depth + 1; + defer ctx.call_depth = call_depth; const js_this = blk: { if (@TypeOf(this) == js.Object) { - break :blk this.js_obj; + break :blk this; } - break :blk try context.zigValueToJs(this, .{}); + break :blk try ctx.zigValueToJs(this, .{}); }; const aargs = if (comptime @typeInfo(@TypeOf(args)) == .null) struct {}{} else args; diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index 67b06f64..c518a4b9 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -97,8 +97,8 @@ pub fn init(self: *Inspector, isolate: *v8.Isolate, ctx: anytype) !void { pub fn deinit(self: *const Inspector) void { var temp_scope: v8.HandleScope = undefined; - v8.HandleScope.init(&temp_scope, self.isolate); - defer temp_scope.deinit(); + v8.v8__HandleScope__CONSTRUCT(&temp_scope, self.isolate); + defer v8.v8__HandleScope__DESTRUCT(&temp_scope); self.session.deinit(); self.client.deinit(); diff --git a/src/browser/js/Isolate.zig b/src/browser/js/Isolate.zig index dcfeb157..fdede915 100644 --- a/src/browser/js/Isolate.zig +++ b/src/browser/js/Isolate.zig @@ -113,14 +113,6 @@ pub fn initNumber(self: Isolate, val: anytype) js.Number { return js.Number.init(self.handle, val); } -pub fn createContextHandle(self: Isolate, global_tmpl: ?*const v8.ObjectTemplate, global_obj: ?*const v8.Value) *const v8.Context { - return v8.v8__Context__New(self.handle, global_tmpl, global_obj).?; -} - -pub fn createFunctionTemplateHandle(self: Isolate) *const v8.FunctionTemplate { - return v8.v8__FunctionTemplate__New__DEFAULT(self.handle).?; -} - pub fn createExternal(self: Isolate, val: *anyopaque) *const v8.External { return v8.v8__External__New(self.handle, val).?; } diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index 410f4771..c48c689e 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -114,19 +114,18 @@ fn isValid(self: Snapshot) bool { return v8.v8__StartupData__IsValid(self.startup_data); } -<<<<<<< HEAD -pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionTemplate) v8.ObjectTemplate { +pub fn createGlobalTemplate(isolate: *v8.Isolate, templates: anytype) *const v8.ObjectTemplate { // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance - const js_global = v8.c.v8__FunctionTemplate__New__DEFAULT(isolate); - const window_name = v8.c.v8__String__NewFromUtf8(isolate, "Window", v8.c.kNormal, 6); - v8.c.v8__FunctionTemplate__SetClassName(js_global, window_name); + const js_global = v8.v8__FunctionTemplate__New__DEFAULT(isolate); + const window_name = v8.v8__String__NewFromUtf8(isolate, "Window", v8.kNormal, 6); + v8.v8__FunctionTemplate__SetClassName(js_global, window_name); // Find Window in JsApis by name (avoids circular import) const window_index = comptime bridge.JsApiLookup.getId(Window.JsApi); - v8.c.v8__FunctionTemplate__Inherit(js_global, templates[window_index]); + v8.v8__FunctionTemplate__Inherit(js_global, templates[window_index]); - return v8.c.v8__FunctionTemplate__InstanceTemplate(js_global); + return v8.v8__FunctionTemplate__InstanceTemplate(js_global).?; } pub fn create() !Snapshot { @@ -406,8 +405,9 @@ fn generateConstructor(comptime JsApi: type, isolate: *v8.Isolate) *v8.FunctionT // Attaches JsApi members to the prototype template (normal case) fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.FunctionTemplate) void { - const target = v8.c.v8__FunctionTemplate__PrototypeTemplate(template); - const instance = v8.c.v8__FunctionTemplate__InstanceTemplate(template); + const target = v8.v8__FunctionTemplate__PrototypeTemplate(template); + const instance = v8.v8__FunctionTemplate__InstanceTemplate(template); + const declarations = @typeInfo(JsApi).@"struct".decls; inline for (declarations) |d| { @@ -495,13 +495,14 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio } if (@hasDecl(JsApi.Meta, "htmldda")) { - v8.c.v8__ObjectTemplate__MarkAsUndetectable(instance); - v8.c.v8__ObjectTemplate__SetCallAsFunctionHandler(instance, JsApi.Meta.callable.func); + v8.v8__ObjectTemplate__MarkAsUndetectable(instance); + v8.v8__ObjectTemplate__SetCallAsFunctionHandler(instance, JsApi.Meta.callable.func); } if (@hasDecl(JsApi.Meta, "name")) { - const js_name = v8.Symbol.getToStringTag(isolate).toName(); - instance.set(js_name, v8.String.initUtf8(isolate, JsApi.Meta.name), v8.PropertyAttribute.ReadOnly + v8.PropertyAttribute.DontDelete); + const js_name = v8.v8__Symbol__GetToStringTag(isolate); + const js_value = v8.v8__String__NewFromUtf8(isolate, JsApi.Meta.name.ptr, v8.kNormal, @intCast(JsApi.Meta.name.len)); + v8.v8__Template__Set(@ptrCast(instance), js_name, js_value, v8.ReadOnly + v8.DontDelete); } } diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 2c81942d..54415df0 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -42,14 +42,6 @@ pub fn isArray(self: Value) bool { return v8.v8__Value__IsArray(self.handle); } -pub fn isNull(self: Value) bool { - return self.js_val.isNull(); -} - -pub fn isUndefined(self: Value) bool { - return self.js_val.isUndefined(); -} - pub fn isSymbol(self: Value) bool { return v8.v8__Value__IsSymbol(self.handle); } @@ -168,7 +160,7 @@ pub fn toBool(self: Value) bool { pub fn typeOf(self: Value) js.String { const str_handle = v8.v8__Value__TypeOf(self.handle, self.ctx.isolate.handle).?; - return js.String{ .ctx = self.ctx) .handle = str_handle }; + return js.String{ .ctx = self.ctx, .handle = str_handle }; } pub fn toF32(self: Value) !f32 { @@ -265,7 +257,7 @@ pub fn persist(self: Value) !Value { } pub fn toZig(self: Value, comptime T: type) !T { - return self.context.jsValueToZig(T, self.js_val); + return self.ctx.jsValueToZig(T, self); } pub fn toObject(self: Value) js.Object { @@ -307,15 +299,3 @@ pub fn format(self: Value, writer: *std.Io.Writer) !void { const str = self.toString(.{}) catch return error.WriteFailed; return writer.writeAll(str); } - -pub fn persist(self: Value) !Value { - var ctx = self.ctx; - - const global = js.Global(Value).init(ctx.isolate.handle, self.handle); - try ctx.global_values.append(ctx.arena, global); - - return .{ - .ctx = ctx, - .handle = global.local(), - }; -} diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 2d2adace..c875ad34 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -892,6 +892,62 @@ pub const Property = union(enum) { int: i64, }; +pub fn unknownPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { + const isolate_handle = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; + const context = Context.fromIsolate(.{ .handle = isolate_handle }); + + const property: []const u8 = context.valueToString(.{ .ctx = context, .handle = c_name.? }, .{}) catch { + return 0; + }; + + const ignored = std.StaticStringMap(void).initComptime(.{ + .{ "process", {} }, + .{ "ShadyDOM", {} }, + .{ "ShadyCSS", {} }, + + .{ "litNonce", {} }, + .{ "litHtmlVersions", {} }, + .{ "litElementVersions", {} }, + .{ "litHtmlPolyfillSupport", {} }, + .{ "litElementHydrateSupport", {} }, + .{ "litElementPolyfillSupport", {} }, + .{ "reactiveElementVersions", {} }, + + .{ "recaptcha", {} }, + .{ "grecaptcha", {} }, + .{ "___grecaptcha_cfg", {} }, + .{ "__recaptcha_api", {} }, + .{ "__google_recaptcha_client", {} }, + + .{ "CLOSURE_FLAGS", {} }, + }); + + if (!ignored.has(property)) { + const page = context.page; + const document = page.document; + + if (document.getElementById(property, page)) |el| { + const js_value = context.zigValueToJs(el, .{}) catch { + return 0; + }; + var pc = PropertyCallbackInfo{ .handle = handle.? }; + pc.getReturnValue().set(js_value); + return 1; + } + + if (comptime IS_DEBUG) { + log.debug(.unknown_prop, "unknown global property", .{ + .info = "but the property can exist in pure JS", + .stack = context.stackTrace() catch "???", + .property = property, + }); + } + } + + // not intercepted + return 0; +} + // Given a Type, returns the length of the prototype chain, including self fn prototypeChainLength(comptime T: type) usize { var l: usize = 1; From 4e4a615df8fc614b6d084ca22d44499a4c352e8f Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 12 Jan 2026 10:49:43 +0800 Subject: [PATCH 21/26] Move Env's FunctionTemplate from Global -> Eternal (we'll move more to Eternal's, this is just a first teaser) --- build.zig.zon | 2 +- src/browser/js/Env.zig | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 805a294c..cc45cfb7 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,7 +7,7 @@ .dependencies = .{ .v8 = .{ .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/direct_v8.tar.gz", - .hash = "v8-0.0.0-xddH69smBABCCW8Q-9pislHtX8OolAmcuHk8QoTPx78F", + .hash = "v8-0.0.0-xddH6yMpBABN1G8VKVISuYT8LFi05tQmnfDGRkHyIpwD", }, //.v8 = .{ .path = "../zig-v8-fork" }, .@"boringssl-zig" = .{ diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 4effc3cd..ff00ffcb 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -54,7 +54,7 @@ isolate_params: *v8.CreateParams, context_id: usize, // Global handles that need to be freed on deinit -globals: []v8.Global, +eternal_function_templates: []v8.Eternal, // Dynamic slice to avoid circular dependency on JsApis.len at comptime templates: []*const v8.FunctionTemplate, @@ -83,8 +83,8 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot v8.v8__Isolate__SetHostInitializeImportMetaObjectCallback(isolate.handle, Context.metaObjectCallback); // Allocate arrays dynamically to avoid comptime dependency on JsApis.len - const globals = try allocator.alloc(v8.Global, JsApis.len); - errdefer allocator.free(globals); + const eternal_function_templates = try allocator.alloc(v8.Eternal, JsApis.len); + errdefer allocator.free(eternal_function_templates); const templates = try allocator.alloc(*const v8.FunctionTemplate, JsApis.len); errdefer allocator.free(templates); @@ -98,10 +98,12 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot JsApi.Meta.class_id = i; const data = v8.v8__Isolate__GetDataFromSnapshotOnce(isolate.handle, snapshot.data_start + i); const function_handle: *const v8.FunctionTemplate = @ptrCast(data); - // Make function template global/persistent - v8.v8__Global__New(isolate.handle, @ptrCast(function_handle), &globals[i]); + // Make function template eternal + v8.v8__Eternal__New(isolate.handle, @ptrCast(function_handle), &eternal_function_templates[i]); + // Extract the local handle from the global for easy access - templates[i] = @ptrCast(@alignCast(@as(*const anyopaque, @ptrFromInt(globals[i].data_ptr)))); + const eternal_ptr = v8.v8__Eternal__Get(&eternal_function_templates[i], isolate.handle); + templates[i] = @ptrCast(@alignCast(eternal_ptr.?)); } } @@ -110,19 +112,15 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot .isolate = isolate, .platform = platform, .allocator = allocator, - .globals = globals, .templates = templates, .isolate_params = params, + .eternal_function_templates = eternal_function_templates, }; } pub fn deinit(self: *Env) void { - // Free global handles before destroying the isolate - for (self.globals) |*global| { - v8.v8__Global__Reset(global); - } - self.allocator.free(self.globals); self.allocator.free(self.templates); + self.allocator.free(self.eternal_function_templates); self.isolate.exit(); self.isolate.deinit(); From d77a6620f3f15549e5b9cd75f78cfe91ffaaa8d2 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 13 Jan 2026 13:05:16 +0800 Subject: [PATCH 22/26] merge main --- src/browser/js/Value.zig | 2 +- src/browser/webapi/Console.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 54415df0..6b452bd8 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -236,7 +236,7 @@ fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOp } -pub fn (ctx: *js.Context, json: []const u8) !Value { +pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { const v8_isolate = v8.Isolate{ .handle = ctx.isolate.handle }; const json_string = v8.String.initUtf8(v8_isolate, json); const v8_context = v8.Context{ .handle = ctx.handle }; diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index 46412295..1f9b458e 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -54,7 +54,7 @@ pub fn warn(_: *const Console, values: []js.Value, page: *Page) void { pub fn clear(_: *const Console) void {} -pub fn assert(_: *const Console, assertion: js.Value, values: []js.Object, page: *Page) void { +pub fn assert(_: *const Console, assertion: js.Value, values: []js.Value, page: *Page) void { if (assertion.toBool()) { return; } From 801c019150fbe418568a602eaab7a863f4c8df87 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 13 Jan 2026 16:07:49 +0800 Subject: [PATCH 23/26] update v8 --- .github/actions/install/action.yml | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 51f7b2e2..3a00da65 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -13,7 +13,7 @@ inputs: zig-v8: description: 'zig v8 version to install' required: false - default: 'v0.2.2' + default: 'v0.2.3' v8: description: 'v8 version to install' required: false diff --git a/Dockerfile b/Dockerfile index e064621e..aee4842e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM debian:stable-slim ARG MINISIG=0.12 ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U ARG V8=14.0.365.4 -ARG ZIG_V8=v0.2.2 +ARG ZIG_V8=v0.2.3 ARG TARGETPLATFORM RUN apt-get update -yq && \ From c252c8e870b663b260f37f4dccaaf378cbfa8cab Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 13 Jan 2026 16:12:28 +0800 Subject: [PATCH 24/26] update v8 dep version --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index cc45cfb7..a44dd0b4 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,8 +6,8 @@ .minimum_zig_version = "0.15.2", .dependencies = .{ .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/direct_v8.tar.gz", - .hash = "v8-0.0.0-xddH6yMpBABN1G8VKVISuYT8LFi05tQmnfDGRkHyIpwD", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/5b0555e6b6154f957f9d7002ecb8005cc5a41b7a.tar.gz", + .hash = "v8-0.0.0-xddH6xUqBABofwwIBsof3cD3c2FstBvm7_VzoughX1Km", }, //.v8 = .{ .path = "../zig-v8-fork" }, .@"boringssl-zig" = .{ From 63f1c859645c18e5032c7b8855f9381debaf940e Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 13 Jan 2026 18:09:58 +0800 Subject: [PATCH 25/26] Remove unnecessary microtask run. This crashes linux in releasesafe without an embedded snapshot. Not sure why, but it shouldn't be necessary. This was added back when we were executing microtasks on a schedule, rather than manually at explicit points. --- src/browser/js/ExecutionWorld.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index e6f08bf6..c65fcaa0 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -143,12 +143,6 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context } pub fn removeContext(self: *ExecutionWorld) void { - // Force running the micro task to drain the queue before reseting the - // context arena. - // Tasks in the queue are relying to the arena memory could be present in - // the queue. Running them later could lead to invalid memory accesses. - self.env.runMicrotasks(); - var context = &(self.context orelse return); context.deinit(); self.context = null; From 223a6170d587479f55fcf4d8789fb16e6b73ad90 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 14 Jan 2026 09:37:10 +0800 Subject: [PATCH 26/26] Fix use-after free On CDP.BrowserContext.deinit, clear the isolated world ExecutionContext before terminating the session. This is important as the isolated_world list is allocated from the session.arena. Also, semi-revert 63f1c859645c18e5032c7b8855f9381debaf940e. Before all this we were running microtasks on ExecutionWorld.removeContext. That didn't seem right (and I thought it was the original source of the bug). But, for the "real" Page context, this is critical, since Microtasks can reference the Page object. Since microTasks are isolation-level, it's possible for a microtasks for Page1 to execute after Page1 goes away (if we create a new page, Page2). This re-adds the microtask "draining", but only for the Page (i.e. in Page.deinit). --- src/browser/Page.zig | 9 +++++---- src/cdp/cdp.zig | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 86374179..8e4e6806 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -204,10 +204,11 @@ pub fn deinit(self: *Page) void { // stats.print(&stream) catch unreachable; } - // removeContext() will execute the destructor of any type that - // registered a destructor (e.g. XMLHttpRequest). - // Should be called before we deinit the page, because these objects - // could be referencing it. + + // some MicroTasks might be referencing the page, we need to drain it while + // the page still exists + self.js.runMicrotasks(); + const session = self._session; session.executor.removeContext(); diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 3aef758a..b58ff9b2 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -411,15 +411,16 @@ pub fn BrowserContext(comptime CDP_T: type) type { transfer.abort(error.ClientDisconnect); } + for (self.isolated_worlds.items) |*world| { + world.deinit(); + } + self.isolated_worlds.clearRetainingCapacity(); + // If the session has a page, we need to clear it first. The page // context is always nested inside of the isolated world context, // so we need to shutdown the page one first. self.cdp.browser.closeSession(); - for (self.isolated_worlds.items) |*world| { - world.deinit(); - } - self.isolated_worlds.clearRetainingCapacity(); self.node_registry.deinit(); self.node_search_list.deinit(); self.cdp.browser.notification.unregisterAll(self);