@@ -460,8 +460,11 @@ pub const Lua = struct {
460
460
461
461
/// Pushes onto the stack the `n`th user value associated with the full userdata at the given `index`
462
462
/// Returns the type of the pushed value
463
- pub fn getIUserValue (lua : * Lua , index : i32 , n : i32 ) LuaType {
464
- return @intToEnum (LuaType , c .lua_getiuservalue (lua .state , index , n ));
463
+ /// Returns an error if the userdata does not have that value
464
+ pub fn getIndexUserValue (lua : * Lua , index : i32 , n : i32 ) ! LuaType {
465
+ const val_type = @intToEnum (LuaType , c .lua_getiuservalue (lua .state , index , n ));
466
+ if (val_type == .none ) return Error .Fail ;
467
+ return val_type ;
465
468
}
466
469
467
470
/// If the value at the given `index` has a metatable, the function pushes that metatable onto the stack
@@ -608,8 +611,11 @@ pub const Lua = struct {
608
611
/// Returns the address of the block of memory
609
612
/// TODO: use comptime to return the a pointer to a block of type T for safety?
610
613
/// See how this is used if that would be useful
611
- pub fn newUserdataUV (lua : * Lua , size : usize , new_uvalue : i32 ) * anyopaque {
612
- return c .lua_newuserdatauv (lua .state , size , new_uvalue ).? ;
614
+ pub fn newUserdataUV (lua : * Lua , comptime T : type , new_uvalue : i32 ) * T {
615
+ // safe to .? because this function throws a Lua error on out of memory
616
+ // so the returned pointer should never be null
617
+ const ptr = c .lua_newuserdatauv (lua .state , @sizeOf (T ), new_uvalue ).? ;
618
+ return opaqueCast (T , ptr );
613
619
}
614
620
615
621
/// Pops a key from the stack, and pushes a key-value pair from the table at the given `index`
@@ -658,7 +664,6 @@ pub const Lua = struct {
658
664
659
665
/// Pushes a new C Closure onto the stack
660
666
/// `n` tells how many upvalues this function will have
661
- /// TODO: add a Zig interface to pass Zig functions wrapped
662
667
pub fn pushCClosure (lua : * Lua , c_fn : CFn , n : i32 ) void {
663
668
c .lua_pushcclosure (lua .state , c_fn , n );
664
669
}
@@ -756,7 +761,7 @@ pub const Lua = struct {
756
761
757
762
/// Pushes onto the stack the value t[k] where t is the table at the given `index` and
758
763
/// k is the pointer `p` represented as a light userdata
759
- pub fn rawGetP (lua : * Lua , index : i32 , p : * anyopaque ) LuaType {
764
+ pub fn rawGetPtr (lua : * Lua , index : i32 , p : * const anyopaque ) LuaType {
760
765
return @intToEnum (LuaType , c .lua_rawgetp (lua .state , index , p ));
761
766
}
762
767
@@ -783,7 +788,7 @@ pub const Lua = struct {
783
788
/// Does the equivalent of t[p] = v where t is the table at the given `index`
784
789
/// `p` is encoded as a light user data, and v is the value at the top of the stack
785
790
/// Pops the value from the stack. Does not use __newindex metavalue
786
- pub fn rawSetP (lua : * Lua , index : i32 , p : * anyopaque ) void {
791
+ pub fn rawSetPtr (lua : * Lua , index : i32 , p : * const anyopaque ) void {
787
792
c .lua_rawsetp (lua .state , index , p );
788
793
}
789
794
@@ -854,7 +859,7 @@ pub const Lua = struct {
854
859
/// Pops a value from the stack and sets it as the new `n`th user value associated to
855
860
/// the full userdata at the given index
856
861
/// Returns an error if the userdata does not have that value
857
- pub fn setIUserValue (lua : * Lua , index : i32 , n : i32 ) ! void {
862
+ pub fn setIndexUserValue (lua : * Lua , index : i32 , n : i32 ) ! void {
858
863
if (c .lua_setiuservalue (lua .state , index , n ) == 0 ) return Error .Fail ;
859
864
}
860
865
@@ -960,8 +965,9 @@ pub const Lua = struct {
960
965
}
961
966
962
967
/// Converts the value at the given `index` to an opaque pointer
963
- pub fn toPointer (lua : * Lua , index : i32 ) ? * const anyopaque {
964
- return c .lua_topointer (lua .state , index );
968
+ pub fn toPointer (lua : * Lua , index : i32 ) ! * const anyopaque {
969
+ if (c .lua_topointer (lua .state , index )) | ptr | return ptr ;
970
+ return Error .Fail ;
965
971
}
966
972
967
973
/// Converts the Lua value at the given `index` to a zero-terminated slice (string)
@@ -985,9 +991,10 @@ pub const Lua = struct {
985
991
986
992
/// If the value at the given `index` is a full userdata, returns its memory-block address
987
993
/// If the value is a light userdata, returns its value (a pointer)
988
- /// Otherwise returns null
989
- pub fn toUserdata (lua : * Lua , index : i32 ) ? * anyopaque {
990
- return c .lua_touserdata (lua .state , index );
994
+ /// Otherwise returns an error
995
+ pub fn toUserdata (lua : * Lua , index : i32 ) ! * anyopaque {
996
+ if (c .lua_touserdata (lua .state , index )) | ptr | return ptr ;
997
+ return Error .Fail ;
991
998
}
992
999
993
1000
/// Returns the `LuaType` of the value at the given index
@@ -2508,26 +2515,137 @@ test "dump and load" {
2508
2515
try expectEqual (@as (Integer , 11 ), lua .toInteger (-1 ));
2509
2516
}
2510
2517
2518
+ test "threads" {
2519
+ var lua = try Lua .init (testing .allocator );
2520
+ defer lua .deinit ();
2521
+
2522
+ var new_thread = lua .newThread ();
2523
+ try expectEqual (@as (i32 , 1 ), lua .getTop ());
2524
+ try expectEqual (@as (i32 , 0 ), new_thread .getTop ());
2525
+
2526
+ lua .pushInteger (10 );
2527
+ lua .pushNil ();
2528
+
2529
+ lua .xMove (new_thread , 2 );
2530
+ try expectEqual (@as (i32 , 2 ), new_thread .getTop ());
2531
+ }
2532
+
2533
+ test "userdata and uservalues" {
2534
+ var lua = try Lua .init (testing .allocator );
2535
+ defer lua .deinit ();
2536
+
2537
+ const Data = struct {
2538
+ val : i32 ,
2539
+ code : [4 ]u8 ,
2540
+ };
2541
+
2542
+ // create a Lua-owned pointer to a Data with 2 associated user values
2543
+ var data = lua .newUserdataUV (Data , 2 );
2544
+ data .val = 1 ;
2545
+ std .mem .copy (u8 , & data .code , "abcd" );
2546
+
2547
+ // assign the user values
2548
+ lua .pushNumber (1234.56 );
2549
+ try lua .setIndexUserValue (1 , 1 );
2550
+
2551
+ _ = lua .pushString ("test string" );
2552
+ try lua .setIndexUserValue (1 , 2 );
2553
+
2554
+ try expectEqual (LuaType .number , try lua .getIndexUserValue (1 , 1 ));
2555
+ try expectEqual (@as (Number , 1234.56 ), lua .toNumber (-1 ));
2556
+ try expectEqual (LuaType .string , try lua .getIndexUserValue (1 , 2 ));
2557
+ try expectEqualStrings ("test string" , lua .toString (-1 ).? );
2558
+
2559
+ try expectError (Error .Fail , lua .setIndexUserValue (1 , 3 ));
2560
+ try expectError (Error .Fail , lua .getIndexUserValue (1 , 3 ));
2561
+
2562
+ try expectEqual (data , opaqueCast (Data , try lua .toUserdata (1 )));
2563
+ try expectEqual (@ptrCast (* const anyopaque , data ), @alignCast (@alignOf (Data ), try lua .toPointer (1 )));
2564
+ }
2565
+
2566
+ test "upvalues" {
2567
+ var lua = try Lua .init (testing .allocator );
2568
+ defer lua .deinit ();
2569
+
2570
+ // counter from PIL
2571
+ const counter = struct {
2572
+ fn inner (l : * Lua ) i32 {
2573
+ var counter = l .toInteger (Lua .upvalueIndex (1 ));
2574
+ counter += 1 ;
2575
+ l .pushInteger (counter );
2576
+ l .copy (-1 , Lua .upvalueIndex (1 ));
2577
+ return 1 ;
2578
+ }
2579
+ }.inner ;
2580
+
2581
+ // Initialize the counter at 0
2582
+ lua .pushInteger (0 );
2583
+ lua .pushCClosure (wrap (counter ), 1 );
2584
+ lua .setGlobal ("counter" );
2585
+
2586
+ // call the function repeatedly, each time ensuring the result increases by one
2587
+ var expected : Integer = 1 ;
2588
+ while (expected <= 10 ) : (expected += 1 ) {
2589
+ _ = lua .getGlobal ("counter" );
2590
+ lua .call (0 , 1 );
2591
+ try expectEqual (expected , lua .toInteger (-1 ));
2592
+ lua .pop (1 );
2593
+ }
2594
+ }
2595
+
2596
+ test "table traversal" {
2597
+ var lua = try Lua .init (testing .allocator );
2598
+ defer lua .deinit ();
2599
+
2600
+ try lua .doString ("t = { key = 'value', second = true, third = 1 }" );
2601
+ _ = lua .getGlobal ("t" );
2602
+
2603
+ lua .pushNil ();
2604
+
2605
+ while (lua .next (1 )) {
2606
+ switch (lua .typeOf (-1 )) {
2607
+ .string = > {
2608
+ try expectEqualStrings ("key" , lua .toString (-2 ).? );
2609
+ try expectEqualStrings ("value" , lua .toString (-1 ).? );
2610
+ },
2611
+ .boolean = > {
2612
+ try expectEqualStrings ("second" , lua .toString (-2 ).? );
2613
+ try expectEqual (true , lua .toBoolean (-1 ));
2614
+ },
2615
+ .number = > {
2616
+ try expectEqualStrings ("third" , lua .toString (-2 ).? );
2617
+ try expectEqual (@as (Integer , 1 ), lua .toInteger (-1 ));
2618
+ },
2619
+ else = > unreachable ,
2620
+ }
2621
+ lua .pop (1 );
2622
+ }
2623
+ }
2624
+
2625
+ test "registry" {
2626
+ var lua = try Lua .init (testing .allocator );
2627
+ defer lua .deinit ();
2628
+
2629
+ const key = "mykey" ;
2630
+
2631
+ // store a string in the registry
2632
+ _ = lua .pushString ("hello there" );
2633
+ lua .rawSetPtr (registry_index , key );
2634
+
2635
+ // get key from the registry
2636
+ _ = lua .rawGetPtr (registry_index , key );
2637
+ try expectEqualStrings ("hello there" , lua .toString (-1 ).? );
2638
+ }
2639
+
2511
2640
test "refs" {
2512
2641
// temporary test that includes a reference to all functions so
2513
2642
// they will be type-checked
2514
2643
2515
2644
// stdlib
2516
2645
_ = Lua .closeSlot ;
2517
2646
_ = Lua .raiseError ;
2518
- _ = Lua .getIUserValue ;
2519
2647
_ = Lua .isYieldable ;
2520
- _ = Lua .newThread ;
2521
- _ = Lua .newUserdataUV ;
2522
- _ = Lua .next ;
2523
- _ = Lua .rawGetP ;
2524
- _ = Lua .rawSetP ;
2525
- _ = Lua .setIUserValue ;
2526
2648
_ = Lua .toClose ;
2527
- _ = Lua .toPointer ;
2528
- _ = Lua .toUserdata ;
2529
- _ = Lua .upvalueIndex ;
2530
- _ = Lua .xMove ;
2531
2649
_ = Lua .yield ;
2532
2650
_ = Lua .yieldCont ;
2533
2651
0 commit comments