diff --git a/build.zig b/build.zig index d74f44b..396dbac 100644 --- a/build.zig +++ b/build.zig @@ -27,7 +27,7 @@ pub fn build(b: *Build) void { std.debug.panic("Luau does not support compiling or loading shared modules", .{}); } - if (lua_user_h != null and (lang == .luajit or lang == .luau)) { + if (lua_user_h != null and (lang == .luajit or lang == .luau or lang == .lua55)) { std.debug.panic("Only basic lua supports a user provided header file", .{}); } @@ -58,6 +58,7 @@ pub fn build(b: *Build) void { .lua52 => zlua.linkSystemLibrary("lua5.2", .{ .preferred_link_mode = link_mode }), .lua53 => zlua.linkSystemLibrary("lua5.3", .{ .preferred_link_mode = link_mode }), .lua54 => zlua.linkSystemLibrary("lua5.4", .{ .preferred_link_mode = link_mode }), + .lua55 => zlua.linkSystemLibrary("lua5.5", .{ .preferred_link_mode = link_mode }), .luajit => zlua.linkSystemLibrary("luajit", .{ .preferred_link_mode = link_mode }), .luau => @panic("luau not supported for system lua"), } diff --git a/build.zig.zon b/build.zig.zon index fc508ca..d56d7dc 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -33,6 +33,12 @@ .lazy = true, }, + .lua55 = .{ + .url = "https://www.lua.org/ftp/lua-5.5.0.tar.gz", + .hash = "N-V-__8AAGuAFQAFQL34FRuOIGC9klkZq44uTTan4cUS9vas", + .lazy = true, + }, + .luajit = .{ .url = "https://github.com/LuaJIT/LuaJIT/archive/c525bcb9024510cad9e170e12b6209aedb330f83.tar.gz", .hash = "N-V-__8AACcgQgCuLYTPzCp6pnBmFJHyG77RAtM13hjOfTaG", diff --git a/build/lua.zig b/build/lua.zig index fd6f1cc..1c00038 100644 --- a/build/lua.zig +++ b/build/lua.zig @@ -10,6 +10,7 @@ pub const Language = enum { lua52, lua53, lua54, + lua55, luajit, luau, }; @@ -38,6 +39,7 @@ pub fn configure( .lua52 => .{ .major = 5, .minor = 2, .patch = 4 }, .lua53 => .{ .major = 5, .minor = 3, .patch = 6 }, .lua54 => .{ .major = 5, .minor = 4, .patch = 8 }, + .lua55 => .{ .major = 5, .minor = 5, .patch = 0 }, else => unreachable, }; @@ -83,6 +85,7 @@ pub fn configure( .lua52 => &lua_52_source_files, .lua53 => &lua_53_source_files, .lua54 => &lua_54_source_files, + .lua55 => &lua_55_source_files, else => unreachable, }; @@ -169,3 +172,10 @@ const lua_54_source_files = lua_base_source_files ++ [_][]const u8{ "src/lcorolib.c", "src/lutf8lib.c", }; + +const lua_55_source_files = lua_base_source_files ++ [_][]const u8{ + "src/ldo.c", + "src/lctype.c", + "src/lcorolib.c", + "src/lutf8lib.c", +}; diff --git a/build/luajit.zig b/build/luajit.zig index 48de014..f7a2db7 100644 --- a/build/luajit.zig +++ b/build/luajit.zig @@ -122,9 +122,6 @@ pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin. const buildvm_os_c_flags: []const []const u8 = if (target.result.os.tag == .windows) &.{"-DLUAJIT_OS=1"} - else if (target.result.os.tag.isDarwin()) - // FIXME: this can be removed once https://codeberg.org/ziglang/zig/issues/30669 is successfully resolved - &.{"-DLJ_NO_UNWIND=1"} else &.{}; @@ -208,7 +205,6 @@ pub fn configure(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin. library.is_linking_libc = true; lib.addCMacro("LUAJIT_UNWIND_EXTERNAL", ""); - lib.linkSystemLibrary("unwind", .{}); library.root_module.addIncludePath(upstream.path("src")); diff --git a/build/patch.zig b/build/patch.zig index d71a3a9..23deb01 100644 --- a/build/patch.zig +++ b/build/patch.zig @@ -1,7 +1,7 @@ //! A simple script to apply a patch to a file //! Does minimal validation and is just enough for patching Lua 5.1 -pub fn main() !void { +pub fn main(init: std.process.Init.Minimal) !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); @@ -10,12 +10,16 @@ pub fn main() !void { defer threaded.deinit(); const io = threaded.io(); - const args = try std.process.argsAlloc(allocator); - if (args.len != 4) @panic("Wrong number of arguments"); + var iter = init.args.iterate(); - const file_path = args[1]; - const patch_file_path = args[2]; - const output_path = args[3]; + // Skip executable name + _ = iter.next(); + + const file_path = iter.next() orelse @panic("Missing file_path argument"); + const patch_file_path = iter.next() orelse @panic("Missing patch_file_path argument"); + const output_path = iter.next() orelse @panic("Missing output_path argument"); + + if (iter.next() != null) @panic("Too many arguments"); const patch_file = patch_file: { const patch_file = try Io.Dir.cwd().openFile(io, patch_file_path, .{ .mode = .read_only }); diff --git a/makefile b/makefile index f1224ca..d3e61d2 100644 --- a/makefile +++ b/makefile @@ -6,6 +6,7 @@ test_zig_nightly: zig build test --summary failures -Dlang=lua52 zig build test --summary failures -Dlang=lua53 zig build test --summary failures -Dlang=lua54 + zig build test --summary failures -Dlang=lua55 zig build test --summary failures -Dlang=luau zig build install-example-interpreter @@ -21,6 +22,7 @@ test_zig_stable: zig build test --summary failures -Dlang=lua52 zig build test --summary failures -Dlang=lua53 zig build test --summary failures -Dlang=lua54 + zig build test --summary failures -Dlang=lua55 zig build test --summary failures -Dlang=luau zig build install-example-interpreter diff --git a/readme.md b/readme.md index a4199ad..b6228d0 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ [![shield showing current tests status](https://github.com/natecraddock/ziglua/actions/workflows/tests.yml/badge.svg)](https://github.com/natecraddock/ziglua/actions/workflows/tests.yml) [![Discord](https://img.shields.io/discord/1196908820140671077?style=flat&logo=discord)](https://discord.com/invite/XpZqDFvAtK) -Zig bindings for the [Lua C API](https://www.lua.org/manual/5.4/manual.html#4). Ziglua currently supports the latest releases of Lua 5.1, 5.2, 5.3, 5.4, and [Luau](https://luau-lang.org). +Zig bindings for the [Lua C API](https://www.lua.org/manual/5.4/manual.html#4). Ziglua currently supports the latest releases of Lua 5.1, 5.2, 5.3, 5.4, 5.5, [LuaJIT](https://luajit.org) and [Luau](https://luau-lang.org). Ziglua can be used in two ways, either * **embedded** to statically embed the Lua VM in a Zig program, diff --git a/src/lib.zig b/src/lib.zig index 85ef173..8d00fb6 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -77,7 +77,7 @@ const ArithOperator53 = enum(u4) { /// Operations supported by `Lua.arith()` pub const ArithOperator = switch (lang) { - .lua53, .lua54 => ArithOperator53, + .lua53, .lua54, .lua55 => ArithOperator53, else => ArithOperator52, }; @@ -260,6 +260,63 @@ const DebugInfo54 = struct { }; }; +/// The Lua debug interface structure for Lua 5.5 +const DebugInfo55 = struct { + source: [:0]const u8 = undefined, + src_len: usize = 0, + short_src: [c.LUA_IDSIZE:0]u8 = undefined, + + name: ?[:0]const u8 = undefined, + name_what: NameType = undefined, + what: FnType = undefined, + + current_line: ?i32 = null, + first_line_defined: ?i32 = null, + last_line_defined: ?i32 = null, + + num_upvalues: u8 = 0, + num_params: u8 = 0, + + is_vararg: bool = false, + is_tail_call: bool = false, + + first_transfer: i32 = 0, + num_transfer: i32 = 0, + + private: *anyopaque = undefined, + + pub const NameType = enum { global, local, method, field, upvalue, other }; + + pub const FnType = enum { lua, c, main }; + + pub const Options = packed struct { + @">": bool = false, + f: bool = false, + l: bool = false, + n: bool = false, + r: bool = false, + S: bool = false, + t: bool = false, + u: bool = false, + L: bool = false, + + fn toString(options: Options) [10:0]u8 { + var str = [_:0]u8{0} ** 10; + var index: u8 = 0; + + inline for (std.meta.fields(Options)) |field| { + if (@field(options, field.name)) { + str[index] = field.name[0]; + index += 1; + } + } + while (index < str.len) : (index += 1) str[index] = 0; + + return str; + } + }; +}; + pub const DebugInfoLuau = struct { source: [:0]const u8 = undefined, src_len: usize = 0, @@ -306,6 +363,7 @@ pub const DebugInfo = switch (lang) { .lua51, .luajit => DebugInfo51, .lua52, .lua53 => DebugInfo52, .lua54 => DebugInfo54, + .lua55 => DebugInfo55, .luau => DebugInfoLuau, }; @@ -349,7 +407,7 @@ const Event52 = enum(u3) { pub const Event = switch (lang) { .lua51, .luajit => Event51, - .lua52, .lua53, .lua54 => Event52, + .lua52, .lua53, .lua54, .lua55 => Event52, // TODO: probably something better than void here .luau => void, }; @@ -646,7 +704,10 @@ pub const Lua = opaque { const allocator_ptr = try a.create(Allocator); allocator_ptr.* = a; - if (c.lua_newstate(alloc, allocator_ptr)) |state| { + if (switch (lang) { + .lua55 => c.lua_newstate(alloc, allocator_ptr, 0), + else => c.lua_newstate(alloc, allocator_ptr), + }) |state| { return @ptrCast(state); } else return error.OutOfMemory; } @@ -786,13 +847,13 @@ pub const Lua = opaque { /// Context value passed to the continuation function ctx: switch (lang) { .lua52 => i32, - .lua53, .lua54 => Context, + .lua53, .lua54, .lua55 => Context, else => void, }, /// The continuation function k: switch (lang) { .lua52 => CFn, - .lua53, .lua54 => CContFn, + .lua53, .lua54, .lua55 => CContFn, else => void, }, }; @@ -972,7 +1033,7 @@ pub const Lua = opaque { /// /// See https://www.lua.org/manual/5.4/manual.html#lua_dump pub const dump = switch (lang) { - .lua53, .lua54 => dump53, + .lua53, .lua54, .lua55 => dump53, else => dump51, .luau => @compileError("Not implemented in Luau."), }; @@ -989,7 +1050,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.1/manual.html#lua_equal pub fn equal(lua: *Lua, index1: i32, index2: i32) bool { switch (lang) { - .lua52, .lua53, .lua54 => @compileError("lua_equal is deprecated, use Lua.compare with .eq instead"), + .lua52, .lua53, .lua54, .lua55 => @compileError("lua_equal is deprecated, use Lua.compare with .eq instead"), else => {}, } return c.lua_equal(@ptrCast(lua), index1, index2) == 1; @@ -1012,7 +1073,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCollect(lua: *Lua) void { _ = switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOLLECT), + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOLLECT), else => c.lua_gc(@ptrCast(lua), c.LUA_GCCOLLECT, 0), }; } @@ -1021,7 +1082,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcStop(lua: *Lua) void { _ = switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCSTOP), + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCSTOP), else => c.lua_gc(@ptrCast(lua), c.LUA_GCSTOP, 0), }; } @@ -1030,7 +1091,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcRestart(lua: *Lua) void { _ = switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCRESTART), + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCRESTART), else => c.lua_gc(@ptrCast(lua), c.LUA_GCRESTART, 0), }; } @@ -1045,7 +1106,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCount(lua: *Lua) i32 { return switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNT), + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNT), else => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNT, 0), }; } @@ -1054,7 +1115,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcCountB(lua: *Lua) i32 { return switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNTB), + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNTB), else => c.lua_gc(@ptrCast(lua), c.LUA_GCCOUNTB, 0), }; } @@ -1063,7 +1124,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub fn gcIsRunning(lua: *Lua) bool { return switch (lang) { - .lua54 => c.lua_gc(@ptrCast(lua), c.LUA_GCISRUNNING) != 0, + .lua54, .lua55 => c.lua_gc(@ptrCast(lua), c.LUA_GCISRUNNING) != 0, else => c.lua_gc(@ptrCast(lua), c.LUA_GCISRUNNING, 0) != 0, }; } @@ -1096,7 +1157,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gc pub const gcSetGenerational = switch (lang) { .lua52 => gcSetGenerational52, - .lua54 => gcSetGenerational54, + .lua54, .lua55 => gcSetGenerational54, else => @compileError("gcSetGenerational() not available"), }; @@ -1185,7 +1246,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_getfield pub fn getField(lua: *Lua, index: i32, key: [:0]const u8) LuaType { switch (lang) { - .lua53, .lua54, .luau => return @enumFromInt(c.lua_getfield(@ptrCast(lua), index, key.ptr)), + .lua53, .lua54, .lua55, .luau => return @enumFromInt(c.lua_getfield(@ptrCast(lua), index, key.ptr)), else => { c.lua_getfield(@ptrCast(lua), index, key.ptr); return lua.typeOf(-1); @@ -1205,7 +1266,7 @@ pub const Lua = opaque { pub fn getGlobal(lua: *Lua, name: [:0]const u8) !LuaType { const lua_type: LuaType = blk: { switch (lang) { - .lua53, .lua54, .luau => break :blk @enumFromInt(c.lua_getglobal(@as(*LuaState, @ptrCast(lua)), name.ptr)), + .lua53, .lua54, .lua55, .luau => break :blk @enumFromInt(c.lua_getglobal(@as(*LuaState, @ptrCast(lua)), name.ptr)), else => { c.lua_getglobal(@as(*LuaState, @ptrCast(lua)), name.ptr); break :blk lua.typeOf(-1); @@ -1256,7 +1317,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_getiuservalue pub const getUserValue = switch (lang) { .lua53 => getUserValue53, - .lua54 => getUserValue54, + .lua54, .lua55 => getUserValue54, else => getUserValue52, }; @@ -1285,7 +1346,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_gettable pub fn getTable(lua: *Lua, index: i32) LuaType { switch (lang) { - .lua53, .lua54, .luau => return @enumFromInt(c.lua_gettable(@ptrCast(lua), index)), + .lua53, .lua54, .lua55, .luau => return @enumFromInt(c.lua_gettable(@ptrCast(lua), index)), else => { c.lua_gettable(@ptrCast(lua), index); return lua.typeOf(-1); @@ -1549,7 +1610,7 @@ pub const Lua = opaque { const ret = c.lua_load(@ptrCast(lua), reader, data, chunk_name.ptr, mode_str.ptr); return switch (lang) { - .lua54 => switch (ret) { + .lua54, .lua55 => switch (ret) { StatusCode.ok => {}, StatusCode.err_syntax => error.LuaSyntax, StatusCode.err_memory => error.OutOfMemory, @@ -1626,7 +1687,7 @@ pub const Lua = opaque { } pub const newUserdata = switch (lang) { - .lua54 => newUserdata54, + .lua54, .lua55 => newUserdata54, else => newUserdata51, }; @@ -1649,7 +1710,7 @@ pub const Lua = opaque { } pub const newUserdataSlice = switch (lang) { - .lua54 => newUserdataSlice54, + .lua54, .lua55 => newUserdataSlice54, else => newUserdataSlice51, }; @@ -1767,7 +1828,7 @@ pub const Lua = opaque { }; return switch (lang) { - .lua54 => switch (ret) { + .lua54, .lua55 => switch (ret) { StatusCode.ok => return, StatusCode.err_runtime => return error.LuaRuntime, StatusCode.err_memory => return error.OutOfMemory, @@ -1802,13 +1863,13 @@ pub const Lua = opaque { /// Context value passed to the continuation function ctx: switch (lang) { .lua52 => i32, - .lua53, .lua54 => Context, + .lua53, .lua54, .lua55 => Context, else => void, }, /// The continuation function k: switch (lang) { .lua52 => CFn, - .lua53, .lua54 => CContFn, + .lua53, .lua54, .lua55 => CContFn, else => void, }, }; @@ -1827,7 +1888,7 @@ pub const Lua = opaque { }; return switch (lang) { - .lua54 => switch (ret) { + .lua54, .lua55 => switch (ret) { StatusCode.ok => return, StatusCode.err_runtime => return error.LuaRuntime, StatusCode.err_memory => return error.OutOfMemory, @@ -1842,7 +1903,7 @@ pub const Lua = opaque { StatusCode.err_gcmm => return error.LuaGCMetaMethod, else => unreachable, }, - else => @compileError("Only implemented in Lua 5.2, 5.3, and 5.4"), + else => @compileError("Only implemented in Lua 5.2, 5.3, 5.4, and 5.5"), }; } @@ -2150,7 +2211,7 @@ pub const Lua = opaque { /// TODO: should this be renamed to getTableRaw (seems more logical)? pub fn rawGetTable(lua: *Lua, index: i32) LuaType { switch (lang) { - .lua53, .lua54, .luau => return @enumFromInt(c.lua_rawget(@ptrCast(lua), index)), + .lua53, .lua54, .lua55, .luau => return @enumFromInt(c.lua_rawget(@ptrCast(lua), index)), else => { c.lua_rawget(@ptrCast(lua), index); return lua.typeOf(-1); @@ -2175,7 +2236,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_rawgeti pub fn rawGetIndex(lua: *Lua, index: i32, n: RawGetIndexNType) LuaType { switch (lang) { - .lua53, .lua54, .luau => return @enumFromInt(c.lua_rawgeti(@ptrCast(lua), index, n)), + .lua53, .lua54, .lua55, .luau => return @enumFromInt(c.lua_rawgeti(@ptrCast(lua), index, n)), else => { c.lua_rawgeti(@ptrCast(lua), index, n); return lua.typeOf(-1); @@ -2195,7 +2256,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#lua_rawgetp pub fn rawGetPtr(lua: *Lua, index: i32, p: *const anyopaque) LuaType { switch (lang) { - .lua53, .lua54 => return @enumFromInt(c.lua_rawgetp(@ptrCast(lua), index, p)), + .lua53, .lua54, .lua55 => return @enumFromInt(c.lua_rawgetp(@ptrCast(lua), index, p)), else => { c.lua_rawgetp(@ptrCast(lua), index, p); return lua.typeOf(-1); @@ -2359,7 +2420,7 @@ pub const Lua = opaque { pub const resumeThread = switch (lang) { .lua51, .luajit => resumeThread51, .lua52, .lua53 => resumeThread52, - .lua54 => resumeThread54, + .lua54, .lua55 => resumeThread54, .luau => resumeThreadLuau, }; @@ -2451,7 +2512,7 @@ pub const Lua = opaque { } pub const setUserValue = switch (lang) { - .lua54 => setUserValue54, + .lua54, .lua55 => setUserValue54, else => setUserValue52, }; @@ -2874,7 +2935,7 @@ pub const Lua = opaque { /// /// See https://www.lua.org/manual/5.4/manual.html#lua_version pub const version = switch (lang) { - .lua54 => version54, + .lua54, .lua55 => version54, else => version52, }; @@ -2934,7 +2995,7 @@ pub const Lua = opaque { /// This function never returns /// See https://www.lua.org/manual/5.4/manual.html#lua_yieldk pub const yieldCont = switch (lang) { - .lua53, .lua54 => yieldCont53, + .lua53, .lua54, .lua55 => yieldCont53, else => yieldCont52, }; @@ -3005,7 +3066,7 @@ pub const Lua = opaque { unreachable; }; } - if (lang == .lua54 and options.r) { + if ((lang == .lua54 or lang == .lua55) and options.r) { info.first_transfer = ar.ftransfer; info.num_transfer = ar.ntransfer; } @@ -3562,7 +3623,7 @@ pub const Lua = opaque { /// /// See https://www.lua.org/manual/5.4/manual.html#luaL_checkversion pub const checkVersion = switch (lang) { - .lua53, .lua54 => checkVersion53, + .lua53, .lua54, .lua55 => checkVersion53, else => checkVersion52, }; @@ -3677,7 +3738,7 @@ pub const Lua = opaque { /// See https://www.lua.org/manual/5.4/manual.html#luaL_getmetatable pub fn getMetatableRegistry(lua: *Lua, table_name: [:0]const u8) LuaType { switch (lang) { - .lua53, .lua54, .luau => return @enumFromInt(c.luaL_getmetatable(@as(*LuaState, @ptrCast(lua)), table_name.ptr)), + .lua53, .lua54, .lua55, .luau => return @enumFromInt(c.luaL_getmetatable(@as(*LuaState, @ptrCast(lua)), table_name.ptr)), else => { c.luaL_getmetatable(@as(*LuaState, @ptrCast(lua)), table_name.ptr); return lua.typeOf(-1); @@ -4239,7 +4300,7 @@ pub const Lua = opaque { /// /// See https://www.lua.org/manual/5.4/manual.html#luaL_openlibs pub fn openLibs(lua: *Lua) void { - c.luaL_openlibs(@ptrCast(lua)); + c.luaL_openlibs(@as(*LuaState, @ptrCast(lua))); } /// Open the basic standard library @@ -4249,7 +4310,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openBase(lua: *Lua) void { lua.requireF("_G", c.luaopen_base, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the coroutine standard library @@ -4261,7 +4322,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openCoroutine(lua: *Lua) void { lua.requireF(c.LUA_COLIBNAME, c.luaopen_coroutine, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the package standard library @@ -4274,7 +4335,7 @@ pub const Lua = opaque { pub fn openPackage(lua: *Lua) void { if (lang == .luau) @compileError(@src().fn_name ++ " is not available in Luau."); lua.requireF(c.LUA_LOADLIBNAME, c.luaopen_package, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the string standard library @@ -4284,7 +4345,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openString(lua: *Lua) void { lua.requireF(c.LUA_STRLIBNAME, c.luaopen_string, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the UTF-8 standard library @@ -4296,7 +4357,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openUtf8(lua: *Lua) void { lua.requireF(c.LUA_UTF8LIBNAME, c.luaopen_utf8, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the table standard library @@ -4306,7 +4367,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openTable(lua: *Lua) void { lua.requireF(c.LUA_TABLIBNAME, c.luaopen_table, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the math standard library @@ -4316,7 +4377,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openMath(lua: *Lua) void { lua.requireF(c.LUA_MATHLIBNAME, c.luaopen_math, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the io standard library @@ -4329,7 +4390,7 @@ pub const Lua = opaque { pub fn openIO(lua: *Lua) void { if (lang == .luau) @compileError(@src().fn_name ++ " is not available in Luau."); lua.requireF(c.LUA_IOLIBNAME, c.luaopen_io, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the os standard library @@ -4339,7 +4400,7 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openOS(lua: *Lua) void { lua.requireF(c.LUA_OSLIBNAME, c.luaopen_os, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the debug standard library @@ -4349,18 +4410,25 @@ pub const Lua = opaque { /// * Errors: `other` pub fn openDebug(lua: *Lua) void { lua.requireF(c.LUA_DBLIBNAME, c.luaopen_debug, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Open the bit32 standard library /// + /// Only available in Lua 5.2 (and deprecated in Lua 5.3) and LuaJIT + /// /// * Pops: `0` /// * Pushes: `0` /// * Errors: `other` pub fn openBit32(lua: *Lua) void { - lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit32, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + switch (lang) { + .lua52, .lua53 => lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit32, true), + .luajit => lua.requireF(c.LUA_BITLIBNAME, c.luaopen_bit, true), + else => @compileError(@src().fn_name ++ " is only available in Lua 5.2 and LuaJIT."), + } + lua.pop(1); } + pub const openBit = openBit32; /// Open the vector standard library /// @@ -4372,7 +4440,7 @@ pub const Lua = opaque { pub fn openVector(lua: *Lua) void { if (lang == .luau) @compileError(@src().fn_name ++ " is only available in Luau."); lua.requireF(c.LUA_VECLIBNAME, c.luaopen_vector, true); - if (lang == .lua52 or lang == .lua53 or lang == .lua54) lua.pop(1); + if (lang == .lua52 or lang == .lua53 or lang == .lua54 or lang == .lua55) lua.pop(1); } /// Returns if given typeinfo is a string type @@ -5317,6 +5385,8 @@ pub fn wrap(comptime function: anytype) TypeOfWrap(function) { }.inner, CWriterFn => struct { fn inner(state: ?*LuaState, buf: ?*const anyopaque, size: usize, data: ?*anyopaque) callconv(.c) c_int { + // Lua 5.5 calls the writer with null at the end of dump + if (lang == .lua55 and buf == null) return 0; // this is called by Lua, state should never be null var lua: *Lua = @ptrCast(state.?); const buffer = @as([*]const u8, @ptrCast(buf))[0..size]; diff --git a/src/tests.zig b/src/tests.zig index 0158c3d..220cce2 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -87,7 +87,7 @@ test "standard library loading" { lua.openOS(); lua.openDebug(); - if (zlua.lang != .lua51 and zlua.lang != .lua53 and zlua.lang != .lua54) lua.openBit32(); + if (zlua.lang == .lua52 or zlua.lang == .luajit) lua.openBit32(); if (zlua.lang != .luau) { lua.openPackage(); @@ -118,7 +118,7 @@ test "number conversion success and failure" { } test "arithmetic (lua_arith)" { - if (!langIn(.{ .lua52, .lua53, .lua54 })) return; + if (!langIn(.{ .lua52, .lua53, .lua54, .lua55 })) return; const lua: *Lua = try .init(testing.allocator); defer lua.deinit(); @@ -191,7 +191,7 @@ test "compare" { lua.pushNumber(1); lua.pushNumber(2); - if (langIn(.{ .lua52, .lua53, .lua54 })) { + if (langIn(.{ .lua52, .lua53, .lua54, .lua55 })) { try expect(!lua.compare(-2, -1, .eq)); try expect(!lua.compare(-1, -2, .le)); try expect(!lua.compare(-1, -2, .lt)); @@ -369,7 +369,7 @@ test "stack manipulation" { defer lua.deinit(); // TODO: combine these more - if (zlua.lang == .lua53 or zlua.lang == .lua54) { + if (zlua.lang == .lua53 or zlua.lang == .lua54 or zlua.lang == .lua55) { var num: i32 = 1; while (num <= 10) : (num += 1) { lua.pushInteger(num); @@ -479,6 +479,7 @@ test "version" { .lua52 => try expectEqual(502, lua.version(false).*), .lua53 => try expectEqual(503, lua.version(false).*), .lua54 => try expectEqual(504, lua.version()), + .lua55 => try expectEqual(505, lua.version()), else => unreachable, } @@ -697,7 +698,7 @@ test "concat" { _ = lua.pushStringZ(" wow!"); lua.concat(3); - if (zlua.lang == .lua53 or zlua.lang == .lua54) { + if (zlua.lang == .lua53 or zlua.lang == .lua54 or zlua.lang == .lua55) { try expectEqualStrings("hello 10.0 wow!", try lua.toString(-1)); } else { try expectEqualStrings("hello 10 wow!", try lua.toString(-1)); @@ -727,7 +728,7 @@ test "garbage collector" { if (zlua.lang == .lua52) { lua.gcSetGenerational(); lua.gcSetGenerational(); - } else if (zlua.lang == .lua54) { + } else if (zlua.lang == .lua54 or zlua.lang == .lua55) { try expect(lua.gcSetGenerational(0, 10)); try expect(lua.gcSetIncremental(0, 0, 0)); try expect(!lua.gcSetIncremental(0, 0, 0)); @@ -739,7 +740,7 @@ test "garbage collector" { } test "extra space" { - if (zlua.lang != .lua53 and zlua.lang != .lua54) return; + if (zlua.lang != .lua53 and zlua.lang != .lua54 and zlua.lang != .lua55) return; const lua: *Lua = try .init(testing.allocator); defer lua.deinit(); @@ -758,13 +759,13 @@ test "table access" { try lua.doString("a = { [1] = 'first', key = 'value', ['other one'] = 1234 }"); _ = try lua.getGlobal("a"); - if (zlua.lang == .lua53 or zlua.lang == .lua54) { + if (zlua.lang == .lua53 or zlua.lang == .lua54 or zlua.lang == .lua55) { try expectEqual(.string, lua.rawGetIndex(1, 1)); try expectEqualStrings("first", try lua.toString(-1)); } try expectEqual(.string, switch (zlua.lang) { - .lua53, .lua54 => lua.getIndex(1, 1), + .lua53, .lua54, .lua55 => lua.getIndex(1, 1), else => lua.rawGetIndex(1, 1), }); try expectEqualStrings("first", try lua.toString(-1)); @@ -812,7 +813,7 @@ test "table access" { var index: i32 = 1; while (index <= 5) : (index += 1) { lua.pushInteger(index); - if (zlua.lang == .lua53 or zlua.lang == .lua54) lua.setIndex(-2, index) else lua.rawSetIndex(-2, index); + if (zlua.lang == .lua53 or zlua.lang == .lua54 or zlua.lang == .lua55) lua.setIndex(-2, index) else lua.rawSetIndex(-2, index); } if (!langIn(.{ .lua51, .luajit, .luau })) { @@ -828,7 +829,7 @@ test "table access" { } test "conversions" { - if (zlua.lang != .lua53 and zlua.lang != .lua54) return; + if (zlua.lang != .lua53 and zlua.lang != .lua54 and zlua.lang != .lua55) return; const lua: *Lua = try .init(testing.allocator); defer lua.deinit(); @@ -885,15 +886,20 @@ test "dump and load" { defer buffer.deinit(std.testing.allocator); // save the function as a binary chunk in the buffer - if (zlua.lang == .lua53 or zlua.lang == .lua54) { + if (zlua.lang == .lua53 or zlua.lang == .lua54 or zlua.lang == .lua55) { try lua.dump(zlua.wrap(writer), &buffer, false); } else { try lua.dump(zlua.wrap(writer), &buffer); } // clear the stack - if (zlua.lang == .lua54) { - try lua.closeThread(lua); + if (zlua.lang == .lua54 or zlua.lang == .lua55) { + // NOTE: for closeThread, passing `lua` as `from` when L==from means + // "thread closing itself" which only works inside a resume (Lua 5.5+). + // Pass null to reset the thread normally. + // See: https://www.lua.org/manual/5.4/manual.html#lua_closethread + // https://www.lua.org/manual/5.5/manual.html#lua_closethread + try lua.closeThread(null); } else lua.setTop(0); const reader = struct { @@ -947,7 +953,7 @@ test "userdata and uservalues" { }; // create a Lua-owned pointer to a Data with 2 associated user values - var data = if (zlua.lang == .lua54) lua.newUserdata(Data, 2) else lua.newUserdata(Data); + var data = if (zlua.lang == .lua54 or zlua.lang == .lua55) lua.newUserdata(Data, 2) else lua.newUserdata(Data); data.val = 1; @memcpy(&data.code, "abcd"); @@ -961,7 +967,7 @@ test "userdata and uservalues" { _ = lua.getUserValue(1); try expectEqual(.nil, lua.typeOf(-1)); - } else if (zlua.lang == .lua54) { + } else if (zlua.lang == .lua54 or zlua.lang == .lua55) { // assign the user values lua.pushNumber(1234.56); try lua.setUserValue(1, 1); @@ -1120,7 +1126,7 @@ fn continuation(l: *Lua, status: zlua.Status, ctx: isize) i32 { } test "yielding" { - if (zlua.lang != .lua53 and zlua.lang != .lua54) return; + if (zlua.lang != .lua53 and zlua.lang != .lua54 and zlua.lang != .lua55) return; const lua: *Lua = try .init(testing.allocator); defer lua.deinit(); @@ -1139,7 +1145,7 @@ test "yielding" { try expect(!lua.isYieldable()); var i: i32 = 0; - if (zlua.lang == .lua54) { + if (zlua.lang == .lua54 or zlua.lang == .lua55) { try expect(thread.isYieldable()); var results: i32 = undefined; @@ -1151,6 +1157,7 @@ test "yielding" { try expectEqual(.ok, try thread.resumeThread(lua, 0, &results)); } else { + // Lua 5.3 try expect(!thread.isYieldable()); while (i < 5) : (i += 1) { @@ -1228,8 +1235,6 @@ test "yielding no continuation" { } test "resuming" { - if (zlua.lang == .lua54) return; - const lua: *Lua = try .init(testing.allocator); defer lua.deinit(); @@ -1249,13 +1254,25 @@ test "resuming" { ); _ = try thread.getGlobal("counter"); + var num_results: i32 = 0; var i: i32 = 1; while (i <= 5) : (i += 1) { - try expectEqual(.yield, if (zlua.lang == .lua51 or zlua.lang == .luajit) try thread.resumeThread(0) else try thread.resumeThread(lua, 0)); + try expectEqual(.yield, switch (zlua.lang) { + .lua51, .luajit => thread.resumeThread(0), + .lua54, .lua55 => thread.resumeThread(lua, 0, &num_results), + else => thread.resumeThread(lua, 0), + }); + try expectEqual(i, thread.toInteger(-1)); lua.pop(lua.getTop()); } - try expectEqual(.ok, if (zlua.lang == .lua51 or zlua.lang == .luajit) try thread.resumeThread(0) else try thread.resumeThread(lua, 0)); + + try expectEqual(.ok, switch (zlua.lang) { + .lua51, .luajit => thread.resumeThread(0), + .lua54, .lua55 => thread.resumeThread(lua, 0, &num_results), + else => thread.resumeThread(lua, 0), + }); + try expectEqualStrings("done", try thread.toString(-1)); } @@ -1330,7 +1347,7 @@ test "aux check functions" { lua.pushFunction(function); // test pushFail here (currently acts the same as pushNil) - if (zlua.lang == .lua54) lua.pushFail() else lua.pushNil(); + if (zlua.lang == .lua54 or zlua.lang == .lua55) lua.pushFail() else lua.pushNil(); lua.pushInteger(3); lua.pushNumber(4); _ = lua.pushString("hello world"); @@ -1653,7 +1670,7 @@ test "userdata" { lua.pushFunction(checkUdata); { - var t = if (zlua.lang == .lua54) lua.newUserdata(Type, 0) else lua.newUserdata(Type); + var t = if (zlua.lang == .lua54 or zlua.lang == .lua55) lua.newUserdata(Type, 0) else lua.newUserdata(Type); if (langIn(.{ .lua51, .luajit, .luau })) { _ = lua.getField(zlua.registry_index, "Type"); lua.setMetatable(-2); @@ -1687,7 +1704,7 @@ test "userdata" { lua.pushFunction(testUdata); { - var t = if (zlua.lang == .lua54) lua.newUserdata(Type, 0) else lua.newUserdata(Type); + var t = if (zlua.lang == .lua54 or zlua.lang == .lua55) lua.newUserdata(Type, 0) else lua.newUserdata(Type); lua.setMetatableRegistry("Type"); t.a = 1234; t.b = 3.14; @@ -1707,7 +1724,7 @@ test "userdata slices" { try lua.newMetatable("FixedArray"); // create an array of 10 - const slice = if (zlua.lang == .lua54) lua.newUserdataSlice(Integer, 10, 0) else lua.newUserdataSlice(Integer, 10); + const slice = if (zlua.lang == .lua54 or zlua.lang == .lua55) lua.newUserdataSlice(Integer, 10, 0) else lua.newUserdataSlice(Integer, 10); if (langIn(.{ .lua51, .luajit, .luau })) { _ = lua.getField(zlua.registry_index, "FixedArray"); lua.setMetatable(-2); @@ -1826,9 +1843,9 @@ test "debug interface" { fn inner(l: *Lua, event: zlua.Event, i: *DebugInfo) !void { switch (event) { .call => { - if (zlua.lang == .lua54) l.getInfo(.{ .l = true, .r = true }, i) else l.getInfo(.{ .l = true }, i); + if (zlua.lang == .lua54 or zlua.lang == .lua55) l.getInfo(.{ .l = true, .r = true }, i) else l.getInfo(.{ .l = true }, i); if (i.current_line.? != 2) std.debug.panic("Expected line to be 2", .{}); - _ = if (zlua.lang == .lua54) try l.getLocal(i, i.first_transfer) else try l.getLocal(i, 1); + _ = if (zlua.lang == .lua54 or zlua.lang == .lua55) try l.getLocal(i, i.first_transfer) else try l.getLocal(i, 1); if ((try l.toNumber(-1)) != 3) std.debug.panic("Expected x to equal 3", .{}); }, .line => if (i.current_line.? == 4) { @@ -1837,9 +1854,9 @@ test "debug interface" { _ = try l.setLocal(i, 2); }, .ret => { - if (zlua.lang == .lua54) l.getInfo(.{ .l = true, .r = true }, i) else l.getInfo(.{ .l = true }, i); + if (zlua.lang == .lua54 or zlua.lang == .lua55) l.getInfo(.{ .l = true, .r = true }, i) else l.getInfo(.{ .l = true }, i); if (i.current_line.? != 4) std.debug.panic("Expected line to be 4", .{}); - _ = if (zlua.lang == .lua54) try l.getLocal(i, i.first_transfer) else try l.getLocal(i, 1); + _ = if (zlua.lang == .lua54 or zlua.lang == .lua55) try l.getLocal(i, i.first_transfer) else try l.getLocal(i, 1); if ((try l.toNumber(-1)) != 3) std.debug.panic("Expected result to equal 3", .{}); }, else => unreachable, @@ -1978,7 +1995,7 @@ test "debug upvalues" { _ = try lua.setUpvalue(-2, 1); // test a bad index (the valid one's result is unpredicable) - if (zlua.lang == .lua54) try expectError(error.LuaError, lua.upvalueId(-1, 2)); + if (zlua.lang == .lua54 or zlua.lang == .lua55) try expectError(error.LuaError, lua.upvalueId(-1, 2)); // call the new function (should return 7) lua.pushNumber(2); @@ -2070,7 +2087,7 @@ test "userdata dtor" { gc_hits_ptr: *i32, pub fn dtor(udata: *anyopaque) void { - const self: *@This() = @alignCast(@ptrCast(udata)); + const self: *@This() = @ptrCast(@alignCast(udata)); self.gc_hits_ptr.* = self.gc_hits_ptr.* + 1; } };