@@ -446,6 +446,90 @@ fn test_userdata_destroy() -> Result<()> {
446446 Ok ( ( ) )
447447}
448448
449+ #[ test]
450+ #[ cfg( panic = "unwind" ) ]
451+ fn test_userdata_destroy_panic ( ) -> Result < ( ) > {
452+ use std:: panic:: { AssertUnwindSafe , catch_unwind} ;
453+
454+ struct Panicking ;
455+ impl Drop for Panicking {
456+ fn drop ( & mut self ) {
457+ panic ! ( "userdata drop" ) ;
458+ }
459+ }
460+
461+ let lua = Lua :: new ( ) ;
462+ // Repeat beyond Lua's C-call limit
463+ for _ in 0 ..256 {
464+ let ud = lua. create_any_userdata ( Panicking ) ?;
465+ let panic = catch_unwind ( AssertUnwindSafe ( || ud. destroy ( ) ) ) . unwrap_err ( ) ;
466+ assert_eq ! ( panic. downcast_ref:: <& str >( ) , Some ( & "userdata drop" ) ) ;
467+ assert ! ( matches!( ud. destroy( ) , Err ( Error :: UserDataDestructed ) ) ) ;
468+ assert ! ( lua. inspect_stack( 0 , |_| ( ) ) . is_none( ) ) ;
469+ assert_eq ! ( lua. load( "return 42" ) . eval:: <i32 >( ) ?, 42 ) ;
470+ }
471+ lua. gc_collect ( ) ?;
472+
473+ Ok ( ( ) )
474+ }
475+
476+ #[ cfg( not( target_family = "wasm" ) ) ]
477+ #[ test]
478+ fn test_userdata_gc_panic ( ) -> Result < ( ) > {
479+ use std:: panic:: { AssertUnwindSafe , catch_unwind} ;
480+
481+ struct Panicking ;
482+ impl Drop for Panicking {
483+ fn drop ( & mut self ) {
484+ panic ! ( "userdata drop" ) ;
485+ }
486+ }
487+
488+ if let Ok ( mode) = std:: env:: var ( "MLUA_TEST_GC_PANIC" ) {
489+ let _ = catch_unwind ( AssertUnwindSafe ( || -> Result < ( ) > {
490+ let lua = Lua :: new ( ) ;
491+ lua. gc_stop ( ) ;
492+ if mode. starts_with ( "callback" ) {
493+ let value = Panicking ;
494+ lua. create_function ( move |_, ( ) | {
495+ let _ = & value;
496+ Ok ( ( ) )
497+ } ) ?;
498+ } else {
499+ lua. create_any_userdata ( Panicking ) ?;
500+ }
501+ if mode. ends_with ( "collect" ) {
502+ lua. gc_collect ( ) ?;
503+ lua. gc_collect ( ) ?;
504+ // Collection must abort before reaching here
505+ std:: process:: exit ( 0 ) ;
506+ }
507+ drop ( lua) ;
508+ Ok ( ( ) )
509+ } ) ) ;
510+ return Ok ( ( ) ) ;
511+ }
512+
513+ for mode in [
514+ "userdata_collect" ,
515+ "userdata_close" ,
516+ "callback_collect" ,
517+ "callback_close" ,
518+ ] {
519+ let output = std:: process:: Command :: new ( std:: env:: current_exe ( ) ?)
520+ . args ( [ "--exact" , "test_userdata_gc_panic" ] )
521+ . env ( "MLUA_TEST_GC_PANIC" , mode)
522+ . output ( ) ?;
523+ assert ! ( !output. status. success( ) , "{mode}" ) ;
524+ #[ cfg( unix) ]
525+ {
526+ use std:: os:: unix:: process:: ExitStatusExt ;
527+ assert_eq ! ( output. status. signal( ) , Some ( libc:: SIGABRT ) , "{mode}" ) ;
528+ }
529+ }
530+ Ok ( ( ) )
531+ }
532+
449533#[ test]
450534fn test_userdata_method_once ( ) -> Result < ( ) > {
451535 struct MyUserdata ( Arc < i64 > ) ;
0 commit comments