@@ -171,7 +171,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
171
171
let _sg = StackGuard :: new ( state) ;
172
172
check_stack ( state, 3 ) ?;
173
173
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
175
175
let protect = !self . lua . unlikely_memory_error ( ) ;
176
176
#[ cfg( feature = "luau" ) ]
177
177
let ud_ptr = {
@@ -194,29 +194,79 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
194
194
ffi:: lua_setmetatable ( state, -2 ) ;
195
195
196
196
let ud = AnyUserData ( self . lua . pop_ref ( ) ) ;
197
+ self . attach_destructor :: < T > ( & ud) ;
197
198
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
+ }
202
202
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 ) ?;
207
221
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) ?;
211
231
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 ) ;
213
238
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 ) ;
217
243
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) ) ;
220
270
}
221
271
222
272
/// Adds a destructor function to be run when the scope ends.
0 commit comments