@@ -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
30723099test "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