@@ -20,7 +20,9 @@ use crate::types::{
20
20
Callback , HookCallback , Integer , LightUserData , LuaRef , MaybeSend , Number , RegistryKey ,
21
21
UserDataCell ,
22
22
} ;
23
- use crate :: userdata:: { AnyUserData , MetaMethod , UserData , UserDataMethods , UserDataWrapped } ;
23
+ use crate :: userdata:: {
24
+ AnyUserData , MetaMethod , UserData , UserDataFields , UserDataMethods , UserDataWrapped ,
25
+ } ;
24
26
use crate :: util:: {
25
27
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
26
28
get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
@@ -1511,37 +1513,86 @@ impl Lua {
1511
1513
}
1512
1514
1513
1515
let _sg = StackGuard :: new ( self . state ) ;
1514
- assert_stack ( self . state , 8 ) ;
1516
+ assert_stack ( self . state , 10 ) ;
1515
1517
1518
+ let mut fields = StaticUserDataFields :: default ( ) ;
1516
1519
let mut methods = StaticUserDataMethods :: default ( ) ;
1520
+ T :: add_fields ( & mut fields) ;
1517
1521
T :: add_methods ( & mut methods) ;
1518
1522
1523
+ // Prepare metatable, add meta methods first and then meta fields
1519
1524
protect_lua_closure ( self . state , 0 , 1 , |state| {
1520
1525
ffi:: lua_newtable ( state) ;
1521
1526
} ) ?;
1522
1527
for ( k, m) in methods. meta_methods {
1523
- push_string ( self . state , k. name ( ) ) ?;
1528
+ push_string ( self . state , k. validate ( ) ? . name ( ) ) ?;
1524
1529
self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1525
1530
1526
1531
protect_lua_closure ( self . state , 3 , 1 , |state| {
1527
1532
ffi:: lua_rawset ( state, -3 ) ;
1528
1533
} ) ?;
1529
1534
}
1535
+ for ( k, f) in fields. meta_fields {
1536
+ push_string ( self . state , k. validate ( ) ?. name ( ) ) ?;
1537
+ self . push_value ( f ( self ) ?) ?;
1538
+
1539
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1540
+ ffi:: lua_rawset ( state, -3 ) ;
1541
+ } ) ?;
1542
+ }
1543
+ let metatable_index = ffi:: lua_absindex ( self . state , -1 ) ;
1544
+
1545
+ let mut extra_tables_count = 0 ;
1546
+
1547
+ let mut field_getters_index = None ;
1548
+ let has_field_getters = fields. field_getters . len ( ) > 0 ;
1549
+ if has_field_getters {
1550
+ protect_lua_closure ( self . state , 0 , 1 , |state| {
1551
+ ffi:: lua_newtable ( state) ;
1552
+ } ) ?;
1553
+ for ( k, m) in fields. field_getters {
1554
+ push_string ( self . state , & k) ?;
1555
+ self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1556
+
1557
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1558
+ ffi:: lua_rawset ( state, -3 ) ;
1559
+ } ) ?;
1560
+ }
1561
+ field_getters_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1562
+ extra_tables_count += 1 ;
1563
+ }
1564
+
1565
+ let mut field_setters_index = None ;
1566
+ let has_field_setters = fields. field_setters . len ( ) > 0 ;
1567
+ if has_field_setters {
1568
+ protect_lua_closure ( self . state , 0 , 1 , |state| {
1569
+ ffi:: lua_newtable ( state) ;
1570
+ } ) ?;
1571
+ for ( k, m) in fields. field_setters {
1572
+ push_string ( self . state , & k) ?;
1573
+ self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1574
+
1575
+ protect_lua_closure ( self . state , 3 , 1 , |state| {
1576
+ ffi:: lua_rawset ( state, -3 ) ;
1577
+ } ) ?;
1578
+ }
1579
+ field_setters_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1580
+ extra_tables_count += 1 ;
1581
+ }
1530
1582
1583
+ let mut methods_index = None ;
1531
1584
#[ cfg( feature = "async" ) ]
1532
- let no_methods = methods. methods . is_empty ( ) && methods. async_methods . is_empty ( ) ;
1585
+ let has_methods = methods. methods . len ( ) > 0 || methods. async_methods . len ( ) > 0 ;
1533
1586
#[ cfg( not( feature = "async" ) ) ]
1534
- let no_methods = methods. methods . is_empty ( ) ;
1535
-
1536
- if no_methods {
1537
- init_userdata_metatable :: < UserDataCell < T > > ( self . state , -1 , None ) ?;
1538
- } else {
1587
+ let has_methods = methods. methods . len ( ) > 0 ;
1588
+ if has_methods {
1539
1589
protect_lua_closure ( self . state , 0 , 1 , |state| {
1540
1590
ffi:: lua_newtable ( state) ;
1541
1591
} ) ?;
1542
1592
for ( k, m) in methods. methods {
1543
1593
push_string ( self . state , & k) ?;
1544
1594
self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1595
+
1545
1596
protect_lua_closure ( self . state , 3 , 1 , |state| {
1546
1597
ffi:: lua_rawset ( state, -3 ) ;
1547
1598
} ) ?;
@@ -1550,15 +1601,26 @@ impl Lua {
1550
1601
for ( k, m) in methods. async_methods {
1551
1602
push_string ( self . state , & k) ?;
1552
1603
self . push_value ( Value :: Function ( self . create_async_callback ( m) ?) ) ?;
1604
+
1553
1605
protect_lua_closure ( self . state , 3 , 1 , |state| {
1554
1606
ffi:: lua_rawset ( state, -3 ) ;
1555
1607
} ) ?;
1556
1608
}
1557
-
1558
- init_userdata_metatable :: < UserDataCell < T > > ( self . state , -2 , Some ( -1 ) ) ?;
1559
- ffi:: lua_pop ( self . state , 1 ) ;
1609
+ methods_index = Some ( ffi:: lua_absindex ( self . state , -1 ) ) ;
1610
+ extra_tables_count += 1 ;
1560
1611
}
1561
1612
1613
+ init_userdata_metatable :: < UserDataCell < T > > (
1614
+ self . state ,
1615
+ metatable_index,
1616
+ field_getters_index,
1617
+ field_setters_index,
1618
+ methods_index,
1619
+ ) ?;
1620
+
1621
+ // Pop extra tables to get metatable on top of the stack
1622
+ ffi:: lua_pop ( self . state , extra_tables_count) ;
1623
+
1562
1624
let id = protect_lua_closure ( self . state , 1 , 0 , |state| {
1563
1625
ffi:: luaL_ref ( state, ffi:: LUA_REGISTRYINDEX )
1564
1626
} ) ?;
@@ -2287,41 +2349,48 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
2287
2349
. push ( ( name. as_ref ( ) . to_vec ( ) , Self :: box_async_function ( function) ) ) ;
2288
2350
}
2289
2351
2290
- fn add_meta_method < A , R , M > ( & mut self , meta : MetaMethod , method : M )
2352
+ fn add_meta_method < S , A , R , M > ( & mut self , meta : S , method : M )
2291
2353
where
2354
+ S : Into < MetaMethod > ,
2292
2355
A : FromLuaMulti < ' lua > ,
2293
2356
R : ToLuaMulti < ' lua > ,
2294
2357
M : ' static + MaybeSend + Fn ( & ' lua Lua , & T , A ) -> Result < R > ,
2295
2358
{
2296
- self . meta_methods . push ( ( meta, Self :: box_method ( method) ) ) ;
2359
+ self . meta_methods
2360
+ . push ( ( meta. into ( ) , Self :: box_method ( method) ) ) ;
2297
2361
}
2298
2362
2299
- fn add_meta_method_mut < A , R , M > ( & mut self , meta : MetaMethod , method : M )
2363
+ fn add_meta_method_mut < S , A , R , M > ( & mut self , meta : S , method : M )
2300
2364
where
2365
+ S : Into < MetaMethod > ,
2301
2366
A : FromLuaMulti < ' lua > ,
2302
2367
R : ToLuaMulti < ' lua > ,
2303
2368
M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < R > ,
2304
2369
{
2305
- self . meta_methods . push ( ( meta, Self :: box_method_mut ( method) ) ) ;
2370
+ self . meta_methods
2371
+ . push ( ( meta. into ( ) , Self :: box_method_mut ( method) ) ) ;
2306
2372
}
2307
2373
2308
- fn add_meta_function < A , R , F > ( & mut self , meta : MetaMethod , function : F )
2374
+ fn add_meta_function < S , A , R , F > ( & mut self , meta : S , function : F )
2309
2375
where
2376
+ S : Into < MetaMethod > ,
2310
2377
A : FromLuaMulti < ' lua > ,
2311
2378
R : ToLuaMulti < ' lua > ,
2312
2379
F : ' static + MaybeSend + Fn ( & ' lua Lua , A ) -> Result < R > ,
2313
2380
{
2314
- self . meta_methods . push ( ( meta, Self :: box_function ( function) ) ) ;
2381
+ self . meta_methods
2382
+ . push ( ( meta. into ( ) , Self :: box_function ( function) ) ) ;
2315
2383
}
2316
2384
2317
- fn add_meta_function_mut < A , R , F > ( & mut self , meta : MetaMethod , function : F )
2385
+ fn add_meta_function_mut < S , A , R , F > ( & mut self , meta : S , function : F )
2318
2386
where
2387
+ S : Into < MetaMethod > ,
2319
2388
A : FromLuaMulti < ' lua > ,
2320
2389
R : ToLuaMulti < ' lua > ,
2321
2390
F : ' static + MaybeSend + FnMut ( & ' lua Lua , A ) -> Result < R > ,
2322
2391
{
2323
2392
self . meta_methods
2324
- . push ( ( meta, Self :: box_function_mut ( function) ) ) ;
2393
+ . push ( ( meta. into ( ) , Self :: box_function_mut ( function) ) ) ;
2325
2394
}
2326
2395
}
2327
2396
@@ -2443,3 +2512,104 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
2443
2512
} )
2444
2513
}
2445
2514
}
2515
+
2516
+ struct StaticUserDataFields < ' lua , T : ' static + UserData > {
2517
+ field_getters : Vec < ( Vec < u8 > , Callback < ' lua , ' static > ) > ,
2518
+ field_setters : Vec < ( Vec < u8 > , Callback < ' lua , ' static > ) > ,
2519
+ meta_fields : Vec < (
2520
+ MetaMethod ,
2521
+ Box < dyn Fn ( & ' lua Lua ) -> Result < Value < ' lua > > + ' static > ,
2522
+ ) > ,
2523
+ _type : PhantomData < T > ,
2524
+ }
2525
+
2526
+ impl < ' lua , T : ' static + UserData > Default for StaticUserDataFields < ' lua , T > {
2527
+ fn default ( ) -> StaticUserDataFields < ' lua , T > {
2528
+ StaticUserDataFields {
2529
+ field_getters : Vec :: new ( ) ,
2530
+ field_setters : Vec :: new ( ) ,
2531
+ meta_fields : Vec :: new ( ) ,
2532
+ _type : PhantomData ,
2533
+ }
2534
+ }
2535
+ }
2536
+
2537
+ impl < ' lua , T : ' static + UserData > UserDataFields < ' lua , T > for StaticUserDataFields < ' lua , T > {
2538
+ fn add_field_method_get < S , R , M > ( & mut self , name : & S , method : M )
2539
+ where
2540
+ S : AsRef < [ u8 ] > + ?Sized ,
2541
+ R : ToLua < ' lua > ,
2542
+ M : ' static + MaybeSend + Fn ( & ' lua Lua , & T ) -> Result < R > ,
2543
+ {
2544
+ self . field_getters . push ( (
2545
+ name. as_ref ( ) . to_vec ( ) ,
2546
+ StaticUserDataMethods :: box_method ( move |lua, data, ( ) | method ( lua, data) ) ,
2547
+ ) ) ;
2548
+ }
2549
+
2550
+ fn add_field_method_set < S , A , M > ( & mut self , name : & S , method : M )
2551
+ where
2552
+ S : AsRef < [ u8 ] > + ?Sized ,
2553
+ A : FromLua < ' lua > ,
2554
+ M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < ( ) > ,
2555
+ {
2556
+ self . field_setters . push ( (
2557
+ name. as_ref ( ) . to_vec ( ) ,
2558
+ StaticUserDataMethods :: box_method_mut ( method) ,
2559
+ ) ) ;
2560
+ }
2561
+
2562
+ fn add_field_function_get < S , R , F > ( & mut self , name : & S , function : F )
2563
+ where
2564
+ S : AsRef < [ u8 ] > + ?Sized ,
2565
+ R : ToLua < ' lua > ,
2566
+ F : ' static + MaybeSend + Fn ( & ' lua Lua , AnyUserData < ' lua > ) -> Result < R > ,
2567
+ {
2568
+ self . field_getters . push ( (
2569
+ name. as_ref ( ) . to_vec ( ) ,
2570
+ StaticUserDataMethods :: < T > :: box_function ( move |lua, data| function ( lua, data) ) ,
2571
+ ) ) ;
2572
+ }
2573
+
2574
+ fn add_field_function_set < S , A , F > ( & mut self , name : & S , mut function : F )
2575
+ where
2576
+ S : AsRef < [ u8 ] > + ?Sized ,
2577
+ A : FromLua < ' lua > ,
2578
+ F : ' static + MaybeSend + FnMut ( & ' lua Lua , AnyUserData < ' lua > , A ) -> Result < ( ) > ,
2579
+ {
2580
+ self . field_setters . push ( (
2581
+ name. as_ref ( ) . to_vec ( ) ,
2582
+ StaticUserDataMethods :: < T > :: box_function_mut ( move |lua, ( data, val) | {
2583
+ function ( lua, data, val)
2584
+ } ) ,
2585
+ ) ) ;
2586
+ }
2587
+
2588
+ fn add_meta_field_with < S , R , F > ( & mut self , meta : S , f : F )
2589
+ where
2590
+ S : Into < MetaMethod > ,
2591
+ R : ToLua < ' lua > ,
2592
+ F : ' static + MaybeSend + Fn ( & ' lua Lua ) -> Result < R > ,
2593
+ {
2594
+ let meta = meta. into ( ) ;
2595
+ self . meta_fields . push ( (
2596
+ meta. clone ( ) ,
2597
+ Box :: new ( move |lua| {
2598
+ let value = f ( lua) ?. to_lua ( lua) ?;
2599
+ if meta == MetaMethod :: Index || meta == MetaMethod :: NewIndex {
2600
+ match value {
2601
+ Value :: Nil | Value :: Table ( _) | Value :: Function ( _) => { }
2602
+ _ => {
2603
+ return Err ( Error :: MetaMethodTypeError {
2604
+ method : meta. to_string ( ) ,
2605
+ type_name : value. type_name ( ) ,
2606
+ message : Some ( "expected nil, table or function" . to_string ( ) ) ,
2607
+ } )
2608
+ }
2609
+ }
2610
+ }
2611
+ Ok ( value)
2612
+ } ) ,
2613
+ ) ) ;
2614
+ }
2615
+ }
0 commit comments