Isolate and HandleScope

This commit is contained in:
Karl Seguin
2025-12-31 18:52:47 +08:00
parent ca5a385b51
commit 363b95bdef
9 changed files with 224 additions and 91 deletions

View File

@@ -39,15 +39,16 @@ const CALL_ARENA_RETAIN = 1024 * 16;
const Caller = @This(); const Caller = @This();
context: *Context, context: *Context,
v8_context: v8.Context, v8_context: v8.Context,
isolate: v8.Isolate, isolate: js.Isolate,
call_arena: Allocator, call_arena: Allocator,
// info is a v8.PropertyCallbackInfo or a v8.FunctionCallback // info is a v8.PropertyCallbackInfo or a v8.FunctionCallback
// All we really want from it is the isolate. // All we really want from it is the isolate.
// executor = Isolate -> getCurrentContext -> getEmbedderData() // executor = Isolate -> getCurrentContext -> getEmbedderData()
pub fn init(info: anytype) Caller { pub fn init(info: anytype) Caller {
const isolate = info.getIsolate(); const v8_isolate = info.getIsolate();
const v8_context = isolate.getCurrentContext(); 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()); const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
context.call_depth += 1; context.call_depth += 1;
@@ -133,7 +134,7 @@ pub fn method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionC
pub fn _method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void { pub fn _method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void {
const F = @TypeOf(func); const F = @TypeOf(func);
var handle_scope: v8.HandleScope = undefined; var handle_scope: js.HandleScope = undefined;
handle_scope.init(self.isolate); handle_scope.init(self.isolate);
defer handle_scope.deinit(); defer handle_scope.deinit();
@@ -316,6 +317,7 @@ fn assertIsPageArg(comptime T: type, comptime F: type, index: comptime_int) void
fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void { fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void {
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) { if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
if (log.enabled(.js, .warn)) { if (log.enabled(.js, .warn)) {
@@ -324,21 +326,21 @@ fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror,
} }
var js_err: ?v8.Value = switch (err) { var js_err: ?v8.Value = switch (err) {
error.InvalidArgument => createTypeException(isolate, "invalid argument"), error.InvalidArgument => createTypeException(v8_isolate, "invalid argument"),
error.OutOfMemory => js._createException(isolate, "out of memory"), error.OutOfMemory => js._createException(v8_isolate, "out of memory"),
error.IllegalConstructor => js._createException(isolate, "Illegal Contructor"), error.IllegalConstructor => js._createException(v8_isolate, "Illegal Contructor"),
else => blk: { else => blk: {
if (!comptime opts.dom_exception) { if (!comptime opts.dom_exception) {
break :blk null; break :blk null;
} }
const DOMException = @import("../webapi/DOMException.zig"); const DOMException = @import("../webapi/DOMException.zig");
const ex = DOMException.fromError(err) orelse break :blk null; const ex = DOMException.fromError(err) orelse break :blk null;
break :blk self.context.zigValueToJs(ex, .{}) catch js._createException(isolate, "internal error"); break :blk self.context.zigValueToJs(ex, .{}) catch js._createException(v8_isolate, "internal error");
}, },
}; };
if (js_err == null) { if (js_err == null) {
js_err = js._createException(isolate, @errorName(err)); js_err = js._createException(v8_isolate, @errorName(err));
} }
const js_exception = isolate.throwException(js_err.?); const js_exception = isolate.throwException(js_err.?);
info.getReturnValue().setValueHandle(js_exception.handle); info.getReturnValue().setValueHandle(js_exception.handle);

View File

@@ -43,10 +43,10 @@ const Context = @This();
id: usize, id: usize,
page: *Page, page: *Page,
isolate: v8.Isolate, isolate: js.Isolate,
// This context is a persistent object. The persistent needs to be recovered and reset. // This context is a persistent object. The persistent needs to be recovered and reset.
v8_context: v8.Context, v8_context: v8.Context,
handle_scope: ?v8.HandleScope, handle_scope: ?js.HandleScope,
cpu_profiler: ?v8.CpuProfiler = null, cpu_profiler: ?v8.CpuProfiler = null,
@@ -128,9 +128,10 @@ pub fn fromC(c_context: *const v8.C_Context) *Context {
return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64()); return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
} }
pub fn fromIsolate(isolate: v8.Isolate) *Context { pub fn fromIsolate(isolate: js.Isolate) *Context {
const v8_context = isolate.getCurrentContext(); const v8_context = v8.c.v8__Isolate__GetCurrentContext(isolate.handle).?;
return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64()); const ctx = v8.Context{ .handle = v8_context };
return @ptrFromInt(ctx.getEmbedderData(1).castTo(v8.BigInt).getUint64());
} }
pub fn setupGlobal(self: *Context) !void { pub fn setupGlobal(self: *Context) !void {
@@ -202,8 +203,9 @@ pub fn eval(self: *Context, src: []const u8, name: ?[]const u8) !void {
pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value { pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
const v8_context = self.v8_context; const v8_context = self.v8_context;
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const scr = try compileScript(self.isolate, v8_context, src, name); const scr = try compileScript(v8_isolate, v8_context, src, name);
const value = scr.run(v8_context) catch { const value = scr.run(v8_context) catch {
return error.ExecutionError; return error.ExecutionError;
@@ -213,6 +215,7 @@ pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
} }
pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url: []const u8, cacheable: bool) !(if (want_result) ModuleEntry else void) { pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url: []const u8, cacheable: bool) !(if (want_result) ModuleEntry else void) {
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const mod, const owned_url = blk: { const mod, const owned_url = blk: {
const arena = self.arena; const arena = self.arena;
@@ -231,13 +234,13 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
} }
const owned_url = try arena.dupeZ(u8, url); const owned_url = try arena.dupeZ(u8, url);
const m = try compileModule(self.isolate, src, owned_url); const m = try compileModule(v8_isolate, src, owned_url);
if (cacheable) { if (cacheable) {
// compileModule is synchronous - nothing can modify the cache during compilation // compileModule is synchronous - nothing can modify the cache during compilation
std.debug.assert(gop.value_ptr.module == null); std.debug.assert(gop.value_ptr.module == null);
gop.value_ptr.module = PersistentModule.init(self.isolate, m); gop.value_ptr.module = PersistentModule.init(v8_isolate, m);
if (!gop.found_existing) { if (!gop.found_existing) {
gop.key_ptr.* = owned_url; gop.key_ptr.* = owned_url;
} }
@@ -290,7 +293,7 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
std.debug.assert(entry.module != null); std.debug.assert(entry.module != null);
std.debug.assert(entry.module_promise == null); std.debug.assert(entry.module_promise == null);
entry.module_promise = PersistentPromise.init(self.isolate, .{ .handle = evaluated.handle }); entry.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
return if (comptime want_result) entry.* else {}; return if (comptime want_result) entry.* else {};
} }
@@ -309,7 +312,8 @@ pub fn stringToFunction(self: *Context, str: []const u8) !js.Function {
const full = try std.fmt.allocPrintSentinel(self.call_arena, "(function(e) {{ {s}{s} }})", .{ normalized, extra }, 0); const full = try std.fmt.allocPrintSentinel(self.call_arena, "(function(e) {{ {s}{s} }})", .{ normalized, extra }, 0);
const v8_context = self.v8_context; const v8_context = self.v8_context;
const script = try compileScript(self.isolate, v8_context, full, null); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const script = try compileScript(v8_isolate, v8_context, full, null);
const js_value = script.run(v8_context) catch { const js_value = script.run(v8_context) catch {
return error.ExecutionError; return error.ExecutionError;
}; };
@@ -391,17 +395,19 @@ pub fn createFunction(self: *Context, js_value: v8.Value) !js.Function {
} }
pub fn throw(self: *Context, err: []const u8) js.Exception { pub fn throw(self: *Context, err: []const u8) js.Exception {
const js_value = js._createException(self.isolate, err); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const js_value = js._createException(v8_isolate, err);
return self.createException(js_value); return self.createException(js_value);
} }
pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !v8.Value { pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !v8.Value {
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
// Check if it's a "simple" type. This is extracted so that it can be // Check if it's a "simple" type. This is extracted so that it can be
// reused by other parts of the code. "simple" types only require an // reused by other parts of the code. "simple" types only require an
// isolate to create (specifically, they don't our templates array) // isolate to create (specifically, they don't our templates array)
if (js.simpleZigValueToJs(isolate, value, false, opts.null_as_undefined)) |js_value| { if (js.simpleZigValueToJs(v8_isolate, value, false, opts.null_as_undefined)) |js_value| {
return js_value; return js_value;
} }
@@ -414,7 +420,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
unreachable; unreachable;
}, },
.array => { .array => {
var js_arr = v8.Array.init(isolate, value.len); var js_arr = v8.Array.init(v8_isolate, value.len);
var js_obj = js_arr.castTo(v8.Object); var js_obj = js_arr.castTo(v8.Object);
for (value, 0..) |v, i| { for (value, 0..) |v, i| {
const js_val = try self.zigValueToJs(v, opts); const js_val = try self.zigValueToJs(v, opts);
@@ -453,7 +459,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
// have handled it // have handled it
unreachable; unreachable;
} }
var js_arr = v8.Array.init(isolate, @intCast(value.len)); var js_arr = v8.Array.init(v8_isolate, @intCast(value.len));
var js_obj = js_arr.castTo(v8.Object); var js_obj = js_arr.castTo(v8.Object);
for (value, 0..) |v, i| { for (value, 0..) |v, i| {
@@ -504,7 +510,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
if (s.is_tuple) { if (s.is_tuple) {
// return the tuple struct as an array // return the tuple struct as an array
var js_arr = v8.Array.init(isolate, @intCast(s.fields.len)); var js_arr = v8.Array.init(v8_isolate, @intCast(s.fields.len));
var js_obj = js_arr.castTo(v8.Object); var js_obj = js_arr.castTo(v8.Object);
inline for (s.fields, 0..) |f, i| { inline for (s.fields, 0..) |f, i| {
const js_val = try self.zigValueToJs(@field(value, f.name), opts); const js_val = try self.zigValueToJs(@field(value, f.name), opts);
@@ -516,10 +522,10 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
} }
// return the struct as a JS object // return the struct as a JS object
const js_obj = v8.Object.init(isolate); const js_obj = v8.Object.init(v8_isolate);
inline for (s.fields) |f| { inline for (s.fields) |f| {
const js_val = try self.zigValueToJs(@field(value, f.name), opts); const js_val = try self.zigValueToJs(@field(value, f.name), opts);
const key = v8.String.initUtf8(isolate, f.name); const key = v8.String.initUtf8(v8_isolate, f.name);
if (!js_obj.setValue(v8_context, key, js_val)) { if (!js_obj.setValue(v8_context, key, js_val)) {
return error.CreateObjectFailure; return error.CreateObjectFailure;
} }
@@ -590,6 +596,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
} }
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
const JsApi = bridge.Struct(ptr.child).JsApi; const JsApi = bridge.Struct(ptr.child).JsApi;
// Sometimes we're creating a new v8.Object, like when // Sometimes we're creating a new v8.Object, like when
@@ -619,7 +626,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
// Skip setting internal field for the global object (Window) // Skip setting internal field for the global object (Window)
// Window accessors get the instance from context.page.window instead // Window accessors get the instance from context.page.window instead
if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) { if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) {
js_obj.setInternalField(0, v8.External.init(isolate, tao)); js_obj.setInternalField(0, v8.External.init(v8_isolate, tao));
} }
} else { } else {
// If the struct is empty, we don't need to do all // If the struct is empty, we don't need to do all
@@ -629,7 +636,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
// the type is empty and can create an empty instance. // the type is empty and can create an empty instance.
} }
const js_persistent = PersistentObject.init(isolate, js_obj); const js_persistent = PersistentObject.init(v8_isolate, js_obj);
gop.value_ptr.* = js_persistent; gop.value_ptr.* = js_persistent;
return js_persistent; return js_persistent;
}, },
@@ -638,6 +645,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
} }
pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T { pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T {
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.optional => |o| { .optional => |o| {
// If type type is a ?js.Value or a ?js.Object, then we want to pass // If type type is a ?js.Value or a ?js.Object, then we want to pass
@@ -680,7 +688,7 @@ pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T {
else => {}, else => {},
}, },
.int => return jsIntToZig(T, js_value, self.v8_context), .int => return jsIntToZig(T, js_value, self.v8_context),
.bool => return js_value.toBool(self.isolate), .bool => return js_value.toBool(v8_isolate),
.pointer => |ptr| switch (ptr.size) { .pointer => |ptr| switch (ptr.size) {
.one => { .one => {
if (!js_value.isObject()) { if (!js_value.isObject()) {
@@ -845,11 +853,12 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: v8.Value) !?T {
const js_obj = js_value.castTo(v8.Object); const js_obj = js_value.castTo(v8.Object);
const v8_context = self.v8_context; const v8_context = self.v8_context;
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
var value: T = undefined; var value: T = undefined;
inline for (@typeInfo(T).@"struct".fields) |field| { inline for (@typeInfo(T).@"struct".fields) |field| {
const name = field.name; const name = field.name;
const key = v8.String.initUtf8(isolate, name); const key = v8.String.initUtf8(v8_isolate, name);
if (js_obj.has(v8_context, key.toValue())) { if (js_obj.has(v8_context, key.toValue())) {
@field(value, name) = try self.jsValueToZig(field.type, try js_obj.getValue(v8_context, key)); @field(value, name) = try self.jsValueToZig(field.type, try js_obj.getValue(v8_context, key));
} else if (@typeInfo(field.type) == .optional) { } else if (@typeInfo(field.type) == .optional) {
@@ -1009,7 +1018,8 @@ pub fn valueToString(self: *const Context, js_val: v8.Value, opts: valueToString
const allocator = opts.allocator orelse self.call_arena; const allocator = opts.allocator orelse self.call_arena;
if (js_val.isSymbol()) { if (js_val.isSymbol()) {
const js_sym = v8.Symbol{ .handle = js_val.handle }; const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const js_sym_desc = js_sym.getDescription(v8_isolate);
return self.valueToString(js_sym_desc, .{}); return self.valueToString(js_sym_desc, .{});
} }
const str = try js_val.toString(self.v8_context); const str = try js_val.toString(self.v8_context);
@@ -1020,7 +1030,8 @@ pub fn valueToStringZ(self: *const Context, js_val: v8.Value, opts: valueToStrin
const allocator = opts.allocator orelse self.call_arena; const allocator = opts.allocator orelse self.call_arena;
if (js_val.isSymbol()) { if (js_val.isSymbol()) {
const js_sym = v8.Symbol{ .handle = js_val.handle }; const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const js_sym_desc = js_sym.getDescription(v8_isolate);
return self.valueToStringZ(js_sym_desc, .{}); return self.valueToStringZ(js_sym_desc, .{});
} }
const str = try js_val.toString(self.v8_context); const str = try js_val.toString(self.v8_context);
@@ -1032,18 +1043,20 @@ const JsStringToZigOpts = struct {
}; };
pub fn jsStringToZig(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![]u8 { pub fn jsStringToZig(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![]u8 {
const allocator = opts.allocator orelse self.call_arena; const allocator = opts.allocator orelse self.call_arena;
const len = str.lenUtf8(self.isolate); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const len = str.lenUtf8(v8_isolate);
const buf = try allocator.alloc(u8, len); const buf = try allocator.alloc(u8, len);
const n = str.writeUtf8(self.isolate, buf); const n = str.writeUtf8(v8_isolate, buf);
std.debug.assert(n == len); std.debug.assert(n == len);
return buf; return buf;
} }
pub fn jsStringToZigZ(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![:0]u8 { pub fn jsStringToZigZ(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![:0]u8 {
const allocator = opts.allocator orelse self.call_arena; const allocator = opts.allocator orelse self.call_arena;
const len = str.lenUtf8(self.isolate); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const len = str.lenUtf8(v8_isolate);
const buf = try allocator.allocSentinel(u8, len, 0); const buf = try allocator.allocSentinel(u8, len, 0);
const n = str.writeUtf8(self.isolate, buf); const n = str.writeUtf8(v8_isolate, buf);
std.debug.assert(n == len); std.debug.assert(n == len);
return buf; return buf;
} }
@@ -1073,13 +1086,14 @@ fn _debugValue(self: *const Context, js_val: v8.Value, seen: *std.AutoHashMapUnm
return writer.writeAll("false"); return writer.writeAll("false");
} }
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
if (js_val.isSymbol()) { if (js_val.isSymbol()) {
const js_sym = v8.Symbol{ .handle = js_val.handle }; const js_sym = v8.Symbol{ .handle = js_val.handle };
const js_sym_desc = js_sym.getDescription(self.isolate); const js_sym_desc = js_sym.getDescription(v8_isolate);
const js_sym_str = try self.valueToString(js_sym_desc, .{}); const js_sym_str = try self.valueToString(js_sym_desc, .{});
return writer.print("{s} (symbol)", .{js_sym_str}); return writer.print("{s} (symbol)", .{js_sym_str});
} }
const js_type = try self.jsStringToZig(try js_val.typeOf(self.isolate), .{}); const js_type = try self.jsStringToZig(try js_val.typeOf(v8_isolate), .{});
const js_val_str = try self.valueToString(js_val, .{}); const js_val_str = try self.valueToString(js_val, .{});
if (js_val_str.len > 2000) { if (js_val_str.len > 2000) {
try writer.writeAll(js_val_str[0..2000]); try writer.writeAll(js_val_str[0..2000]);
@@ -1142,20 +1156,21 @@ pub fn stackTrace(self: *const Context) !?[]const u8 {
} }
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
const separator = log.separator(); const separator = log.separator();
var buf: std.ArrayListUnmanaged(u8) = .empty; var buf: std.ArrayListUnmanaged(u8) = .empty;
var writer = buf.writer(self.call_arena); var writer = buf.writer(self.call_arena);
const stack_trace = v8.StackTrace.getCurrentStackTrace(isolate, 30); const stack_trace = v8.StackTrace.getCurrentStackTrace(v8_isolate, 30);
const frame_count = stack_trace.getFrameCount(); const frame_count = stack_trace.getFrameCount();
if (v8.StackTrace.getCurrentScriptNameOrSourceUrl(isolate)) |script| { if (v8.StackTrace.getCurrentScriptNameOrSourceUrl(v8_isolate)) |script| {
try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) }); try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) });
} }
for (0..frame_count) |i| { for (0..frame_count) |i| {
const frame = stack_trace.getFrame(isolate, @intCast(i)); const frame = stack_trace.getFrame(v8_isolate, @intCast(i));
if (frame.getScriptName()) |name| { if (frame.getScriptName()) |name| {
const script = try self.jsStringToZig(name, .{}); const script = try self.jsStringToZig(name, .{});
try writer.print("{s}{s}:{d}", .{ separator, script, frame.getLineNumber() }); try writer.print("{s}{s}:{d}", .{ separator, script, frame.getLineNumber() });
@@ -1214,7 +1229,8 @@ pub fn createPromiseResolver(self: *Context, comptime lifetime: PromiseResolverL
return .{ .context = self, .resolver = resolver }; return .{ .context = self, .resolver = resolver };
} }
const persisted = v8.Persistent(v8.PromiseResolver).init(self.isolate, resolver); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const persisted = v8.Persistent(v8.PromiseResolver).init(v8_isolate, resolver);
if (comptime lifetime == .page) { if (comptime lifetime == .page) {
try self.persisted_promise_resolvers.append(self.arena, persisted); try self.persisted_promise_resolvers.append(self.arena, persisted);
@@ -1305,7 +1321,8 @@ pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_
return; return;
}; };
const js_key = v8.String.initUtf8(self.isolate, "url"); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const js_key = v8.String.initUtf8(v8_isolate, "url");
const js_value = try self.zigValueToJs(url, .{}); const js_value = try self.zigValueToJs(url, .{});
const res = meta.defineOwnProperty(self.v8_context, js_key.toName(), js_value, 0) orelse false; const res = meta.defineOwnProperty(self.v8_context, js_key.toName(), js_value, 0) orelse false;
if (!res) { if (!res) {
@@ -1337,9 +1354,10 @@ fn _resolveModuleCallback(self: *Context, referrer: v8.Module, specifier: [:0]co
try_catch.init(self); try_catch.init(self);
defer try_catch.deinit(); defer try_catch.deinit();
const mod = try compileModule(self.isolate, source.src(), normalized_specifier); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const mod = try compileModule(v8_isolate, source.src(), normalized_specifier);
try self.postCompileModule(mod, normalized_specifier); try self.postCompileModule(mod, normalized_specifier);
entry.module = PersistentModule.init(self.isolate, mod); entry.module = PersistentModule.init(v8_isolate, mod);
return entry.module.?.castToModule().handle; return entry.module.?.castToModule().handle;
} }
@@ -1357,6 +1375,7 @@ const DynamicModuleResolveState = struct {
fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !v8.Promise { fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !v8.Promise {
const isolate = self.isolate; const isolate = self.isolate;
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
const gop = try self.module_cache.getOrPut(self.arena, specifier); const gop = try self.module_cache.getOrPut(self.arena, specifier);
if (gop.found_existing and gop.value_ptr.resolver_promise != null) { if (gop.found_existing and gop.value_ptr.resolver_promise != null) {
// This is easy, there's already something responsible // This is easy, there's already something responsible
@@ -1365,7 +1384,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
return gop.value_ptr.resolver_promise.?.castToPromise(); return gop.value_ptr.resolver_promise.?.castToPromise();
} }
const persistent_resolver = v8.Persistent(v8.PromiseResolver).init(isolate, v8.PromiseResolver.init(self.v8_context)); const persistent_resolver = v8.Persistent(v8.PromiseResolver).init(v8_isolate, v8.PromiseResolver.init(self.v8_context));
try self.persisted_promise_resolvers.append(self.arena, persistent_resolver); try self.persisted_promise_resolvers.append(self.arena, persistent_resolver);
var resolver = persistent_resolver.castToPromiseResolver(); var resolver = persistent_resolver.castToPromiseResolver();
@@ -1379,7 +1398,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
.resolver = persistent_resolver, .resolver = persistent_resolver,
}; };
const persisted_promise = PersistentPromise.init(self.isolate, resolver.getPromise()); const persisted_promise = PersistentPromise.init(v8_isolate, resolver.getPromise());
const promise = persisted_promise.castToPromise(); const promise = persisted_promise.castToPromise();
if (!gop.found_existing) { if (!gop.found_existing) {
@@ -1397,7 +1416,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
// Next, we need to actually load it. // Next, we need to actually load it.
self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| { self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| {
const error_msg = v8.String.initUtf8(isolate, @errorName(err)); const error_msg = v8.String.initUtf8(v8_isolate, @errorName(err));
_ = resolver.reject(self.v8_context, error_msg.toValue()); _ = resolver.reject(self.v8_context, error_msg.toValue());
}; };
@@ -1425,21 +1444,21 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
if (status == .kEvaluated or status == .kEvaluating) { if (status == .kEvaluated or status == .kEvaluating) {
// Module was already evaluated (shouldn't normally happen, but handle it). // Module was already evaluated (shouldn't normally happen, but handle it).
// Create a pre-resolved promise with the module namespace. // Create a pre-resolved promise with the module namespace.
const persisted_module_resolver = v8.Persistent(v8.PromiseResolver).init(isolate, v8.PromiseResolver.init(self.v8_context)); const persisted_module_resolver = v8.Persistent(v8.PromiseResolver).init(v8_isolate, v8.PromiseResolver.init(self.v8_context));
try self.persisted_promise_resolvers.append(self.arena, persisted_module_resolver); try self.persisted_promise_resolvers.append(self.arena, persisted_module_resolver);
var module_resolver = persisted_module_resolver.castToPromiseResolver(); var module_resolver = persisted_module_resolver.castToPromiseResolver();
_ = module_resolver.resolve(self.v8_context, mod.getModuleNamespace()); _ = module_resolver.resolve(self.v8_context, mod.getModuleNamespace());
gop.value_ptr.module_promise = PersistentPromise.init(self.isolate, module_resolver.getPromise()); gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, module_resolver.getPromise());
} else { } else {
// the module was loaded, but not evaluated, we _have_ to evaluate it now // the module was loaded, but not evaluated, we _have_ to evaluate it now
const evaluated = mod.evaluate(self.v8_context) catch { const evaluated = mod.evaluate(self.v8_context) catch {
std.debug.assert(status == .kErrored); std.debug.assert(status == .kErrored);
const error_msg = v8.String.initUtf8(isolate, "Module evaluation failed"); const error_msg = v8.String.initUtf8(v8_isolate, "Module evaluation failed");
_ = resolver.reject(self.v8_context, error_msg.toValue()); _ = resolver.reject(self.v8_context, error_msg.toValue());
return promise; return promise;
}; };
std.debug.assert(evaluated.isPromise()); std.debug.assert(evaluated.isPromise());
gop.value_ptr.module_promise = PersistentPromise.init(self.isolate, .{ .handle = evaluated.handle }); gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
} }
} }
@@ -1459,7 +1478,8 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
var self = state.context; var self = state.context;
var ms = module_source_ catch |err| { var ms = module_source_ catch |err| {
const error_msg = v8.String.initUtf8(self.isolate, @errorName(err)); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const error_msg = v8.String.initUtf8(v8_isolate, @errorName(err));
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue()); _ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
return; return;
}; };
@@ -1479,7 +1499,8 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
.stack = try_catch.stack(self.call_arena) catch null, .stack = try_catch.stack(self.call_arena) catch null,
.line = try_catch.sourceLineNumber() orelse 0, .line = try_catch.sourceLineNumber() orelse 0,
}); });
const error_msg = v8.String.initUtf8(self.isolate, ex); const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
const error_msg = v8.String.initUtf8(v8_isolate, ex);
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue()); _ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
return; return;
}; };
@@ -1492,7 +1513,8 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
defer self.runMicrotasks(); defer self.runMicrotasks();
const ctx = self.v8_context; const ctx = self.v8_context;
const isolate = self.isolate; const isolate = self.isolate;
const external = v8.External.init(self.isolate, @ptrCast(state)); const v8_isolate = v8.Isolate{ .handle = isolate.handle };
const external = v8.External.init(v8_isolate, @ptrCast(state));
// we can only be here if the module has been evaluated and if // we can only be here if the module has been evaluated and if
// we have a resolve loading this asynchronously. // we have a resolve loading this asynchronously.
@@ -1551,7 +1573,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
.err = err, .err = err,
.specifier = state.specifier, .specifier = state.specifier,
}); });
const error_msg = v8.String.initUtf8(isolate, "Failed to evaluate promise"); const error_msg = v8.String.initUtf8(v8_isolate, "Failed to evaluate promise");
_ = state.resolver.castToPromiseResolver().reject(ctx, error_msg.toValue()); _ = state.resolver.castToPromiseResolver().reject(ctx, error_msg.toValue());
}; };
} }
@@ -1582,7 +1604,8 @@ pub fn typeTaggedAnyOpaque(comptime R: type, js_obj: v8.Object) !R {
// Normally, this would be an error. All JsObject that map to a Zig type // 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 // are either `empty_with_no_proto` (handled above) or have an
// interalFieldCount. The only exception to that is the Window... // interalFieldCount. The only exception to that is the Window...
const isolate = js_obj.getIsolate(); const v8_isolate = js_obj.getIsolate();
const isolate = js.Isolate{ .handle = v8_isolate.handle };
const context = fromIsolate(isolate); const context = fromIsolate(isolate);
const Window = @import("../webapi/Window.zig"); const Window = @import("../webapi/Window.zig");
@@ -2024,7 +2047,7 @@ pub fn queueSlotchangeDelivery(self: *Context) !void {
} }
pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void { pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
self.isolate.enqueueMicrotaskFunc(.{ .handle = cb.handle }); self.isolate.enqueueMicrotaskFunc(cb);
} }
// == Misc == // == Misc ==

View File

@@ -46,7 +46,7 @@ allocator: Allocator,
platform: *const Platform, platform: *const Platform,
// the global isolate // the global isolate
isolate: v8.Isolate, isolate: js.Isolate,
// just kept around because we need to free it on deinit // just kept around because we need to free it on deinit
isolate_params: *v8.CreateParams, isolate_params: *v8.CreateParams,
@@ -67,28 +67,30 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot
params.external_references = &snapshot.external_references; params.external_references = &snapshot.external_references;
var isolate = v8.Isolate.init(params); var v8_isolate = v8.Isolate.init(params);
errdefer isolate.deinit(); errdefer v8_isolate.deinit();
// This is the callback that runs whenever a module is dynamically imported. // This is the callback that runs whenever a module is dynamically imported.
isolate.setHostImportModuleDynamicallyCallback(Context.dynamicModuleCallback); v8_isolate.setHostImportModuleDynamicallyCallback(Context.dynamicModuleCallback);
isolate.setPromiseRejectCallback(promiseRejectCallback); v8_isolate.setPromiseRejectCallback(promiseRejectCallback);
isolate.setMicrotasksPolicy(v8.c.kExplicit); v8_isolate.setMicrotasksPolicy(v8.c.kExplicit);
isolate.enter(); v8_isolate.enter();
errdefer isolate.exit(); errdefer v8_isolate.exit();
isolate.setHostInitializeImportMetaObjectCallback(Context.metaObjectCallback); v8_isolate.setHostInitializeImportMetaObjectCallback(Context.metaObjectCallback);
const isolate = js.Isolate{ .handle = v8_isolate.handle };
// Allocate templates array dynamically to avoid comptime dependency on JsApis.len // Allocate templates array dynamically to avoid comptime dependency on JsApis.len
const templates = try allocator.alloc(v8.FunctionTemplate, JsApis.len); const templates = try allocator.alloc(v8.FunctionTemplate, JsApis.len);
errdefer allocator.free(templates); errdefer allocator.free(templates);
{ {
var temp_scope: v8.HandleScope = undefined; var temp_scope: js.HandleScope = undefined;
v8.HandleScope.init(&temp_scope, isolate); temp_scope.init(isolate);
defer temp_scope.deinit(); defer temp_scope.deinit();
const context = v8.Context.init(isolate, null, null); const context = v8.Context.init(v8_isolate, null, null);
context.enter(); context.enter();
defer context.exit(); defer context.exit();
@@ -97,7 +99,7 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot
JsApi.Meta.class_id = i; JsApi.Meta.class_id = i;
const data = context.getDataFromSnapshotOnce(snapshot.data_start + i); const data = context.getDataFromSnapshotOnce(snapshot.data_start + i);
const function = v8.FunctionTemplate{ .handle = @ptrCast(data) }; const function = v8.FunctionTemplate{ .handle = @ptrCast(data) };
templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, function).castToFunctionTemplate(); templates[i] = v8.Persistent(v8.FunctionTemplate).init(v8_isolate, function).castToFunctionTemplate();
} }
} }
@@ -149,8 +151,8 @@ pub fn newExecutionWorld(self: *Env) !ExecutionWorld {
// `lowMemoryNotification` call on the isolate to encourage v8 to free // `lowMemoryNotification` call on the isolate to encourage v8 to free
// any contexts which have been freed. // any contexts which have been freed.
pub fn lowMemoryNotification(self: *Env) void { pub fn lowMemoryNotification(self: *Env) void {
var handle_scope: v8.HandleScope = undefined; var handle_scope: js.HandleScope = undefined;
v8.HandleScope.init(&handle_scope, self.isolate); handle_scope.init(self.isolate);
defer handle_scope.deinit(); defer handle_scope.deinit();
self.isolate.lowMemoryNotification(); self.isolate.lowMemoryNotification();
} }
@@ -178,8 +180,9 @@ pub fn dumpMemoryStats(self: *Env) void {
fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void { fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void {
const msg = v8.PromiseRejectMessage.initFromC(v8_msg); const msg = v8.PromiseRejectMessage.initFromC(v8_msg);
const isolate = msg.getPromise().toObject().getIsolate(); const v8_isolate = msg.getPromise().toObject().getIsolate();
const context = Context.fromIsolate(isolate); const js_isolate = js.Isolate{ .handle = v8_isolate.handle };
const context = Context.fromIsolate(js_isolate);
const value = const value =
if (msg.getValue()) |v8_value| if (msg.getValue()) |v8_value|

View File

@@ -64,7 +64,7 @@ pub fn deinit(self: *ExecutionWorld) void {
} }
// Only the top Context in the Main ExecutionWorld should hold a handle_scope. // Only the top Context in the Main ExecutionWorld should hold a handle_scope.
// A v8.HandleScope is like an arena. Once created, any "Local" that // A js.HandleScope is like an arena. Once created, any "Local" that
// v8 creates will be released (or at least, releasable by the v8 GC) // v8 creates will be released (or at least, releasable by the v8 GC)
// when the handle_scope is freed. // when the handle_scope is freed.
// We also maintain our own "context_arena" which allows us to have // We also maintain our own "context_arena" which allows us to have
@@ -77,19 +77,20 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
const arena = self.context_arena.allocator(); const arena = self.context_arena.allocator();
var v8_context: v8.Context = blk: { var v8_context: v8.Context = blk: {
var temp_scope: v8.HandleScope = undefined; const v8_isolate = v8.Isolate{ .handle = isolate.handle };
v8.HandleScope.init(&temp_scope, isolate); var temp_scope: js.HandleScope = undefined;
temp_scope.init(isolate);
defer temp_scope.deinit(); defer temp_scope.deinit();
// Creates a global template that inherits from Window. // Creates a global template that inherits from Window.
const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate, env.templates); const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate, env.templates);
// Add the named property handler // Add the named property handler
global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{ global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{
.getter = unknownPropertyCallback, .getter = unknownPropertyCallback,
.flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings, .flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings,
}, null); }, null);
const context_local = v8.Context.init(isolate, global_template, null); const context_local = v8.Context.init(isolate, global_template, null);
const v8_context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext(); const v8_context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext();
break :blk v8_context; break :blk v8_context;
@@ -98,10 +99,10 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
// 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. // 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 // 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 // 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
var handle_scope: ?v8.HandleScope = null; var handle_scope: ?js.HandleScope = null;
if (enter) { if (enter) {
handle_scope = @as(v8.HandleScope, undefined); handle_scope = @as(js.HandleScope, undefined);
v8.HandleScope.init(&handle_scope.?, isolate); handle_scope.?.init(isolate);
v8_context.enter(); v8_context.enter();
} }
errdefer if (enter) { errdefer if (enter) {
@@ -127,7 +128,8 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
var context = &self.context.?; var context = &self.context.?;
// Store a pointer to our context inside the v8 context so that, given // Store a pointer to our context inside the v8 context so that, given
// a v8 context, we can get our context out // a v8 context, we can get our context out
const data = isolate.initBigIntU64(@intCast(@intFromPtr(context))); const v8_isolate = v8.Isolate{ .handle = isolate.handle };
const data = v8_isolate.initBigIntU64(@intCast(@intFromPtr(context)));
v8_context.setEmbedderData(1, data); v8_context.setEmbedderData(1, data);
try context.setupGlobal(); try context.setupGlobal();
@@ -156,8 +158,8 @@ pub fn resumeExecution(self: *const ExecutionWorld) void {
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 info = v8.PropertyCallbackInfo.initFromV8(raw_info); const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
const context = Context.fromIsolate(info.getIsolate()); const context = Context.fromIsolate(info.getIsolate());
const maybe_property: ?[]u8 = context.valueToString(.{ .handle = c_name.? }, .{}) catch null; const maybe_property: ?[]u8 = context.valueToString(.{ .handle = c_name.? }, .{}) catch null;
const ignored = std.StaticStringMap(void).initComptime(.{ const ignored = std.StaticStringMap(void).initComptime(.{
@@ -196,6 +198,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C
return v8.Intercepted.Yes; return v8.Intercepted.Yes;
} }
if (comptime IS_DEBUG) {
log.debug(.unknown_prop, "unknown global property", .{ log.debug(.unknown_prop, "unknown global property", .{
.info = "but the property can exist in pure JS", .info = "but the property can exist in pure JS",
.stack = context.stackTrace() catch "???", .stack = context.stackTrace() catch "???",
@@ -203,6 +206,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C
}); });
} }
} }
}
return v8.Intercepted.No; return v8.Intercepted.No;
} }

View File

@@ -180,7 +180,8 @@ pub fn src(self: *const Function) ![]const u8 {
pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value { pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
const ctx = self.ctx; const ctx = self.ctx;
const key = v8.String.initUtf8(ctx.isolate, name); const v8_isolate = v8.Isolate{ .handle = ctx.isolate.handle };
const key = v8.String.initUtf8(v8_isolate, name);
const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse { const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse {
return error.JsException; return error.JsException;
}; };

View File

@@ -0,0 +1,32 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("js.zig");
const v8 = js.v8;
const HandleScope = @This();
inner: v8.c.HandleScope,
pub fn init(self: *HandleScope, isolate: js.Isolate) void {
v8.c.v8__HandleScope__CONSTRUCT(&self.inner, isolate.handle);
}
pub fn deinit(self: *HandleScope) void {
v8.c.v8__HandleScope__DESTRUCT(&self.inner);
}

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("js.zig");
const v8 = js.v8;
const Isolate = @This();
handle: *v8.c.Isolate,
pub fn deinit(self: Isolate) void {
v8.c.v8__Isolate__Dispose(self.handle);
}
pub fn exit(self: Isolate) void {
v8.c.v8__Isolate__Exit(self.handle);
}
pub fn performMicrotasksCheckpoint(self: Isolate) void {
v8.c.v8__Isolate__PerformMicrotaskCheckpoint(self.handle);
}
pub fn enqueueMicrotask(self: Isolate, callback: anytype, data: anytype) void {
const v8_isolate = v8.Isolate{ .handle = self.handle };
v8_isolate.enqueueMicrotask(callback, data);
}
pub fn enqueueMicrotaskFunc(self: Isolate, function: js.Function) void {
v8.c.v8__Isolate__EnqueueMicrotaskFunc(self.handle, function.handle);
}
pub fn lowMemoryNotification(self: Isolate) void {
v8.c.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);
return res;
}
pub fn throwException(self: Isolate, value: anytype) v8.Value {
const handle = switch (@TypeOf(value)) {
v8.Value => value.handle,
else => @compileError("Unsupported type for throwException"),
};
return .{
.handle = v8.c.v8__Isolate__ThrowException(self.handle, handle).?,
};
}

View File

@@ -85,7 +85,8 @@ pub fn toBool(self: Value) bool {
} }
pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { pub fn fromJson(ctx: *js.Context, json: []const u8) !Value {
const json_string = v8.String.initUtf8(ctx.isolate, json); const v8_isolate = v8.Isolate{ .handle = ctx.isolate.handle };
const json_string = v8.String.initUtf8(v8_isolate, json);
const value = try v8.Json.parse(ctx.v8_context, json_string); const value = try v8.Json.parse(ctx.v8_context, json_string);
return .{ .ctx = ctx, .handle = value.handle }; return .{ .ctx = ctx, .handle = value.handle };
} }

View File

@@ -28,6 +28,8 @@ pub const Context = @import("Context.zig");
pub const Inspector = @import("Inspector.zig"); pub const Inspector = @import("Inspector.zig");
pub const Snapshot = @import("Snapshot.zig"); pub const Snapshot = @import("Snapshot.zig");
pub const Platform = @import("Platform.zig"); pub const Platform = @import("Platform.zig");
pub const Isolate = @import("Isolate.zig");
pub const HandleScope = @import("HandleScope.zig");
pub const Value = @import("Value.zig"); pub const Value = @import("Value.zig");
pub const Array = @import("Array.zig"); pub const Array = @import("Array.zig");