@@ -171,7 +171,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
171171 let _sg = StackGuard :: new ( state) ;
172172 check_stack ( state, 3 ) ?;
173173
174- // // We don't write the data to the userdata until pushing the metatable
174+ // We don't write the data to the userdata until pushing the metatable
175175 let protect = !self . lua . unlikely_memory_error ( ) ;
176176 #[ cfg( feature = "luau" ) ]
177177 let ud_ptr = {
@@ -194,29 +194,79 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
194194 ffi:: lua_setmetatable ( state, -2 ) ;
195195
196196 let ud = AnyUserData ( self . lua . pop_ref ( ) ) ;
197+ self . attach_destructor :: < T > ( & ud) ;
197198
198- let destructor: DestructorCallback = Box :: new ( |rawlua, vref| {
199- let state = rawlua. state ( ) ;
200- let _sg = StackGuard :: new ( state) ;
201- assert_stack ( state, 2 ) ;
199+ Ok ( ud)
200+ }
201+ }
202202
203- // Check that userdata is valid (very likely)
204- if rawlua. push_userdata_ref ( & vref) . is_err ( ) {
205- return vec ! [ ] ;
206- }
203+ /// Creates a Lua userdata object from a custom Rust type.
204+ ///
205+ /// Since the Rust type is not required to be static and implement [`UserData`] trait,
206+ /// you need to provide a function to register fields or methods for the object.
207+ ///
208+ /// See also [`Scope::create_userdata`] for more details about non-static limitations.
209+ pub fn create_any_userdata < T > (
210+ & ' scope self ,
211+ data : T ,
212+ register : impl FnOnce ( & mut UserDataRegistry < T > ) ,
213+ ) -> Result < AnyUserData >
214+ where
215+ T : ' env ,
216+ {
217+ let state = self . lua . state ( ) ;
218+ let ud = unsafe {
219+ let _sg = StackGuard :: new ( state) ;
220+ check_stack ( state, 3 ) ?;
207221
208- // Deregister metatable
209- let mt_ptr = get_metatable_ptr ( state, -1 ) ;
210- rawlua. deregister_userdata_metatable ( mt_ptr) ;
222+ // We don't write the data to the userdata until pushing the metatable
223+ let protect = !self . lua . unlikely_memory_error ( ) ;
224+ #[ cfg( feature = "luau" ) ]
225+ let ud_ptr = {
226+ let data = UserDataStorage :: new_scoped ( data) ;
227+ util:: push_userdata :: < UserDataStorage < T > > ( state, data, protect) ?
228+ } ;
229+ #[ cfg( not( feature = "luau" ) ) ]
230+ let ud_ptr = util:: push_uninit_userdata :: < UserDataStorage < T > > ( state, protect) ?;
211231
212- let ud = take_userdata :: < UserDataStorage < T > > ( state) ;
232+ // Push the metatable and register it with no TypeId
233+ let mut registry = UserDataRegistry :: new_unique ( ud_ptr as * mut _ ) ;
234+ register ( & mut registry) ;
235+ self . lua . push_userdata_metatable ( registry) ?;
236+ let mt_ptr = ffi:: lua_topointer ( state, -1 ) ;
237+ self . lua . register_userdata_metatable ( mt_ptr, None ) ;
213238
214- vec ! [ Box :: new( move || drop( ud) ) ]
215- } ) ;
216- self . destructors . 0 . borrow_mut ( ) . push ( ( ud. 0 . clone ( ) , destructor) ) ;
239+ // Write data to the pointer and attach metatable
240+ #[ cfg( not( feature = "luau" ) ) ]
241+ std:: ptr:: write ( ud_ptr, UserDataStorage :: new_scoped ( data) ) ;
242+ ffi:: lua_setmetatable ( state, -2 ) ;
217243
218- Ok ( ud)
219- }
244+ AnyUserData ( self . lua . pop_ref ( ) )
245+ } ;
246+ self . attach_destructor :: < T > ( & ud) ;
247+ Ok ( ud)
248+ }
249+
250+ fn attach_destructor < T : ' env > ( & ' scope self , ud : & AnyUserData ) {
251+ let destructor: DestructorCallback = Box :: new ( |rawlua, vref| unsafe {
252+ let state = rawlua. state ( ) ;
253+ let _sg = StackGuard :: new ( state) ;
254+ assert_stack ( state, 2 ) ;
255+
256+ // Check that userdata is valid (very likely)
257+ if rawlua. push_userdata_ref ( & vref) . is_err ( ) {
258+ return vec ! [ ] ;
259+ }
260+
261+ // Deregister metatable
262+ let mt_ptr = get_metatable_ptr ( state, -1 ) ;
263+ rawlua. deregister_userdata_metatable ( mt_ptr) ;
264+
265+ let ud = take_userdata :: < UserDataStorage < T > > ( state) ;
266+
267+ vec ! [ Box :: new( move || drop( ud) ) ]
268+ } ) ;
269+ self . destructors . 0 . borrow_mut ( ) . push ( ( ud. 0 . clone ( ) , destructor) ) ;
220270 }
221271
222272 /// Adds a destructor function to be run when the scope ends.
0 commit comments