Skip to content

Commit 0dd090d

Browse files
authored
⭐Add support for lua.getUpvalue() and lua.setUpvalue() to finish Zig API for great good (#12)
1 parent 144f97e commit 0dd090d

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ you.
8787
|-----------------------------|---------------------------------------|
8888
| Lua C API (`lua_*`) | 🎉 100% coverage<sup>†</sup> (92/92) |
8989
| Auxilary Library (`luaL_*`) | 🤩 100% coverage (48/48) |
90-
| Debug API (`lua_Debug`) | 72% coverage (9/12) |
90+
| Debug API (`lua_Debug`) | 🥳 100% coverage (12/12) |
9191
| LuaJIT Extensions | *No plans to implement.* |
9292

9393
*†: Coroutine yield/resume is not yet part of the public `zig-luajit` Zig API, see [#6][ISSUE-6].*
@@ -286,8 +286,8 @@ This section describes the current status of Zig language bindings ("the Zig API
286286
| `lua_sethook` | ☑️ `lua.setHook()` |
287287
| `lua_getlocal` | ☑️ `lua.getLocal()` |
288288
| `lua_setlocal` | ☑️ `lua.setLocal()` |
289-
| `lua_getupvalue` ||
290-
| `lua_setupvalue` ||
289+
| `lua_getupvalue` | ☑️ `lua.getUpvalue()` |
290+
| `lua_setupvalue` | ☑️ `lua.setUpvalue()` |
291291

292292

293293
## Additions to the API in `zig-luajit`

src/root.zig

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,33 @@ pub const Lua = opaque {
30673067
const str: ?[*:0]const u8 = @ptrCast(c.lua_setlocal(asState(lua), @ptrCast(info), index));
30683068
return asSlice(str);
30693069
}
3070+
3071+
/// Gets information about a closure's upvalue. For Lua functions, upvalues are the external local variables
3072+
/// that the function uses, and that are consequently included in its closure. This function gets the index
3073+
/// `index` of an upvalue, pushes the upvalue's value onto the stack, and returns its name. The `funcindex`
3074+
/// points to the closure in the stack. Returns `null` (and pushes nothing) when the index is greater
3075+
/// than the number of upvalues.
3076+
///
3077+
/// For C functions, this function uses the empty string `""` as a name for all upvalues.
3078+
///
3079+
/// From: `const char *lua_getupvalue(lua_State *L, int funcindex, int n);`
3080+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_getupvalue
3081+
/// Stack Behavior: `[-0, +(0|1), -]`
3082+
pub fn getUpvalue(lua: *Lua, funcindex: i32, index: i32) ?[:0]const u8 {
3083+
const string: ?[*:0]const u8 = @ptrCast(c.lua_getupvalue(asState(lua), funcindex, index));
3084+
return asSlice(string);
3085+
}
3086+
3087+
/// Sets the value of a closure's upvalue. It assigns the value at the top of the stack to the upvalue and returns
3088+
/// its name. Only pops the value from the stack when it can be assigned to the upvalue at the given index.
3089+
///
3090+
/// From: `const char *lua_setupvalue(lua_State *L, int funcindex, int n);`
3091+
/// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_setupvalue
3092+
/// Stack Behavior: `[-(0|1), +0, -]`
3093+
pub fn setUpvalue(lua: *Lua, funcindex: i32, index: i32) ?[:0]const u8 {
3094+
const string: ?[*:0]const u8 = @ptrCast(c.lua_setupvalue(asState(lua), funcindex, index));
3095+
return asSlice(string);
3096+
}
30703097
};
30713098

30723099
test "Lua can be initialized with an allocator" {
@@ -5938,3 +5965,46 @@ test "getLocal() and setLocal() should return null for invalid indices" {
59385965

59395966
try std.testing.expect(T.hook_executed);
59405967
}
5968+
5969+
test "getUpvalue() and setUpvalue() can inspect and modify closure upvalues" {
5970+
const lua = try Lua.init(std.testing.allocator);
5971+
defer lua.deinit();
5972+
5973+
const test_code =
5974+
\\local counter = 0
5975+
\\function increment(amount)
5976+
\\ counter = counter + amount
5977+
\\ return counter
5978+
\\end
5979+
;
5980+
try lua.doString(test_code);
5981+
try std.testing.expectEqual(Lua.Type.function, lua.getGlobal("increment"));
5982+
5983+
const upvalue_name = lua.getUpvalue(-1, 1);
5984+
try std.testing.expect(upvalue_name != null);
5985+
try std.testing.expectEqualStrings("counter", std.mem.sliceTo(upvalue_name.?, 0));
5986+
5987+
try std.testing.expect(lua.isNumber(-1));
5988+
try std.testing.expectEqual(0, try lua.toIntegerStrict(-1));
5989+
lua.pop(1);
5990+
5991+
lua.pushInteger(10);
5992+
const modified_name = lua.setUpvalue(-2, 1);
5993+
try std.testing.expect(modified_name != null);
5994+
try std.testing.expectEqualStrings("counter", std.mem.sliceTo(modified_name.?, 0));
5995+
5996+
lua.pushInteger(5);
5997+
try lua.callProtected(1, 1, 0);
5998+
try std.testing.expect(lua.isNumber(-1));
5999+
try std.testing.expectEqual(15, try lua.toIntegerStrict(-1)); // 10 + 5 = 15
6000+
lua.pop(1);
6001+
6002+
try std.testing.expectEqual(Lua.Type.function, lua.getGlobal("increment"));
6003+
try std.testing.expect(lua.getUpvalue(-1, 999) == null);
6004+
try std.testing.expectEqual(1, lua.getTop()); // Stack unchanged
6005+
6006+
lua.pushInteger(50);
6007+
try std.testing.expect(lua.setUpvalue(-2, 999) == null);
6008+
try std.testing.expectEqual(2, lua.getTop()); // Stack unchanged
6009+
lua.pop(2);
6010+
}

0 commit comments

Comments
 (0)