@@ -21,7 +21,9 @@ use crate::types::{
2121 Callback , HookCallback , Integer , LightUserData , LuaRef , MaybeSend , Number , RegistryKey ,
2222 UserDataCell ,
2323} ;
24- use crate :: userdata:: { AnyUserData , MetaMethod , UserData , UserDataMethods , UserDataWrapped } ;
24+ use crate :: userdata:: {
25+ AnyUserData , MetaMethod , UserData , UserDataFields , UserDataMethods , UserDataWrapped ,
26+ } ;
2527use crate :: util:: {
2628 assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
2729 get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
@@ -1523,37 +1525,86 @@ impl Lua {
15231525 }
15241526
15251527 let _sg = StackGuard :: new ( self . state ) ;
1526- assert_stack ( self . state , 8 ) ;
1528+ assert_stack ( self . state , 10 ) ;
15271529
1530+ let mut fields = StaticUserDataFields :: default ( ) ;
15281531 let mut methods = StaticUserDataMethods :: default ( ) ;
1532+ T :: add_fields ( & mut fields) ;
15291533 T :: add_methods ( & mut methods) ;
15301534
1535+ // Prepare metatable, add meta methods first and then meta fields
15311536 protect_lua_closure ( self . state , 0 , 1 , |state| {
15321537 ffi:: lua_newtable ( state) ;
15331538 } ) ?;
15341539 for ( k, m) in methods. meta_methods {
1535- push_string ( self . state , k. name ( ) ) ?;
1540+ push_string ( self . state , k. validate ( ) ? . name ( ) ) ?;
15361541 self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
15371542
15381543 protect_lua_closure ( self . state , 3 , 1 , |state| {
15391544 ffi:: lua_rawset ( state, -3 ) ;
15401545 } ) ?;
15411546 }
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 ) ;
15421556
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 ;
15431596 #[ 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 ;
15451598 #[ 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 {
15511601 protect_lua_closure ( self . state , 0 , 1 , |state| {
15521602 ffi:: lua_newtable ( state) ;
15531603 } ) ?;
15541604 for ( k, m) in methods. methods {
15551605 push_string ( self . state , & k) ?;
15561606 self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1607+
15571608 protect_lua_closure ( self . state , 3 , 1 , |state| {
15581609 ffi:: lua_rawset ( state, -3 ) ;
15591610 } ) ?;
@@ -1562,15 +1613,26 @@ impl Lua {
15621613 for ( k, m) in methods. async_methods {
15631614 push_string ( self . state , & k) ?;
15641615 self . push_value ( Value :: Function ( self . create_async_callback ( m) ?) ) ?;
1616+
15651617 protect_lua_closure ( self . state , 3 , 1 , |state| {
15661618 ffi:: lua_rawset ( state, -3 ) ;
15671619 } ) ?;
15681620 }
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 ;
15721623 }
15731624
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+
15741636 let ptr = ffi:: lua_topointer ( self . state , -1 ) ;
15751637 let id = protect_lua_closure ( self . state , 1 , 0 , |state| {
15761638 ffi:: luaL_ref ( state, ffi:: LUA_REGISTRYINDEX )
@@ -2317,41 +2379,48 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
23172379 . push ( ( name. as_ref ( ) . to_vec ( ) , Self :: box_async_function ( function) ) ) ;
23182380 }
23192381
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 )
23212383 where
2384+ S : Into < MetaMethod > ,
23222385 A : FromLuaMulti < ' lua > ,
23232386 R : ToLuaMulti < ' lua > ,
23242387 M : ' static + MaybeSend + Fn ( & ' lua Lua , & T , A ) -> Result < R > ,
23252388 {
2326- self . meta_methods . push ( ( meta, Self :: box_method ( method) ) ) ;
2389+ self . meta_methods
2390+ . push ( ( meta. into ( ) , Self :: box_method ( method) ) ) ;
23272391 }
23282392
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 )
23302394 where
2395+ S : Into < MetaMethod > ,
23312396 A : FromLuaMulti < ' lua > ,
23322397 R : ToLuaMulti < ' lua > ,
23332398 M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < R > ,
23342399 {
2335- self . meta_methods . push ( ( meta, Self :: box_method_mut ( method) ) ) ;
2400+ self . meta_methods
2401+ . push ( ( meta. into ( ) , Self :: box_method_mut ( method) ) ) ;
23362402 }
23372403
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 )
23392405 where
2406+ S : Into < MetaMethod > ,
23402407 A : FromLuaMulti < ' lua > ,
23412408 R : ToLuaMulti < ' lua > ,
23422409 F : ' static + MaybeSend + Fn ( & ' lua Lua , A ) -> Result < R > ,
23432410 {
2344- self . meta_methods . push ( ( meta, Self :: box_function ( function) ) ) ;
2411+ self . meta_methods
2412+ . push ( ( meta. into ( ) , Self :: box_function ( function) ) ) ;
23452413 }
23462414
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 )
23482416 where
2417+ S : Into < MetaMethod > ,
23492418 A : FromLuaMulti < ' lua > ,
23502419 R : ToLuaMulti < ' lua > ,
23512420 F : ' static + MaybeSend + FnMut ( & ' lua Lua , A ) -> Result < R > ,
23522421 {
23532422 self . meta_methods
2354- . push ( ( meta, Self :: box_function_mut ( function) ) ) ;
2423+ . push ( ( meta. into ( ) , Self :: box_function_mut ( function) ) ) ;
23552424 }
23562425}
23572426
@@ -2473,3 +2542,104 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
24732542 } )
24742543 }
24752544}
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