Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 144 additions & 168 deletions src/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,7 @@ pub const Stream = c.luaL_Stream;
pub const Unsigned = c.lua_Unsigned;

/// The type of warning functions used by Lua to emit warnings
pub const CWarnFn = switch (lang) {
.lua54 => *const fn (data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.c) void,
else => @compileError("CWarnFn not defined"),
};
pub const CWarnFn = *const fn (data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.c) void;

/// The type of the writer function used by `Lua.dump()`
pub const CWriterFn = *const fn (state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.c) c_int;
Expand Down Expand Up @@ -5157,21 +5154,30 @@ pub const Buffer = struct {

// Helper functions to make the zlua API easier to use

const Tuple = std.meta.Tuple;

fn TypeOfWrap(comptime function: anytype) type {
const Args = std.meta.ArgsTuple(@TypeOf(function));
return switch (Args) {
Tuple(&.{*Lua}) => CFn,
Tuple(&.{ *Lua, Event, *DebugInfo }) => CHookFn,
Tuple(&.{ *Lua, Status, Context }) => CContFn,
Tuple(&.{ *Lua, *anyopaque }) => CReaderFn,
Tuple(&.{*anyopaque}) => CUserdataDtorFn,
Tuple(&.{ *Lua, i32 }) => CInterruptCallbackFn,
Tuple(&.{[]const u8}) => CUserAtomCallbackFn,
Tuple(&.{ ?*anyopaque, []const u8, bool }) => CWarnFn,
Tuple(&.{ *Lua, []const u8, *anyopaque }) => CWriterFn,
else => @compileError("Unsupported function given to wrap '" ++ @typeName(@TypeOf(function)) ++ "'"),
const params = @typeInfo(@TypeOf(function)).@"fn".params;
if (params.len == 1) {
if (params[0].type.? == *Lua) return CFn;
if (params[0].type.? == []const u8) return CUserAtomCallbackFn;
if (params[0].type.? == *anyopaque) return CUserdataDtorFn;
}
if (params.len == 2) {
if (params[0].type.? == *Lua) {
if (params[1].type.? == i32) return CInterruptCallbackFn;
if (params[1].type.? == *anyopaque) return CReaderFn;
}
}
if (params.len == 3) {
if (params[0].type.? == ?*anyopaque and params[1].type.? == []const u8 and params[2].type.? == bool) return CWarnFn;
if (params[0].type.? == *Lua) {
if (params[1].type.? == Event and params[2].type.? == *DebugInfo) return CHookFn;
if (params[1].type.? == Status and params[2].type.? == Context) return CContFn;
if (params[1].type.? == []const u8 and params[2].type.? == *anyopaque) return CWriterFn;
}
}
return {
@compileLog(@TypeOf(function));
@compileError("Unsupported function given to wrap.");
};
}

Expand All @@ -5190,169 +5196,139 @@ fn TypeOfWrap(comptime function: anytype) type {
/// Functions that accept a `*Lua` pointer also support returning error unions. For example,
/// wrap also supports `fn (lua: *Lua) !i32` for a `CFn`.
pub fn wrap(comptime function: anytype) TypeOfWrap(function) {
const info = @typeInfo(@TypeOf(function));
if (info != .@"fn") {
@compileError("Wrap only accepts functions");
}

const has_error_union = @typeInfo(info.@"fn".return_type.?) == .error_union;

const Args = std.meta.ArgsTuple(@TypeOf(function));
switch (Args) {
// CFn
Tuple(&.{*Lua}) => {
return struct {
fn inner(state: ?*LuaState) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
return @call(.always_inline, function, .{lua}) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
return @call(.always_inline, function, .{lua});
}
const info = @typeInfo(@TypeOf(function)).@"fn";

const has_error_union = @typeInfo(info.return_type.?) == .error_union;

const Return = TypeOfWrap(function);
return switch (Return) {
CFn => struct {
fn inner(state: ?*LuaState) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
return @call(.always_inline, function, .{lua}) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
return @call(.always_inline, function, .{lua});
}
}.inner;
},
// CHookFn
Tuple(&.{ *Lua, Event, *DebugInfo }) => {
return struct {
fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.c) void {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
var debug_info: DebugInfo = .{
.current_line = if (ar.?.currentline == -1) null else ar.?.currentline,
.private = switch (lang) {
.lua51, .luajit => ar.?.i_ci,
else => @ptrCast(ar.?.i_ci),
},
}
}.inner,
CHookFn => struct {
fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.c) void {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
var debug_info: DebugInfo = .{
.current_line = if (ar.?.currentline == -1) null else ar.?.currentline,
.private = switch (lang) {
.lua51, .luajit => ar.?.i_ci,
else => @ptrCast(ar.?.i_ci),
},
};
if (has_error_union) {
@call(.always_inline, function, .{ lua, @as(Event, @enumFromInt(ar.?.event)), &debug_info }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
if (has_error_union) {
@call(.always_inline, function, .{ lua, @as(Event, @enumFromInt(ar.?.event)), &debug_info }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
@call(.always_inline, function, .{ lua, @as(Event, @enumFromInt(ar.?.event)), &debug_info });
}
} else {
@call(.always_inline, function, .{ lua, @as(Event, @enumFromInt(ar.?.event)), &debug_info });
}
}.inner;
},
// CContFn
Tuple(&.{ *Lua, Status, Context }) => {
return struct {
fn inner(state: ?*LuaState, status: c_int, ctx: Context) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
return @call(.always_inline, function, .{ lua, @as(Status, @enumFromInt(status)), ctx }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
return @call(.always_inline, function, .{ lua, @as(Status, @enumFromInt(status)), ctx });
}
}
}.inner,
CContFn => struct {
fn inner(state: ?*LuaState, status: c_int, ctx: Context) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
return @call(.always_inline, function, .{ lua, @as(Status, @enumFromInt(status)), ctx }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
return @call(.always_inline, function, .{ lua, @as(Status, @enumFromInt(status)), ctx });
}
}.inner;
},
// CReaderFn
Tuple(&.{ *Lua, *anyopaque }) => {
return struct {
fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.c) [*c]const u8 {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
const result = @call(.always_inline, function, .{ lua, data.? }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
if (result) |buffer| {
size.* = buffer.len;
return buffer.ptr;
} else {
size.* = 0;
return null;
}
}
}.inner,
CReaderFn => struct {
fn inner(state: ?*LuaState, data: ?*anyopaque, size: [*c]usize) callconv(.c) [*c]const u8 {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
const result = @call(.always_inline, function, .{ lua, data.? }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
if (result) |buffer| {
size.* = buffer.len;
return buffer.ptr;
} else {
if (@call(.always_inline, function, .{ lua, data.? })) |buffer| {
size.* = buffer.len;
return buffer.ptr;
} else {
size.* = 0;
return null;
}
size.* = 0;
return null;
}
}
}.inner;
},
// CUserdataDtorFn
Tuple(&.{*anyopaque}) => {
return struct {
fn inner(userdata: *anyopaque) callconv(.c) void {
return @call(.always_inline, function, .{userdata});
}
}.inner;
},
// CInterruptCallbackFn
Tuple(&.{ *Lua, i32 }) => {
return struct {
fn inner(state: ?*LuaState, gc: c_int) callconv(.c) void {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
@call(.always_inline, function, .{ lua, gc }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
if (@call(.always_inline, function, .{ lua, data.? })) |buffer| {
size.* = buffer.len;
return buffer.ptr;
} else {
@call(.always_inline, function, .{ lua, gc });
size.* = 0;
return null;
}
}
}.inner;
},
// CUserAtomCallbackFn
Tuple(&.{[]const u8}) => {
return struct {
fn inner(str: [*c]const u8, len: usize) callconv(.c) i16 {
if (str) |s| {
const buf = s[0..len];
return @call(.always_inline, function, .{buf});
}
return -1;
}
}.inner,
CUserdataDtorFn => struct {
fn inner(userdata: *anyopaque) callconv(.c) void {
return @call(.always_inline, function, .{userdata});
}
}.inner,
CInterruptCallbackFn => struct {
fn inner(state: ?*LuaState, gc: c_int) callconv(.c) void {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
if (has_error_union) {
@call(.always_inline, function, .{ lua, gc }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else {
@call(.always_inline, function, .{ lua, gc });
}
}.inner;
},
// CWarnFn
Tuple(&.{ ?*anyopaque, []const u8, bool }) => {
return struct {
fn inner(data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.c) void {
// warning messages emitted from Lua should be null-terminated for display
const message = std.mem.span(@as([*:0]const u8, @ptrCast(msg)));
@call(.always_inline, function, .{ data, message, to_cont != 0 });
}
}.inner,
CUserAtomCallbackFn => struct {
fn inner(str: [*c]const u8, len: usize) callconv(.c) i16 {
if (str) |s| {
const buf = s[0..len];
return @call(.always_inline, function, .{buf});
}
}.inner;
},
// CWriterFn
Tuple(&.{ *Lua, []const u8, *anyopaque }) => {
return struct {
fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
const buffer = @as([*]const u8, @ptrCast(buf))[0..size];

const result = if (has_error_union) blk: {
break :blk @call(.always_inline, function, .{ lua, buffer, data.? }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else blk: {
break :blk @call(.always_inline, function, .{ lua, buffer, data.? });
return -1;
}
}.inner,
CWarnFn => if (lang != .lua54) @compileError("CWarnFn is only valid in Lua >= 5.4") else struct {
fn inner(data: ?*anyopaque, msg: [*c]const u8, to_cont: c_int) callconv(.c) void {
// warning messages emitted from Lua should be null-terminated for display
const message = std.mem.span(@as([*:0]const u8, @ptrCast(msg)));
@call(.always_inline, function, .{ data, message, to_cont != 0 });
}
}.inner,
CWriterFn => struct {
fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.c) c_int {
// this is called by Lua, state should never be null
var lua: *Lua = @ptrCast(state.?);
const buffer = @as([*]const u8, @ptrCast(buf))[0..size];

const result = if (has_error_union) blk: {
break :blk @call(.always_inline, function, .{ lua, buffer, data.? }) catch |err| {
lua.raiseErrorStr(@errorName(err), .{});
};
} else blk: {
break :blk @call(.always_inline, function, .{ lua, buffer, data.? });
};

// it makes more sense for the inner writer function to return false for failure,
// so negate the result here
return @intFromBool(!result);
}
}.inner;
},
else => @compileError("Unsupported function given to wrap '" ++ @typeName(@TypeOf(function)) ++ "'"),
}
// it makes more sense for the inner writer function to return false for failure,
// so negate the result here
return @intFromBool(!result);
}
}.inner,
else => unreachable,
};
}

/// Zig wrapper for Luau lua_CompileOptions that uses the same defaults as Luau if
Expand Down
Loading