Skip to content

Commit 737beb7

Browse files
committed
tests: add debug interface test
Tests the getInfo function
1 parent 0582f77 commit 737beb7

File tree

2 files changed

+167
-15
lines changed

2 files changed

+167
-15
lines changed

src/tests.zig

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,38 @@ test "yielding" {
977977
try expectEqualStrings("done", try thread.toBytes(-1));
978978
}
979979

980+
test "debug interface" {
981+
var lua = try Lua.init(testing.allocator);
982+
defer lua.deinit();
983+
984+
try lua.doString(
985+
\\f = function(x)
986+
\\ return x + 1
987+
\\end
988+
);
989+
_ = lua.getGlobal("f");
990+
991+
var info = lua.getInfo(.{
992+
.@">" = true,
993+
.l = true,
994+
.S = true,
995+
.n = true,
996+
.u = true,
997+
.t = true,
998+
});
999+
1000+
try expectEqual(ziglua.DebugInfo.FnType.lua, info.what);
1001+
try expectEqual(ziglua.DebugInfo.NameType.other, info.name_what);
1002+
const len = std.mem.len(@ptrCast([*:0]u8, &info.short_src));
1003+
try expectEqualStrings("[string \"f = function(x)...\"]", info.short_src[0..len]);
1004+
try expectEqual(@as(?i32, 1), info.first_line_defined);
1005+
try expectEqual(@as(?i32, 3), info.last_line_defined);
1006+
try expectEqual(@as(u8, 1), info.num_params);
1007+
try expectEqual(@as(u8, 0), info.num_upvalues);
1008+
try expect(!info.is_tail_call);
1009+
try expectEqual(@as(?i32, null), info.current_line);
1010+
}
1011+
9801012
test "refs" {
9811013
// temporary test that includes a reference to all functions so
9821014
// they will be type-checked
@@ -985,7 +1017,6 @@ test "refs" {
9851017
_ = Lua.getHook;
9861018
_ = Lua.getHookCount;
9871019
_ = Lua.getHookMask;
988-
_ = Lua.getInfo;
9891020
_ = Lua.getLocal;
9901021
_ = Lua.getStack;
9911022
_ = Lua.getUpvalue;

src/ziglua.zig

Lines changed: 135 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,64 @@ pub const CompareOperator = enum(u2) {
5353
le = c.LUA_OPLE,
5454
};
5555

56+
/// The internal Lua debug structure
57+
const Debug = c.lua_Debug;
58+
5659
/// The Lua debug interface structure
57-
pub const DebugInfo = c.lua_Debug;
60+
pub const DebugInfo = struct {
61+
source: [:0]const u8 = undefined,
62+
src_len: usize = 0,
63+
short_src: [c.LUA_IDSIZE:0]u8 = undefined,
64+
65+
name: ?[:0]const u8 = undefined,
66+
name_what: NameType = undefined,
67+
what: FnType = undefined,
68+
69+
current_line: ?i32 = null,
70+
first_line_defined: ?i32 = null,
71+
last_line_defined: ?i32 = null,
72+
73+
num_upvalues: u8 = 0,
74+
num_params: u8 = 0,
75+
76+
is_vararg: bool = false,
77+
is_tail_call: bool = false,
78+
79+
first_transfer: u16 = 0,
80+
num_transfer: u16 = 0,
81+
82+
private: *anyopaque = undefined,
83+
84+
pub const NameType = enum { global, local, method, field, upvalue, other };
85+
86+
pub const FnType = enum { lua, c, main };
87+
88+
pub const Options = packed struct {
89+
@">": bool = false,
90+
f: bool = false,
91+
l: bool = false,
92+
n: bool = false,
93+
r: bool = false,
94+
S: bool = false,
95+
t: bool = false,
96+
u: bool = false,
97+
L: bool = false,
98+
99+
fn toString(options: Options) [10:0]u8 {
100+
var str = [_:0]u8{0} ** 10;
101+
var index: u8 = 0;
102+
103+
inline for (std.meta.fields(Options)) |field| {
104+
if (@field(options, field.name)) {
105+
str[index] = field.name[0];
106+
index += 1;
107+
}
108+
}
109+
110+
return str;
111+
}
112+
};
113+
};
58114

59115
/// The superset of all errors returned from ziglua
60116
pub const Error = error{
@@ -72,6 +128,15 @@ pub const Error = error{
72128
File,
73129
};
74130

131+
/// The type of event that triggers a hook
132+
pub const Event = enum(u3) {
133+
call = c.LUA_HOOKCALL,
134+
ret = c.LUA_HOOKRET,
135+
line = c.LUA_HOOKLINE,
136+
count = c.LUA_HOOKCOUNT,
137+
tail_call = c.LUA_HOOKTAILCALL,
138+
};
139+
75140
/// Type for arrays of functions to be registered
76141
pub const FnReg = struct {
77142
name: [:0]const u8,
@@ -92,7 +157,7 @@ pub const GCAction = enum(u5) {
92157
};
93158

94159
/// Type for debugging hook functions
95-
pub const CHookFn = fn (state: ?*LuaState, ar: ?*DebugInfo) callconv(.C) void;
160+
pub const CHookFn = fn (state: ?*LuaState, ar: ?*Debug) callconv(.C) void;
96161

97162
/// Specifies on which events the hook will be called
98163
pub const HookMask = packed struct {
@@ -1060,22 +1125,72 @@ pub const Lua = struct {
10601125
}
10611126

10621127
/// Gets information about a specific function or function invocation
1063-
/// TODO: look at possible types for what
1064-
pub fn getInfo(lua: *Lua, what: [:0]const u8, ar: *DebugInfo) bool {
1065-
return c.lua_getinfo(lua.state, what, ar) != 0;
1128+
/// Returns an error if an invalid option was given, but the valid options
1129+
/// are still handled
1130+
pub fn getInfo(lua: *Lua, options: DebugInfo.Options) DebugInfo {
1131+
const str = options.toString();
1132+
var ar: Debug = undefined;
1133+
1134+
// should never fail because we are controlling options with the struct param
1135+
std.debug.assert(c.lua_getinfo(lua.state, &str, &ar) != 0);
1136+
1137+
// copy data into a struct
1138+
var info: DebugInfo = undefined;
1139+
if (options.l) info.current_line = if (ar.currentline == -1) null else ar.currentline;
1140+
if (options.n) {
1141+
info.name = if (ar.name != null) std.mem.span(ar.name) else null;
1142+
info.name_what = blk: {
1143+
const what = std.mem.span(ar.namewhat);
1144+
if (std.mem.eql(u8, "global", what)) break :blk .global;
1145+
if (std.mem.eql(u8, "local", what)) break :blk .local;
1146+
if (std.mem.eql(u8, "method", what)) break :blk .method;
1147+
if (std.mem.eql(u8, "field", what)) break :blk .field;
1148+
if (std.mem.eql(u8, "upvalue", what)) break :blk .upvalue;
1149+
if (what.len == 0) break :blk .other;
1150+
unreachable;
1151+
};
1152+
}
1153+
if (options.r) {
1154+
info.first_transfer = ar.ftransfer;
1155+
info.num_transfer = ar.ntransfer;
1156+
}
1157+
if (options.S) {
1158+
info.source = std.mem.span(ar.source);
1159+
std.mem.copy(u8, &info.short_src, &ar.short_src);
1160+
info.first_line_defined = ar.linedefined;
1161+
info.last_line_defined = ar.lastlinedefined;
1162+
info.what = blk: {
1163+
const what = std.mem.span(ar.what);
1164+
if (std.mem.eql(u8, "Lua", what)) break :blk .lua;
1165+
if (std.mem.eql(u8, "C", what)) break :blk .c;
1166+
if (std.mem.eql(u8, "main", what)) break :blk .main;
1167+
unreachable;
1168+
};
1169+
}
1170+
if (options.t) info.is_tail_call = ar.istailcall != 0;
1171+
if (options.u) {
1172+
info.num_upvalues = ar.nups;
1173+
info.num_params = ar.nparams;
1174+
info.is_vararg = ar.isvararg != 0;
1175+
}
1176+
return info;
10661177
}
10671178

10681179
/// Gets information about a local variable
1069-
pub fn getLocal(lua: *Lua, ar: *DebugInfo, n: i32) ![:0]const u8 {
1070-
if (c.lua_getlocal(lua.state, ar, n)) |name| {
1180+
pub fn getLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 {
1181+
var ar: Debug = undefined;
1182+
ar.i_ci = @ptrCast(*c.struct_CallInfo, info.private);
1183+
if (c.lua_getlocal(lua.state, &ar, n)) |name| {
10711184
return std.mem.span(name);
10721185
}
10731186
return Error.Fail;
10741187
}
10751188

10761189
/// Gets information about the interpreter runtime stack
1077-
pub fn getStack(lua: *Lua, level: i32, ar: *DebugInfo) bool {
1078-
return c.lua_getstack(lua.state, level, ar) != 0;
1190+
pub fn getStack(lua: *Lua, level: i32) !DebugInfo {
1191+
var ar: Debug = undefined;
1192+
if (c.lua_getstack(lua.state, level, &ar) == 0) return Error.Fail;
1193+
return DebugInfo{ .private = ar.i_ci.? };
10791194
}
10801195

10811196
/// Gets information about the `n`th upvalue of the closure at index `func_index`
@@ -1094,8 +1209,10 @@ pub const Lua = struct {
10941209

10951210
/// Sets the value of a local variable
10961211
/// Returns an error when the index is greater than the number of active locals
1097-
pub fn setLocal(lua: *Lua, ar: *DebugInfo, n: i32) ![:0]const u8 {
1098-
if (c.lua_setlocal(lua.state, ar, n)) |name| {
1212+
pub fn setLocal(lua: *Lua, info: *DebugInfo, n: i32) ![:0]const u8 {
1213+
var ar: Debug = undefined;
1214+
ar.i_ci = @ptrCast(*c.struct_CallInfo, info.private);
1215+
if (c.lua_setlocal(lua.state, &ar, n)) |name| {
10991216
return std.mem.span(name);
11001217
}
11011218
return Error.Fail;
@@ -1660,7 +1777,7 @@ pub inline fn opaqueCast(comptime T: type, ptr: *anyopaque) *T {
16601777
}
16611778

16621779
pub const ZigFn = fn (lua: *Lua) i32;
1663-
pub const ZigHookFn = fn (lua: *Lua, ar: *DebugInfo) void;
1780+
pub const ZigHookFn = fn (lua: *Lua, event: Event, info: *DebugInfo) void;
16641781
pub const ZigContFn = fn (lua: *Lua, status: Status, ctx: Context) i32;
16651782
pub const ZigReaderFn = fn (lua: *Lua, data: *anyopaque) ?[]const u8;
16661783
pub const ZigWarnFn = fn (data: ?*anyopaque, msg: []const u8, to_cont: bool) void;
@@ -1711,10 +1828,14 @@ fn wrapZigFn(comptime f: ZigFn) CFn {
17111828
/// Wrap a ZigHookFn in a CHookFn for passing to the API
17121829
fn wrapZigHookFn(comptime f: ZigHookFn) CHookFn {
17131830
return struct {
1714-
fn inner(state: ?*LuaState, ar: *DebugInfo) callconv(.C) void {
1831+
fn inner(state: ?*LuaState, ar: ?*Debug) callconv(.C) void {
17151832
// this is called by Lua, state should never be null
17161833
var lua: Lua = .{ .state = state.? };
1717-
@call(.{ .modifier = .always_inline }, f, .{ &lua, ar.? });
1834+
var info: DebugInfo = .{
1835+
.current_line = if (ar.?.currentline == -1) null else ar.?.currentline,
1836+
.private = ar.i_ci,
1837+
};
1838+
@call(.{ .modifier = .always_inline }, f, .{ &lua, @intToEnum(Event, ar.?.event), info });
17181839
}
17191840
}.inner;
17201841
}

0 commit comments

Comments
 (0)