Change NamedFunction from a generic to a normal struct.

NamedFunction is important for displaying good error messages when there's
something wrong with the Zig structs we're trying to bind to JS. By making it
a normal struct, it's easier and cheaper to pass wherever an @compileError
might be needed.
This commit is contained in:
Karl Seguin
2025-05-07 13:44:41 +08:00
parent f1fe4c0c70
commit c31290b794

View File

@@ -57,7 +57,7 @@ pub const Platform = struct {
// The `S` parameter is arbitrary state. When we start an Executor, an instance // The `S` parameter is arbitrary state. When we start an Executor, an instance
// of S must be given. This instance is available to any Zig binding. // of S must be given. This instance is available to any Zig binding.
// The `types` parameter is a tuple of Zig structures we want to bind to V8. // The `types` parameter is a tuple of Zig structures we want to bind to V8.
pub fn Env(comptime S: type, comptime WebApis: type) type { pub fn Env(comptime State: type, comptime WebApis: type) type {
const Types = @typeInfo(WebApis.Interfaces).@"struct".fields; const Types = @typeInfo(WebApis.Interfaces).@"struct".fields;
// Imagine we have a type Cat which has a getter: // Imagine we have a type Cat which has a getter:
@@ -179,7 +179,6 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
const Self = @This(); const Self = @This();
const State = S;
const TYPE_LOOKUP = TypeLookup{}; const TYPE_LOOKUP = TypeLookup{};
const Opts = struct { const Opts = struct {
@@ -1216,7 +1215,7 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
const template = v8.FunctionTemplate.initCallback(isolate, struct { const template = v8.FunctionTemplate.initCallback(isolate, struct {
fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info); const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
// See comment above. We generateConstructor on all types // See comment above. We generateConstructor on all types
@@ -1233,9 +1232,9 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
// Safe to call now, because if Struct.constructor didn't // Safe to call now, because if Struct.constructor didn't
// exist, the above if block would have returned. // exist, the above if block would have returned.
const named_function = NamedFunction(Struct, Struct.constructor, "constructor"){}; const named_function = comptime NamedFunction.init(Struct, "constructor");
caller.constructor(named_function, info) catch |err| { caller.constructor(Struct, named_function, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback); }.callback);
@@ -1261,12 +1260,12 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
const function_template = v8.FunctionTemplate.initCallback(isolate, struct { const function_template = v8.FunctionTemplate.initCallback(isolate, struct {
fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void { fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info); const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
const named_function = NamedFunction(Struct, @field(Struct, name), name){}; const named_function = comptime NamedFunction.init(Struct, name);
caller.method(named_function, info) catch |err| { caller.method(Struct, named_function, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback); }.callback);
@@ -1303,12 +1302,12 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
const getter_callback = struct { const getter_callback = struct {
fn callback(_: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void { fn callback(_: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
const info = v8.PropertyCallbackInfo.initFromV8(raw_info); const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
const named_function = NamedFunction(Struct, getter, "get_" ++ name){}; const named_function = comptime NamedFunction.init(Struct, "get_" ++ name);
caller.getter(named_function, info) catch |err| { caller.getter(Struct, named_function, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback; }.callback;
@@ -1319,17 +1318,16 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
return; return;
} }
const setter = @field(Struct, setter_name);
const setter_callback = struct { const setter_callback = struct {
fn callback(_: ?*const v8.C_Name, raw_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void { fn callback(_: ?*const v8.C_Name, raw_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
const info = v8.PropertyCallbackInfo.initFromV8(raw_info); const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
const js_value = v8.Value{ .handle = raw_value.? }; const js_value = v8.Value{ .handle = raw_value.? };
const named_function = NamedFunction(Struct, setter, "set_" ++ name){}; const named_function = comptime NamedFunction.init(Struct, "set_" ++ name);
caller.setter(named_function, js_value, info) catch |err| { caller.setter(Struct, named_function, js_value, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback; }.callback;
@@ -1344,12 +1342,12 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
.getter = struct { .getter = struct {
fn callback(idx: u32, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void { fn callback(idx: u32, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
const info = v8.PropertyCallbackInfo.initFromV8(raw_info); const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
const named_function = NamedFunction(Struct, Struct.indexed_get, "indexed_get"){}; const named_function = comptime NamedFunction.init(Struct, "indexed_get");
caller.getIndex(named_function, idx, info) catch |err| { caller.getIndex(Struct, named_function, idx, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback, }.callback,
@@ -1373,12 +1371,12 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
.getter = struct { .getter = struct {
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void { fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
const info = v8.PropertyCallbackInfo.initFromV8(raw_info); const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
var caller = Caller(Self).init(info); var caller = Caller(Self, State).init(info);
defer caller.deinit(); defer caller.deinit();
const named_function = NamedFunction(Struct, Struct.named_get, "named_get"){}; const named_function = comptime NamedFunction.init(Struct, "named_get");
caller.getNamedIndex(named_function, .{ .handle = c_name.? }, info) catch |err| { caller.getNamedIndex(Struct, named_function, .{ .handle = c_name.? }, info) catch |err| {
caller.handleError(named_function, err, info); caller.handleError(Struct, named_function, err, info);
}; };
} }
}.callback, }.callback,
@@ -1539,7 +1537,7 @@ pub fn Env(comptime S: type, comptime WebApis: type) type {
} }
// Reverses the mapZigInstanceToJs, making sure that our TaggedAnyOpaque // Reverses the mapZigInstanceToJs, making sure that our TaggedAnyOpaque
// contains a ptr to the correct type. // contains a ptr to the correct type.
fn typeTaggedAnyOpaque(comptime named_function: anytype, comptime R: type, js_obj: v8.Object) !R { fn typeTaggedAnyOpaque(comptime named_function: NamedFunction, comptime R: type, js_obj: v8.Object) !R {
const ti = @typeInfo(R); const ti = @typeInfo(R);
if (ti != .pointer) { if (ti != .pointer) {
@compileError(std.fmt.comptimePrint( @compileError(std.fmt.comptimePrint(
@@ -1663,8 +1661,7 @@ fn isEmpty(comptime T: type) bool {
// probably just contained in Executor, but having this specific logic, which // probably just contained in Executor, but having this specific logic, which
// is somewhat repetitive between constructors, functions, getters, etc contained // is somewhat repetitive between constructors, functions, getters, etc contained
// here does feel like it makes it clenaer. // here does feel like it makes it clenaer.
fn Caller(comptime E: type) type { fn Caller(comptime E: type, comptime State: type) type {
const State = E.State;
const TYPE_LOOKUP = E.TYPE_LOOKUP; const TYPE_LOOKUP = E.TYPE_LOOKUP;
const TypeLookup = @TypeOf(TYPE_LOOKUP); const TypeLookup = @TypeOf(TYPE_LOOKUP);
@@ -1723,13 +1720,12 @@ fn Caller(comptime E: type) type {
scope.call_depth = call_depth; scope.call_depth = call_depth;
} }
fn constructor(self: *Self, comptime named_function: anytype, info: v8.FunctionCallbackInfo) !void { fn constructor(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, info: v8.FunctionCallbackInfo) !void {
const S = named_function.S; const args = try self.getArgs(Struct, named_function, 0, info);
const args = try self.getArgs(named_function, 0, info); const res = @call(.auto, Struct.constructor, args);
const res = @call(.auto, S.constructor, args);
const ReturnType = @typeInfo(@TypeOf(S.constructor)).@"fn".return_type orelse { const ReturnType = @typeInfo(@TypeOf(Struct.constructor)).@"fn".return_type orelse {
@compileError(@typeName(S) ++ " has a constructor without a return type"); @compileError(@typeName(Struct) ++ " has a constructor without a return type");
}; };
const this = info.getThis(); const this = info.getThis();
@@ -1742,25 +1738,25 @@ fn Caller(comptime E: type) type {
info.getReturnValue().set(this); info.getReturnValue().set(this);
} }
fn method(self: *Self, comptime named_function: anytype, info: v8.FunctionCallbackInfo) !void { fn method(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, info: v8.FunctionCallbackInfo) !void {
const S = named_function.S; const func = @field(Struct, named_function.name);
comptime assertSelfReceiver(named_function); comptime assertSelfReceiver(Struct, named_function);
var args = try self.getArgs(named_function, 1, info); var args = try self.getArgs(Struct, named_function, 1, info);
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis()); const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
// inject 'self' as the first parameter // inject 'self' as the first parameter
@field(args, "0") = zig_instance; @field(args, "0") = zig_instance;
const res = @call(.auto, named_function.func, args); const res = @call(.auto, func, args);
info.getReturnValue().set(try self.zigValueToJs(res)); info.getReturnValue().set(try self.zigValueToJs(res));
} }
fn getter(self: *Self, comptime named_function: anytype, info: v8.PropertyCallbackInfo) !void { fn getter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, info: v8.PropertyCallbackInfo) !void {
const S = named_function.S; const func = @field(Struct, named_function.name);
const Getter = @TypeOf(named_function.func); const Getter = @TypeOf(func);
if (@typeInfo(Getter).@"fn".return_type == null) { if (@typeInfo(Getter).@"fn".return_type == null) {
@compileError(@typeName(S) ++ " has a getter without a return type: " ++ @typeName(Getter)); @compileError(@typeName(Struct) ++ " has a getter without a return type: " ++ @typeName(Getter));
} }
var args: ParamterTypes(Getter) = undefined; var args: ParamterTypes(Getter) = undefined;
@@ -1768,27 +1764,27 @@ fn Caller(comptime E: type) type {
switch (arg_fields.len) { switch (arg_fields.len) {
0 => {}, // getters _can_ be parameterless 0 => {}, // getters _can_ be parameterless
1, 2 => { 1, 2 => {
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis()); const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
comptime assertSelfReceiver(named_function); comptime assertSelfReceiver(Struct, named_function);
@field(args, "0") = zig_instance; @field(args, "0") = zig_instance;
if (comptime arg_fields.len == 2) { if (comptime arg_fields.len == 2) {
comptime assertIsStateArg(named_function, 1); comptime assertIsStateArg(Struct, named_function, 1);
@field(args, "1") = self.scope.state; @field(args, "1") = self.scope.state;
} }
}, },
else => @compileError(named_function.full_name + " has too many parmaters: " ++ @typeName(named_function.func)), else => @compileError(named_function.full_name + " has too many parmaters: " ++ @typeName(named_function.func)),
} }
const res = @call(.auto, named_function.func, args); const res = @call(.auto, func, args);
info.getReturnValue().set(try self.zigValueToJs(res)); info.getReturnValue().set(try self.zigValueToJs(res));
} }
fn setter(self: *Self, comptime named_function: anytype, js_value: v8.Value, info: v8.PropertyCallbackInfo) !void { fn setter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, js_value: v8.Value, info: v8.PropertyCallbackInfo) !void {
const S = named_function.S; const func = @field(Struct, named_function.name);
comptime assertSelfReceiver(named_function); comptime assertSelfReceiver(Struct, named_function);
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis()); const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
const Setter = @TypeOf(named_function.func); const Setter = @TypeOf(func);
var args: ParamterTypes(Setter) = undefined; var args: ParamterTypes(Setter) = undefined;
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields; const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
switch (arg_fields.len) { switch (arg_fields.len) {
@@ -1798,7 +1794,7 @@ fn Caller(comptime E: type) type {
@field(args, "0") = zig_instance; @field(args, "0") = zig_instance;
@field(args, "1") = try self.jsValueToZig(named_function, arg_fields[1].type, js_value); @field(args, "1") = try self.jsValueToZig(named_function, arg_fields[1].type, js_value);
if (comptime arg_fields.len == 3) { if (comptime arg_fields.len == 3) {
comptime assertIsStateArg(named_function, 2); comptime assertIsStateArg(Struct, named_function, 2);
@field(args, "2") = self.scope.state; @field(args, "2") = self.scope.state;
} }
}, },
@@ -1807,16 +1803,16 @@ fn Caller(comptime E: type) type {
if (@typeInfo(Setter).@"fn".return_type) |return_type| { if (@typeInfo(Setter).@"fn".return_type) |return_type| {
if (@typeInfo(return_type) == .error_union) { if (@typeInfo(return_type) == .error_union) {
_ = try @call(.auto, named_function.func, args); _ = try @call(.auto, func, args);
return; return;
} }
} }
_ = @call(.auto, named_function.func, args); _ = @call(.auto, func, args);
} }
fn getIndex(self: *Self, comptime named_function: anytype, idx: u32, info: v8.PropertyCallbackInfo) !void { fn getIndex(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, idx: u32, info: v8.PropertyCallbackInfo) !void {
const S = named_function.S; const func = @field(Struct, named_function.name);
const IndexedGet = @TypeOf(named_function.func); const IndexedGet = @TypeOf(func);
if (@typeInfo(IndexedGet).@"fn".return_type == null) { if (@typeInfo(IndexedGet).@"fn".return_type == null) {
@compileError(named_function.full_name ++ " must have a return type"); @compileError(named_function.full_name ++ " must have a return type");
} }
@@ -1828,20 +1824,20 @@ fn Caller(comptime E: type) type {
switch (arg_fields.len) { switch (arg_fields.len) {
0, 1, 2 => @compileError(named_function.full_name ++ " must take at least a u32 and *bool parameter"), 0, 1, 2 => @compileError(named_function.full_name ++ " must take at least a u32 and *bool parameter"),
3, 4 => { 3, 4 => {
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis()); const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
comptime assertSelfReceiver(named_function); comptime assertSelfReceiver(Struct, named_function);
@field(args, "0") = zig_instance; @field(args, "0") = zig_instance;
@field(args, "1") = idx; @field(args, "1") = idx;
@field(args, "2") = &has_value; @field(args, "2") = &has_value;
if (comptime arg_fields.len == 4) { if (comptime arg_fields.len == 4) {
comptime assertIsStateArg(named_function, 3); comptime assertIsStateArg(Struct, named_function, 3);
@field(args, "3") = self.scope.state; @field(args, "3") = self.scope.state;
} }
}, },
else => @compileError(named_function.full_name ++ " has too many parmaters"), else => @compileError(named_function.full_name ++ " has too many parmaters"),
} }
const res = @call(.auto, S.indexed_get, args); const res = @call(.auto, func, args);
if (has_value == false) { if (has_value == false) {
// for an indexed parameter, say nodes[10000], we should return // for an indexed parameter, say nodes[10000], we should return
// undefined, not null, if the index is out of rante // undefined, not null, if the index is out of rante
@@ -1851,9 +1847,9 @@ fn Caller(comptime E: type) type {
} }
} }
fn getNamedIndex(self: *Self, comptime named_function: anytype, name: v8.Name, info: v8.PropertyCallbackInfo) !void { fn getNamedIndex(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, name: v8.Name, info: v8.PropertyCallbackInfo) !void {
const S = named_function.S; const func = @field(Struct, named_function.name);
const NamedGet = @TypeOf(named_function.func); const NamedGet = @TypeOf(func);
if (@typeInfo(NamedGet).@"fn".return_type == null) { if (@typeInfo(NamedGet).@"fn".return_type == null) {
@compileError(named_function.full_name ++ " must have a return type"); @compileError(named_function.full_name ++ " must have a return type");
} }
@@ -1864,20 +1860,20 @@ fn Caller(comptime E: type) type {
switch (arg_fields.len) { switch (arg_fields.len) {
0, 1, 2 => @compileError(named_function.full_name ++ " must take at least a u32 and *bool parameter"), 0, 1, 2 => @compileError(named_function.full_name ++ " must take at least a u32 and *bool parameter"),
3, 4 => { 3, 4 => {
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis()); const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
comptime assertSelfReceiver(named_function); comptime assertSelfReceiver(Struct, named_function);
@field(args, "0") = zig_instance; @field(args, "0") = zig_instance;
@field(args, "1") = try self.nameToString(name); @field(args, "1") = try self.nameToString(name);
@field(args, "2") = &has_value; @field(args, "2") = &has_value;
if (comptime arg_fields.len == 4) { if (comptime arg_fields.len == 4) {
comptime assertIsStateArg(named_function, 3); comptime assertIsStateArg(Struct, named_function, 3);
@field(args, "3") = self.scope.state; @field(args, "3") = self.scope.state;
} }
}, },
else => @compileError(named_function.full_name ++ " has too many parmaters"), else => @compileError(named_function.full_name ++ " has too many parmaters"),
} }
const res = @call(.auto, S.named_get, args); const res = @call(.auto, func, args);
if (has_value == false) { if (has_value == false) {
// for an indexed parameter, say nodes[10000], we should return // for an indexed parameter, say nodes[10000], we should return
// undefined, not null, if the index is out of rante // undefined, not null, if the index is out of rante
@@ -1891,12 +1887,13 @@ fn Caller(comptime E: type) type {
return valueToString(self.call_arena, .{ .handle = name.handle }, self.isolate, self.context); return valueToString(self.call_arena, .{ .handle = name.handle }, self.isolate, self.context);
} }
fn assertSelfReceiver(comptime named_function: anytype) void { fn assertSelfReceiver(comptime Struct: type, comptime named_function: NamedFunction) void {
const params = @typeInfo(@TypeOf(named_function.func)).@"fn".params; const func = @field(Struct, named_function.name);
const params = @typeInfo(@TypeOf(func)).@"fn".params;
if (params.len == 0) { if (params.len == 0) {
@compileError(named_function.full_name ++ " must have a self parameter"); @compileError(named_function.full_name ++ " must have a self parameter");
} }
const R = Receiver(named_function.S); const R = Receiver(Struct);
const first_param = params[0].type.?; const first_param = params[0].type.?;
if (first_param != *R and first_param != *const R) { if (first_param != *R and first_param != *const R) {
@@ -1904,8 +1901,9 @@ fn Caller(comptime E: type) type {
} }
} }
fn assertIsStateArg(comptime named_function: anytype, index: comptime_int) void { fn assertIsStateArg(comptime Struct: type, comptime named_function: NamedFunction, index: comptime_int) void {
const F = @TypeOf(named_function.func); const func = @field(Struct, named_function.name);
const F = @TypeOf(func);
const params = @typeInfo(F).@"fn".params; const params = @typeInfo(F).@"fn".params;
const param = params[index].type.?; const param = params[index].type.?;
@@ -1914,13 +1912,14 @@ fn Caller(comptime E: type) type {
} }
} }
fn handleError(self: *Self, comptime named_function: anytype, err: anyerror, info: anytype) void { fn handleError(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, err: anyerror, info: anytype) void {
const isolate = self.isolate; const isolate = self.isolate;
var js_err: ?v8.Value = switch (err) { var js_err: ?v8.Value = switch (err) {
error.InvalidArgument => createTypeException(isolate, "invalid argument"), error.InvalidArgument => createTypeException(isolate, "invalid argument"),
error.OutOfMemory => createException(isolate, "out of memory"), error.OutOfMemory => createException(isolate, "out of memory"),
else => blk: { else => blk: {
const return_type = @typeInfo(@TypeOf(named_function.func)).@"fn".return_type orelse { const func = @field(Struct, named_function.name);
const return_type = @typeInfo(@TypeOf(func)).@"fn".return_type orelse {
// void return type; // void return type;
break :blk null; break :blk null;
}; };
@@ -1935,7 +1934,7 @@ fn Caller(comptime E: type) type {
const function_error_set = @typeInfo(return_type).error_union.error_set; const function_error_set = @typeInfo(return_type).error_union.error_set;
const Exception = comptime getCustomException(named_function.S) orelse break :blk null; const Exception = comptime getCustomException(Struct) orelse break :blk null;
if (function_error_set == Exception or isErrorSetException(Exception, err)) { if (function_error_set == Exception or isErrorSetException(Exception, err)) {
const custom_exception = Exception.init(self.call_arena, err, named_function.js_name) catch |init_err| { const custom_exception = Exception.init(self.call_arena, err, named_function.js_name) catch |init_err| {
switch (init_err) { switch (init_err) {
@@ -2004,8 +2003,8 @@ fn Caller(comptime E: type) type {
// Finally, if the JS function is called with _more_ parameters and // 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 // the last parameter in Zig is an array, we'll try to slurp the additional
// parameters into the array. // parameters into the array.
fn getArgs(self: *const Self, comptime named_function: anytype, comptime offset: usize, info: anytype) !ParamterTypes(@TypeOf(named_function.func)) { fn getArgs(self: *const Self, comptime Struct: type, comptime named_function: NamedFunction, comptime offset: usize, info: anytype) !ParamterTypes(@TypeOf(@field(Struct, named_function.name))) {
const F = @TypeOf(named_function.func); const F = @TypeOf(@field(Struct, named_function.name));
var args: ParamterTypes(F) = undefined; var args: ParamterTypes(F) = undefined;
const params = @typeInfo(F).@"fn".params[offset..]; const params = @typeInfo(F).@"fn".params[offset..];
@@ -2111,7 +2110,7 @@ fn Caller(comptime E: type) type {
return args; return args;
} }
fn jsValueToZig(self: *const Self, comptime named_function: anytype, comptime T: type, js_value: v8.Value) !T { fn jsValueToZig(self: *const Self, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !T {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.optional => |o| { .optional => |o| {
if (js_value.isNullOrUndefined()) { if (js_value.isNullOrUndefined()) {
@@ -2329,7 +2328,7 @@ fn Caller(comptime E: type) type {
// Extracted so that it can be used in both jsValueToZig and in // Extracted so that it can be used in both jsValueToZig and in
// probeJsValueToZig. Avoids having to duplicate this logic when probing. // probeJsValueToZig. Avoids having to duplicate this logic when probing.
fn jsValueToStruct(self: *const Self, comptime named_function: anytype, comptime T: type, js_value: v8.Value) !?T { fn jsValueToStruct(self: *const Self, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !?T {
if (@hasDecl(T, "_CALLBACK_ID_KLUDGE")) { if (@hasDecl(T, "_CALLBACK_ID_KLUDGE")) {
if (!js_value.isFunction()) { if (!js_value.isFunction()) {
return error.InvalidArgument; return error.InvalidArgument;
@@ -2416,7 +2415,7 @@ fn Caller(comptime E: type) type {
invalid: void, invalid: void,
}; };
} }
fn probeJsValueToZig(self: *const Self, comptime named_function: anytype, comptime T: type, js_value: v8.Value) !ProbeResult(T) { fn probeJsValueToZig(self: *const Self, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !ProbeResult(T) {
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.optional => |o| { .optional => |o| {
if (js_value.isNullOrUndefined()) { if (js_value.isNullOrUndefined()) {
@@ -2843,27 +2842,28 @@ const ErrorModuleLoader = struct {
// pub fn meow(self: *OtherImpl) void { ... } // pub fn meow(self: *OtherImpl) void { ... }
// } // }
// In which case, as we see above, the receiver is derived from the Self declaration // In which case, as we see above, the receiver is derived from the Self declaration
fn Receiver(comptime S: type) type { fn Receiver(comptime Struct: type) type {
return if (@hasDecl(S, "Self")) S.Self else S; return if (@hasDecl(Struct, "Self")) Struct.Self else Struct;
} }
// We want the function name, or more precisely, the "Struct.function" for // We want the function name, or more precisely, the "Struct.function" for
// displaying helpful @compileError. // displaying helpful @compileError.
// However, there's no way to get the name from a std.Builtin.Fn, // However, there's no way to get the name from a std.Builtin.Fn, so we create
// so we capture it early and mostly pass around this NamedFunction instance // a NamedFunction as part of our binding, and pass it around incase we need
// whenever we're trying to bind a function/getter/setter/etc so that we always // to display an error
// have the main data (struct + function) along with the meta data for displaying const NamedFunction = struct {
// better errors. name: []const u8,
fn NamedFunction(comptime S: type, comptime function: anytype, comptime name: []const u8) type { js_name: []const u8,
const full_name = @typeName(S) ++ "." ++ name; full_name: []const u8,
const js_name = if (name[0] == '_') name[1..] else name;
return struct { fn init(comptime Struct: type, comptime name: []const u8) NamedFunction {
S: type = S, return .{
full_name: []const u8 = full_name, .name = name,
func: @TypeOf(function) = function, .js_name = if (name[0] == '_') name[1..] else name,
js_name: []const u8 = js_name, .full_name = @typeName(Struct) ++ "." ++ name,
}; };
} }
};
// This is called from V8. Whenever the v8 inspector has to describe a value // This is called from V8. Whenever the v8 inspector has to describe a value
// it'll call this function to gets its [optional] subtype - which, from V8's // it'll call this function to gets its [optional] subtype - which, from V8's