@@ -186,6 +186,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
186
186
let state = u. lua . state ;
187
187
assert_stack ( state, 2 ) ;
188
188
u. lua . push_ref ( & u) ;
189
+
190
+ // Clear uservalue
191
+ #[ cfg( any( feature = "lua54" , feature = "lua53" , feature = "lua52" ) ) ]
192
+ ffi:: lua_pushnil ( state) ;
193
+ #[ cfg( any( feature = "lua51" , feature = "luajit" ) ) ]
194
+ ffi:: lua_newtable ( state) ;
195
+ ffi:: lua_setuservalue ( state, -2 ) ;
196
+
189
197
// We know the destructor has not run yet because we hold a reference to the
190
198
// userdata.
191
199
vec ! [ Box :: new( take_userdata:: <UserDataCell <T >>( state) ) ]
@@ -244,28 +252,28 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
244
252
let check_ud_type = move |lua : & ' callback Lua , value| {
245
253
if let Some ( Value :: UserData ( ud) ) = value {
246
254
unsafe {
247
- assert_stack ( lua. state , 1 ) ;
255
+ let _sg = StackGuard :: new ( lua. state ) ;
256
+ assert_stack ( lua. state , 3 ) ;
248
257
lua. push_ref ( & ud. 0 ) ;
249
- ffi:: lua_getuservalue ( lua. state , -1 ) ;
250
- #[ cfg( any( feature = "lua52" , feature = "lua51" , feature = "luajit" ) ) ]
251
- {
252
- ffi:: lua_rawgeti ( lua. state , -1 , 1 ) ;
253
- ffi:: lua_remove ( lua. state , -2 ) ;
258
+ if ffi:: lua_getmetatable ( lua. state , -1 ) == 0 {
259
+ return Err ( Error :: UserDataTypeMismatch ) ;
260
+ }
261
+ ffi:: lua_pushstring ( lua. state , cstr ! ( "__mlua" ) ) ;
262
+ if ffi:: lua_rawget ( lua. state , -2 ) == ffi:: LUA_TLIGHTUSERDATA {
263
+ let ud_ptr = ffi:: lua_touserdata ( lua. state , -1 ) ;
264
+ if ud_ptr == check_data. as_ptr ( ) as * mut c_void {
265
+ return Ok ( ( ) ) ;
266
+ }
254
267
}
255
- return ffi:: lua_touserdata ( lua. state , -1 )
256
- == check_data. as_ptr ( ) as * mut c_void ;
257
268
}
258
- }
259
-
260
- false
269
+ } ;
270
+ Err ( Error :: UserDataTypeMismatch )
261
271
} ;
262
272
263
273
match method {
264
274
NonStaticMethod :: Method ( method) => {
265
275
let f = Box :: new ( move |lua, mut args : MultiValue < ' callback > | {
266
- if !check_ud_type ( lua, args. pop_front ( ) ) {
267
- return Err ( Error :: UserDataTypeMismatch ) ;
268
- }
276
+ check_ud_type ( lua, args. pop_front ( ) ) ?;
269
277
let data = data
270
278
. try_borrow ( )
271
279
. map ( |cell| Ref :: map ( cell, AsRef :: as_ref) )
@@ -277,9 +285,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
277
285
NonStaticMethod :: MethodMut ( method) => {
278
286
let method = RefCell :: new ( method) ;
279
287
let f = Box :: new ( move |lua, mut args : MultiValue < ' callback > | {
280
- if !check_ud_type ( lua, args. pop_front ( ) ) {
281
- return Err ( Error :: UserDataTypeMismatch ) ;
282
- }
288
+ check_ud_type ( lua, args. pop_front ( ) ) ?;
283
289
let mut method = method
284
290
. try_borrow_mut ( )
285
291
. map_err ( |_| Error :: RecursiveMutCallback ) ?;
@@ -314,23 +320,18 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
314
320
unsafe {
315
321
let lua = self . lua ;
316
322
let _sg = StackGuard :: new ( lua. state ) ;
317
- assert_stack ( lua. state , 6 ) ;
323
+ assert_stack ( lua. state , 13 ) ;
318
324
319
325
push_userdata ( lua. state , ( ) ) ?;
320
- #[ cfg( any( feature = "lua54" , feature = "lua53" ) ) ]
321
- ffi:: lua_pushlightuserdata ( lua. state , data. as_ptr ( ) as * mut c_void ) ;
322
- #[ cfg( any( feature = "lua52" , feature = "lua51" , feature = "luajit" ) ) ]
323
- protect_lua_closure ( lua. state , 0 , 1 , |state| {
324
- // Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
325
- ffi:: lua_createtable ( state, 1 , 0 ) ;
326
- ffi:: lua_pushlightuserdata ( state, data. as_ptr ( ) as * mut c_void ) ;
327
- ffi:: lua_rawseti ( state, -2 , 1 ) ;
328
- } ) ?;
329
- ffi:: lua_setuservalue ( lua. state , -2 ) ;
330
326
331
327
// Prepare metatable, add meta methods first and then meta fields
332
- protect_lua_closure ( lua. state , 0 , 1 , move |state| {
328
+ protect_lua_closure ( lua. state , 0 , 1 , |state| {
333
329
ffi:: lua_newtable ( state) ;
330
+
331
+ // Add internal metamethod to store reference to the data
332
+ ffi:: lua_pushstring ( state, cstr ! ( "__mlua" ) ) ;
333
+ ffi:: lua_pushlightuserdata ( lua. state , data. as_ptr ( ) as * mut c_void ) ;
334
+ ffi:: lua_rawset ( state, -3 ) ;
334
335
} ) ?;
335
336
for ( k, m) in ud_methods. meta_methods {
336
337
push_string ( lua. state , k. validate ( ) ?. name ( ) ) ?;
@@ -413,8 +414,26 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
413
414
ffi:: lua_pop ( lua. state , count) ;
414
415
415
416
ffi:: lua_setmetatable ( lua. state , -2 ) ;
417
+ let ud = AnyUserData ( lua. pop_ref ( ) ) ;
418
+
419
+ self . destructors . borrow_mut ( ) . push ( ( ud. 0 . clone ( ) , |u| {
420
+ let state = u. lua . state ;
421
+ assert_stack ( state, 2 ) ;
422
+ u. lua . push_ref ( & u) ;
423
+
424
+ // Clear uservalue
425
+ #[ cfg( any( feature = "lua54" , feature = "lua53" , feature = "lua52" ) ) ]
426
+ ffi:: lua_pushnil ( state) ;
427
+ #[ cfg( any( feature = "lua51" , feature = "luajit" ) ) ]
428
+ ffi:: lua_newtable ( state) ;
429
+ ffi:: lua_setuservalue ( state, -2 ) ;
430
+
431
+ // We know the destructor has not run yet because we hold a reference to the
432
+ // userdata.
433
+ vec ! [ Box :: new( take_userdata:: <( ) >( state) ) ]
434
+ } ) ) ;
416
435
417
- Ok ( AnyUserData ( lua . pop_ref ( ) ) )
436
+ Ok ( ud )
418
437
}
419
438
}
420
439
0 commit comments