@@ -21,7 +21,9 @@ use crate::types::{
21
21
Callback , HookCallback , Integer , LightUserData , LuaRef , MaybeSend , Number , RegistryKey ,
22
22
UserDataCell ,
23
23
} ;
24
- use crate :: userdata:: { AnyUserData , MetaMethod , UserData , UserDataMethods , UserDataWrapped } ;
24
+ use crate :: userdata:: {
25
+ AnyUserData , MetaMethod , UserData , UserDataFields , UserDataMethods , UserDataWrapped ,
26
+ } ;
25
27
use crate :: util:: {
26
28
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
27
29
get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
@@ -1523,37 +1525,86 @@ impl Lua {
1523
1525
}
1524
1526
1525
1527
let _sg = StackGuard :: new ( self . state ) ;
1526
- assert_stack ( self . state , 8 ) ;
1528
+ assert_stack ( self . state , 10 ) ;
1527
1529
1530
+ let mut fields = StaticUserDataFields :: default ( ) ;
1528
1531
let mut methods = StaticUserDataMethods :: default ( ) ;
1532
+ T :: add_fields ( & mut fields) ;
1529
1533
T :: add_methods ( & mut methods) ;
1530
1534
1535
+ // Prepare metatable, add meta methods first and then meta fields
1531
1536
protect_lua_closure ( self . state , 0 , 1 , |state| {
1532
1537
ffi:: lua_newtable ( state) ;
1533
1538
} ) ?;
1534
1539
for ( k, m) in methods. meta_methods {
1535
- push_string ( self . state , k. name ( ) ) ?;
1540
+ push_string ( self . state , k. validate ( ) ? . name ( ) ) ?;
1536
1541
self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1537
1542
1538
1543
protect_lua_closure ( self . state , 3 , 1 , |state| {
1539
1544
ffi:: lua_rawset ( state, -3 ) ;
1540
1545
} ) ?;
1541
1546
}
1547
+ for ( k, f) in fields. meta_fields {
1548
+ push_string ( self . state , k. validate ( ) ?. name ( ) ) ?;
1549
+ self . push_value ( f ( self ) ?) ?;
1550
+
1551
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1552
+ ffi:: lua_rawset ( state, -3 ) ;
1553
+ } ) ?;
1554
+ }
1555
+ let metatable_index = ffi:: lua_absindex ( self . state , -1 ) ;
1542
1556
1557
+ let mut extra_tables_count = 0 ;
1558
+
1559
+ let mut field_getters_index = None ;
1560
+ let has_field_getters = fields. field_getters . len ( ) > 0 ;
1561
+ if has_field_getters {
1562
+ protect_lua_closure ( self . state , 0 , 1 , |state| {
1563
+ ffi:: lua_newtable ( state) ;
1564
+ } ) ?;
1565
+ for ( k, m) in fields. field_getters {
1566
+ push_string ( self . state , & k) ?;
1567
+ self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1568
+
1569
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1570
+ ffi:: lua_rawset ( state, -3 ) ;
1571
+ } ) ?;
1572
+ }
1573
+ field_getters_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1574
+ extra_tables_count += 1 ;
1575
+ }
1576
+
1577
+ let mut field_setters_index = None ;
1578
+ let has_field_setters = fields. field_setters . len ( ) > 0 ;
1579
+ if has_field_setters {
1580
+ protect_lua_closure ( self . state , 0 , 1 , |state| {
1581
+ ffi:: lua_newtable ( state) ;
1582
+ } ) ?;
1583
+ for ( k, m) in fields. field_setters {
1584
+ push_string ( self . state , & k) ?;
1585
+ self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1586
+
1587
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1588
+ ffi:: lua_rawset ( state, -3 ) ;
1589
+ } ) ?;
1590
+ }
1591
+ field_setters_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1592
+ extra_tables_count += 1 ;
1593
+ }
1594
+
1595
+ let mut methods_index = None ;
1543
1596
#[ cfg( feature = "async" ) ]
1544
- let no_methods = methods. methods . is_empty ( ) && methods. async_methods . is_empty ( ) ;
1597
+ let has_methods = methods. methods . len ( ) > 0 || methods. async_methods . len ( ) > 0 ;
1545
1598
#[ cfg( not( feature = "async" ) ) ]
1546
- let no_methods = methods. methods . is_empty ( ) ;
1547
-
1548
- if no_methods {
1549
- init_userdata_metatable :: < UserDataCell < T > > ( self . state , -1 , None ) ?;
1550
- } else {
1599
+ let has_methods = methods. methods . len ( ) > 0 ;
1600
+ if has_methods {
1551
1601
protect_lua_closure ( self . state , 0 , 1 , |state| {
1552
1602
ffi:: lua_newtable ( state) ;
1553
1603
} ) ?;
1554
1604
for ( k, m) in methods. methods {
1555
1605
push_string ( self . state , & k) ?;
1556
1606
self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1607
+
1557
1608
protect_lua_closure ( self . state , 3 , 1 , |state| {
1558
1609
ffi:: lua_rawset ( state, -3 ) ;
1559
1610
} ) ?;
@@ -1562,15 +1613,26 @@ impl Lua {
1562
1613
for ( k, m) in methods. async_methods {
1563
1614
push_string ( self . state , & k) ?;
1564
1615
self . push_value ( Value :: Function ( self . create_async_callback ( m) ?) ) ?;
1616
+
1565
1617
protect_lua_closure ( self . state , 3 , 1 , |state| {
1566
1618
ffi:: lua_rawset ( state, -3 ) ;
1567
1619
} ) ?;
1568
1620
}
1569
-
1570
- init_userdata_metatable :: < UserDataCell < T > > ( self . state , -2 , Some ( -1 ) ) ?;
1571
- ffi:: lua_pop ( self . state , 1 ) ;
1621
+ methods_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1622
+ extra_tables_count += 1 ;
1572
1623
}
1573
1624
1625
+ init_userdata_metatable :: < UserDataCell < T > > (
1626
+ self . state ,
1627
+ metatable_index,
1628
+ field_getters_index,
1629
+ field_setters_index,
1630
+ methods_index,
1631
+ ) ?;
1632
+
1633
+ // Pop extra tables to get metatable on top of the stack
1634
+ ffi:: lua_pop ( self . state , extra_tables_count) ;
1635
+
1574
1636
let ptr = ffi:: lua_topointer ( self . state , -1 ) ;
1575
1637
let id = protect_lua_closure ( self . state , 1 , 0 , |state| {
1576
1638
ffi:: luaL_ref ( state, ffi:: LUA_REGISTRYINDEX )
@@ -2317,41 +2379,48 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
2317
2379
. push ( ( name. as_ref ( ) . to_vec ( ) , Self :: box_async_function ( function) ) ) ;
2318
2380
}
2319
2381
2320
- fn add_meta_method < A , R , M > ( & mut self , meta : MetaMethod , method : M )
2382
+ fn add_meta_method < S , A , R , M > ( & mut self , meta : S , method : M )
2321
2383
where
2384
+ S : Into < MetaMethod > ,
2322
2385
A : FromLuaMulti < ' lua > ,
2323
2386
R : ToLuaMulti < ' lua > ,
2324
2387
M : ' static + MaybeSend + Fn ( & ' lua Lua , & T , A ) -> Result < R > ,
2325
2388
{
2326
- self . meta_methods . push ( ( meta, Self :: box_method ( method) ) ) ;
2389
+ self . meta_methods
2390
+ . push ( ( meta. into ( ) , Self :: box_method ( method) ) ) ;
2327
2391
}
2328
2392
2329
- fn add_meta_method_mut < A , R , M > ( & mut self , meta : MetaMethod , method : M )
2393
+ fn add_meta_method_mut < S , A , R , M > ( & mut self , meta : S , method : M )
2330
2394
where
2395
+ S : Into < MetaMethod > ,
2331
2396
A : FromLuaMulti < ' lua > ,
2332
2397
R : ToLuaMulti < ' lua > ,
2333
2398
M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < R > ,
2334
2399
{
2335
- self . meta_methods . push ( ( meta, Self :: box_method_mut ( method) ) ) ;
2400
+ self . meta_methods
2401
+ . push ( ( meta. into ( ) , Self :: box_method_mut ( method) ) ) ;
2336
2402
}
2337
2403
2338
- fn add_meta_function < A , R , F > ( & mut self , meta : MetaMethod , function : F )
2404
+ fn add_meta_function < S , A , R , F > ( & mut self , meta : S , function : F )
2339
2405
where
2406
+ S : Into < MetaMethod > ,
2340
2407
A : FromLuaMulti < ' lua > ,
2341
2408
R : ToLuaMulti < ' lua > ,
2342
2409
F : ' static + MaybeSend + Fn ( & ' lua Lua , A ) -> Result < R > ,
2343
2410
{
2344
- self . meta_methods . push ( ( meta, Self :: box_function ( function) ) ) ;
2411
+ self . meta_methods
2412
+ . push ( ( meta. into ( ) , Self :: box_function ( function) ) ) ;
2345
2413
}
2346
2414
2347
- fn add_meta_function_mut < A , R , F > ( & mut self , meta : MetaMethod , function : F )
2415
+ fn add_meta_function_mut < S , A , R , F > ( & mut self , meta : S , function : F )
2348
2416
where
2417
+ S : Into < MetaMethod > ,
2349
2418
A : FromLuaMulti < ' lua > ,
2350
2419
R : ToLuaMulti < ' lua > ,
2351
2420
F : ' static + MaybeSend + FnMut ( & ' lua Lua , A ) -> Result < R > ,
2352
2421
{
2353
2422
self . meta_methods
2354
- . push ( ( meta, Self :: box_function_mut ( function) ) ) ;
2423
+ . push ( ( meta. into ( ) , Self :: box_function_mut ( function) ) ) ;
2355
2424
}
2356
2425
}
2357
2426
@@ -2473,3 +2542,104 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
2473
2542
} )
2474
2543
}
2475
2544
}
2545
+
2546
+ struct StaticUserDataFields < ' lua , T : ' static + UserData > {
2547
+ field_getters : Vec < ( Vec < u8 > , Callback < ' lua , ' static > ) > ,
2548
+ field_setters : Vec < ( Vec < u8 > , Callback < ' lua , ' static > ) > ,
2549
+ meta_fields : Vec < (
2550
+ MetaMethod ,
2551
+ Box < dyn Fn ( & ' lua Lua ) -> Result < Value < ' lua > > + ' static > ,
2552
+ ) > ,
2553
+ _type : PhantomData < T > ,
2554
+ }
2555
+
2556
+ impl < ' lua , T : ' static + UserData > Default for StaticUserDataFields < ' lua , T > {
2557
+ fn default ( ) -> StaticUserDataFields < ' lua , T > {
2558
+ StaticUserDataFields {
2559
+ field_getters : Vec :: new ( ) ,
2560
+ field_setters : Vec :: new ( ) ,
2561
+ meta_fields : Vec :: new ( ) ,
2562
+ _type : PhantomData ,
2563
+ }
2564
+ }
2565
+ }
2566
+
2567
+ impl < ' lua , T : ' static + UserData > UserDataFields < ' lua , T > for StaticUserDataFields < ' lua , T > {
2568
+ fn add_field_method_get < S , R , M > ( & mut self , name : & S , method : M )
2569
+ where
2570
+ S : AsRef < [ u8 ] > + ?Sized ,
2571
+ R : ToLua < ' lua > ,
2572
+ M : ' static + MaybeSend + Fn ( & ' lua Lua , & T ) -> Result < R > ,
2573
+ {
2574
+ self . field_getters . push ( (
2575
+ name. as_ref ( ) . to_vec ( ) ,
2576
+ StaticUserDataMethods :: box_method ( move |lua, data, ( ) | method ( lua, data) ) ,
2577
+ ) ) ;
2578
+ }
2579
+
2580
+ fn add_field_method_set < S , A , M > ( & mut self , name : & S , method : M )
2581
+ where
2582
+ S : AsRef < [ u8 ] > + ?Sized ,
2583
+ A : FromLua < ' lua > ,
2584
+ M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < ( ) > ,
2585
+ {
2586
+ self . field_setters . push ( (
2587
+ name. as_ref ( ) . to_vec ( ) ,
2588
+ StaticUserDataMethods :: box_method_mut ( method) ,
2589
+ ) ) ;
2590
+ }
2591
+
2592
+ fn add_field_function_get < S , R , F > ( & mut self , name : & S , function : F )
2593
+ where
2594
+ S : AsRef < [ u8 ] > + ?Sized ,
2595
+ R : ToLua < ' lua > ,
2596
+ F : ' static + MaybeSend + Fn ( & ' lua Lua , AnyUserData < ' lua > ) -> Result < R > ,
2597
+ {
2598
+ self . field_getters . push ( (
2599
+ name. as_ref ( ) . to_vec ( ) ,
2600
+ StaticUserDataMethods :: < T > :: box_function ( move |lua, data| function ( lua, data) ) ,
2601
+ ) ) ;
2602
+ }
2603
+
2604
+ fn add_field_function_set < S , A , F > ( & mut self , name : & S , mut function : F )
2605
+ where
2606
+ S : AsRef < [ u8 ] > + ?Sized ,
2607
+ A : FromLua < ' lua > ,
2608
+ F : ' static + MaybeSend + FnMut ( & ' lua Lua , AnyUserData < ' lua > , A ) -> Result < ( ) > ,
2609
+ {
2610
+ self . field_setters . push ( (
2611
+ name. as_ref ( ) . to_vec ( ) ,
2612
+ StaticUserDataMethods :: < T > :: box_function_mut ( move |lua, ( data, val) | {
2613
+ function ( lua, data, val)
2614
+ } ) ,
2615
+ ) ) ;
2616
+ }
2617
+
2618
+ fn add_meta_field_with < S , R , F > ( & mut self , meta : S , f : F )
2619
+ where
2620
+ S : Into < MetaMethod > ,
2621
+ R : ToLua < ' lua > ,
2622
+ F : ' static + MaybeSend + Fn ( & ' lua Lua ) -> Result < R > ,
2623
+ {
2624
+ let meta = meta. into ( ) ;
2625
+ self . meta_fields . push ( (
2626
+ meta. clone ( ) ,
2627
+ Box :: new ( move |lua| {
2628
+ let value = f ( lua) ?. to_lua ( lua) ?;
2629
+ if meta == MetaMethod :: Index || meta == MetaMethod :: NewIndex {
2630
+ match value {
2631
+ Value :: Nil | Value :: Table ( _) | Value :: Function ( _) => { }
2632
+ _ => {
2633
+ return Err ( Error :: MetaMethodTypeError {
2634
+ method : meta. to_string ( ) ,
2635
+ type_name : value. type_name ( ) ,
2636
+ message : Some ( "expected nil, table or function" . to_string ( ) ) ,
2637
+ } )
2638
+ }
2639
+ }
2640
+ }
2641
+ Ok ( value)
2642
+ } ) ,
2643
+ ) ) ;
2644
+ }
2645
+ }
0 commit comments