@@ -20,7 +20,9 @@ use crate::types::{
2020 Callback , HookCallback , Integer , LightUserData , LuaRef , MaybeSend , Number , RegistryKey ,
2121 UserDataCell ,
2222} ;
23- use crate :: userdata:: { AnyUserData , MetaMethod , UserData , UserDataMethods , UserDataWrapped } ;
23+ use crate :: userdata:: {
24+ AnyUserData , MetaMethod , UserData , UserDataFields , UserDataMethods , UserDataWrapped ,
25+ } ;
2426use crate :: util:: {
2527 assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
2628 get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
@@ -1511,37 +1513,86 @@ impl Lua {
15111513 }
15121514
15131515 let _sg = StackGuard :: new ( self . state ) ;
1514- assert_stack ( self . state , 8 ) ;
1516+ assert_stack ( self . state , 10 ) ;
15151517
1518+ let mut fields = StaticUserDataFields :: default ( ) ;
15161519 let mut methods = StaticUserDataMethods :: default ( ) ;
1520+ T :: add_fields ( & mut fields) ;
15171521 T :: add_methods ( & mut methods) ;
15181522
1523+ // Prepare metatable, add meta methods first and then meta fields
15191524 protect_lua_closure ( self . state , 0 , 1 , |state| {
15201525 ffi:: lua_newtable ( state) ;
15211526 } ) ?;
15221527 for ( k, m) in methods. meta_methods {
1523- push_string ( self . state , k. name ( ) ) ?;
1528+ push_string ( self . state , k. validate ( ) ? . name ( ) ) ?;
15241529 self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
15251530
15261531 protect_lua_closure ( self . state , 3 , 1 , |state| {
15271532 ffi:: lua_rawset ( state, -3 ) ;
15281533 } ) ?;
15291534 }
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+ }
15301582
1583+ let mut methods_index = None ;
15311584 #[ 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 ;
15331586 #[ 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 {
15391589 protect_lua_closure ( self . state , 0 , 1 , |state| {
15401590 ffi:: lua_newtable ( state) ;
15411591 } ) ?;
15421592 for ( k, m) in methods. methods {
15431593 push_string ( self . state , & k) ?;
15441594 self . push_value ( Value :: Function ( self . create_callback ( m) ?) ) ?;
1595+
15451596 protect_lua_closure ( self . state , 3 , 1 , |state| {
15461597 ffi:: lua_rawset ( state, -3 ) ;
15471598 } ) ?;
@@ -1550,15 +1601,26 @@ impl Lua {
15501601 for ( k, m) in methods. async_methods {
15511602 push_string ( self . state , & k) ?;
15521603 self . push_value ( Value :: Function ( self . create_async_callback ( m) ?) ) ?;
1604+
15531605 protect_lua_closure ( self . state , 3 , 1 , |state| {
15541606 ffi:: lua_rawset ( state, -3 ) ;
15551607 } ) ?;
15561608 }
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 ;
15601611 }
15611612
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+
15621624 let id = protect_lua_closure ( self . state , 1 , 0 , |state| {
15631625 ffi:: luaL_ref ( state, ffi:: LUA_REGISTRYINDEX )
15641626 } ) ?;
@@ -2287,41 +2349,48 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
22872349 . push ( ( name. as_ref ( ) . to_vec ( ) , Self :: box_async_function ( function) ) ) ;
22882350 }
22892351
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 )
22912353 where
2354+ S : Into < MetaMethod > ,
22922355 A : FromLuaMulti < ' lua > ,
22932356 R : ToLuaMulti < ' lua > ,
22942357 M : ' static + MaybeSend + Fn ( & ' lua Lua , & T , A ) -> Result < R > ,
22952358 {
2296- self . meta_methods . push ( ( meta, Self :: box_method ( method) ) ) ;
2359+ self . meta_methods
2360+ . push ( ( meta. into ( ) , Self :: box_method ( method) ) ) ;
22972361 }
22982362
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 )
23002364 where
2365+ S : Into < MetaMethod > ,
23012366 A : FromLuaMulti < ' lua > ,
23022367 R : ToLuaMulti < ' lua > ,
23032368 M : ' static + MaybeSend + FnMut ( & ' lua Lua , & mut T , A ) -> Result < R > ,
23042369 {
2305- self . meta_methods . push ( ( meta, Self :: box_method_mut ( method) ) ) ;
2370+ self . meta_methods
2371+ . push ( ( meta. into ( ) , Self :: box_method_mut ( method) ) ) ;
23062372 }
23072373
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 )
23092375 where
2376+ S : Into < MetaMethod > ,
23102377 A : FromLuaMulti < ' lua > ,
23112378 R : ToLuaMulti < ' lua > ,
23122379 F : ' static + MaybeSend + Fn ( & ' lua Lua , A ) -> Result < R > ,
23132380 {
2314- self . meta_methods . push ( ( meta, Self :: box_function ( function) ) ) ;
2381+ self . meta_methods
2382+ . push ( ( meta. into ( ) , Self :: box_function ( function) ) ) ;
23152383 }
23162384
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 )
23182386 where
2387+ S : Into < MetaMethod > ,
23192388 A : FromLuaMulti < ' lua > ,
23202389 R : ToLuaMulti < ' lua > ,
23212390 F : ' static + MaybeSend + FnMut ( & ' lua Lua , A ) -> Result < R > ,
23222391 {
23232392 self . meta_methods
2324- . push ( ( meta, Self :: box_function_mut ( function) ) ) ;
2393+ . push ( ( meta. into ( ) , Self :: box_function_mut ( function) ) ) ;
23252394 }
23262395}
23272396
@@ -2443,3 +2512,104 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
24432512 } )
24442513 }
24452514}
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