@@ -186,6 +186,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
186186 let state = u. lua . state ;
187187 assert_stack ( state, 2 ) ;
188188 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+
189197 // We know the destructor has not run yet because we hold a reference to the
190198 // userdata.
191199 vec ! [ Box :: new( take_userdata:: <UserDataCell <T >>( state) ) ]
@@ -244,28 +252,28 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
244252 let check_ud_type = move |lua : & ' callback Lua , value| {
245253 if let Some ( Value :: UserData ( ud) ) = value {
246254 unsafe {
247- assert_stack ( lua. state , 1 ) ;
255+ let _sg = StackGuard :: new ( lua. state ) ;
256+ assert_stack ( lua. state , 3 ) ;
248257 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+ }
254267 }
255- return ffi:: lua_touserdata ( lua. state , -1 )
256- == check_data. as_ptr ( ) as * mut c_void ;
257268 }
258- }
259-
260- false
269+ } ;
270+ Err ( Error :: UserDataTypeMismatch )
261271 } ;
262272
263273 match method {
264274 NonStaticMethod :: Method ( method) => {
265275 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 ( ) ) ?;
269277 let data = data
270278 . try_borrow ( )
271279 . map ( |cell| Ref :: map ( cell, AsRef :: as_ref) )
@@ -277,9 +285,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
277285 NonStaticMethod :: MethodMut ( method) => {
278286 let method = RefCell :: new ( method) ;
279287 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 ( ) ) ?;
283289 let mut method = method
284290 . try_borrow_mut ( )
285291 . map_err ( |_| Error :: RecursiveMutCallback ) ?;
@@ -314,23 +320,18 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
314320 unsafe {
315321 let lua = self . lua ;
316322 let _sg = StackGuard :: new ( lua. state ) ;
317- assert_stack ( lua. state , 6 ) ;
323+ assert_stack ( lua. state , 13 ) ;
318324
319325 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 ) ;
330326
331327 // 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| {
333329 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 ) ;
334335 } ) ?;
335336 for ( k, m) in ud_methods. meta_methods {
336337 push_string ( lua. state , k. validate ( ) ?. name ( ) ) ?;
@@ -413,8 +414,26 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
413414 ffi:: lua_pop ( lua. state , count) ;
414415
415416 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+ } ) ) ;
416435
417- Ok ( AnyUserData ( lua . pop_ref ( ) ) )
436+ Ok ( ud )
418437 }
419438 }
420439
0 commit comments