migrate most cases, merge Caller into bridge

This commit is contained in:
Karl Seguin
2026-01-01 16:26:18 +08:00
parent 01ecd725b8
commit bc11a48e6b
12 changed files with 1079 additions and 349 deletions

View File

@@ -22,7 +22,533 @@ const log = @import("../../log.zig");
const v8 = js.v8;
const Caller = @import("Caller.zig");
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;
// ============================================================================
// Internal Callback Info Wrappers
// ============================================================================
// These wrap the raw v8 C API to provide a cleaner interface.
// They are not exported - internal to this module only.
const Value = struct {
handle: *const v8.c.Value,
fn isArray(self: Value) bool {
return v8.c.v8__Value__IsArray(self.handle);
}
fn isTypedArray(self: Value) bool {
return v8.c.v8__Value__IsTypedArray(self.handle);
}
fn isFunction(self: Value) bool {
return v8.c.v8__Value__IsFunction(self.handle);
}
};
const Name = struct {
handle: *const v8.c.Name,
};
const CallbackInfo = struct {
raw: *const v8.c.FunctionCallbackInfo,
fn length(self: CallbackInfo) u32 {
return @intCast(v8.c.v8__FunctionCallbackInfo__Length(self.raw));
}
fn getArg(self: CallbackInfo, index: u32) Value {
return .{ .handle = v8.c.v8__FunctionCallbackInfo__INDEX(self.raw, @intCast(index)).? };
}
fn getThis(self: CallbackInfo) *const v8.c.Object {
return v8.c.v8__FunctionCallbackInfo__This(self.raw).?;
}
fn getReturnValue(self: CallbackInfo) ReturnValue {
var rv: v8.c.ReturnValue = undefined;
v8.c.v8__FunctionCallbackInfo__GetReturnValue(self.raw, &rv);
return .{ .raw = rv };
}
};
const PropertyCallbackInfo = struct {
raw: *const v8.c.PropertyCallbackInfo,
fn getThis(self: PropertyCallbackInfo) *const v8.c.Object {
return v8.c.v8__PropertyCallbackInfo__This(self.raw).?;
}
fn getReturnValue(self: PropertyCallbackInfo) ReturnValue {
var rv: v8.c.ReturnValue = undefined;
v8.c.v8__PropertyCallbackInfo__GetReturnValue(self.raw, &rv);
return .{ .raw = rv };
}
};
const ReturnValue = struct {
raw: v8.c.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) {
self.setValueHandle(@ptrCast(value));
} else if (T == *const v8.c.Value) {
self.setValueHandle(value);
} else if (T == js.Value) {
self.setValueHandle(value.handle);
} else {
@compileError("Unsupported type for ReturnValue.set: " ++ @typeName(T));
}
}
fn setValueHandle(self: ReturnValue, handle: *const v8.c.Value) void {
v8.c.v8__ReturnValue__Set(self.raw, handle);
}
};
// ============================================================================
// Caller - Responsible for calling Zig functions from JS invocations
// ============================================================================
pub const Caller = struct {
context: *Context,
isolate: js.Isolate,
call_arena: Allocator,
// Takes the raw v8 isolate and extracts the context from it.
pub fn init(v8_isolate: *v8.c.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);
var lossless: bool = undefined;
const context: *Context = @ptrFromInt(v8.c.v8__BigInt__Uint64Value(embedder_data, &lossless));
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: CallbackInfo, 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 {
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_handle = info.getThis();
const new_this = v8.Object{ .handle = new_this_handle };
var this = new_this;
if (@typeInfo(ReturnType) == .error_union) {
const non_error_res = res catch |err| return err;
this = (try self.context.mapZigInstanceToJs(new_this_handle, non_error_res)).castToObject();
} else {
this = (try self.context.mapZigInstanceToJs(new_this_handle, 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.handle);
}
pub fn method(self: *Caller, comptime T: type, func: anytype, info: CallbackInfo, 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 {
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: CallbackInfo, 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 {
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: 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;
};
}
fn _getIndex(self: *Caller, comptime T: type, func: anytype, idx: u32, info: 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: 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;
};
}
fn _getNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, info: 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: 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;
};
}
fn _setNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, js_value: Value, info: 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{ .ctx = self.context, .handle = js_value.handle });
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: 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;
};
}
fn _deleteNamedIndex(self: *Caller, comptime T: type, func: anytype, name: Name, info: 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: 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 nameToString(self: *Caller, name: Name) ![]const u8 {
return self.context.valueToString(js.Value{ .ctx = self.context, .handle = @ptrCast(name.handle) }, .{});
}
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 (log.enabled(.js, .warn)) {
self.logFunctionCallError(@typeName(T), @typeName(F), err, info);
}
}
const js_err: *const v8.c.Value = switch (err) {
error.InvalidArgument => isolate.createTypeError("invalid argument"),
error.OutOfMemory => isolate.createError("out of memory"),
error.IllegalConstructor => isolate.createError("Illegal Contructor"),
else => blk: {
if (comptime opts.dom_exception) {
const DOMException = @import("../webapi/DOMException.zig");
if (DOMException.fromError(err)) |ex| {
const value = self.context.zigValueToJs(ex, .{}) catch break :blk isolate.createError("internal error");
break :blk value.handle;
}
}
break :blk isolate.createError(@errorName(err));
},
};
const js_exception = isolate.throwException(js_err);
info.getReturnValue().setValueHandle(js_exception);
}
// 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{ .ctx = context, .handle = js_value.handle });
}
@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{ .ctx = context, .handle = js_value.handle }) 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: CallbackInfo) 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: CallbackInfo) ![]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 });
const val = info.getArg(@intCast(i));
try context.debugValue(js.Value{ .ctx = context, .handle = val.handle }, &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;
}
};
// ============================================================================
// Bridge Builder Functions
// ============================================================================
pub fn Builder(comptime T: type) type {
return struct {
@@ -98,10 +624,11 @@ 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 info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
caller.constructor(T, func, info, .{
.dom_exception = opts.dom_exception,
});
@@ -126,10 +653,11 @@ pub const Function = struct {
.static = opts.static,
.func = struct {
fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
if (comptime opts.static) {
caller.function(T, func, info, .{
.dom_exception = opts.dom_exception,
@@ -169,10 +697,11 @@ pub const Accessor = struct {
if (@typeInfo(@TypeOf(getter)) != .null) {
accessor.getter = struct {
fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
caller.method(T, getter, info, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -184,12 +713,13 @@ pub const Accessor = struct {
if (@typeInfo(@TypeOf(setter)) != .null) {
accessor.setter = struct {
fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
std.debug.assert(info.length() == 1);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
std.debug.assert(info.length() == 1);
caller.method(T, setter, info, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -213,9 +743,11 @@ 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 info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = PropertyCallbackInfo{ .raw = raw_info.? };
return caller.getIndex(T, getter, idx, info, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -238,9 +770,11 @@ 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 info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = PropertyCallbackInfo{ .raw = raw_info.? };
return caller.getNamedIndex(T, getter, .{ .handle = c_name.? }, info, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -250,10 +784,11 @@ pub const NamedIndexed = struct {
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 info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = PropertyCallbackInfo{ .raw = raw_info.? };
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,
@@ -263,10 +798,11 @@ pub const NamedIndexed = struct {
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 info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__PropertyCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = PropertyCallbackInfo{ .raw = raw_info.? };
return caller.deleteNamedIndex(T, deleter, .{ .handle = c_name.? }, info, .{
.as_typed_array = opts.as_typed_array,
.null_as_undefined = opts.null_as_undefined,
@@ -308,9 +844,11 @@ pub const Iterator = struct {
.async = opts.async,
.func = struct {
fn wrap(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
caller.method(T, struct_or_func, info, .{});
}
}.wrap,
@@ -328,9 +866,11 @@ 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 info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller.init(info);
const v8_isolate = v8.c.v8__FunctionCallbackInfo__GetIsolate(raw_info).?;
var caller = Caller.init(v8_isolate);
defer caller.deinit();
const info = CallbackInfo{ .raw = raw_info.? };
caller.method(T, func, info, .{
.null_as_undefined = opts.null_as_undefined,
});