49
49
50
50
use alloc:: boxed:: Box ;
51
51
use core:: any:: Any ;
52
- use core:: mem;
53
- use core:: raw;
52
+ use core:: mem:: { self , ManuallyDrop } ;
54
53
use libc:: { c_int, c_uint, c_void} ;
55
54
55
+ struct Exception {
56
+ // This needs to be an Option because we catch the exception by reference
57
+ // and its destructor is executed by the C++ runtime. When we take the Box
58
+ // out of the exception, we need to leave the exception in a valid state
59
+ // for its destructor to run without double-dropping the Box.
60
+ data : Option < Box < dyn Any + Send > > ,
61
+ }
62
+
56
63
// First up, a whole bunch of type definitions. There's a few platform-specific
57
64
// oddities here, and a lot that's just blatantly copied from LLVM. The purpose
58
65
// of all this is to implement the `panic` function below through a call to
@@ -186,7 +193,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
186
193
properties : 0 ,
187
194
pType : ptr ! ( 0 ) ,
188
195
thisDisplacement : _PMD { mdisp : 0 , pdisp : -1 , vdisp : 0 } ,
189
- sizeOrOffset : mem:: size_of :: < [ u64 ; 2 ] > ( ) as c_int ,
196
+ sizeOrOffset : mem:: size_of :: < Exception > ( ) as c_int ,
190
197
copyFunction : ptr ! ( 0 ) ,
191
198
} ;
192
199
@@ -229,16 +236,16 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
229
236
// because Box<dyn Any> isn't clonable.
230
237
macro_rules! define_cleanup {
231
238
( $abi: tt) => {
232
- unsafe extern $abi fn exception_cleanup( e: * mut [ u64 ; 2 ] ) {
233
- if ( * e ) [ 0 ] != 0 {
234
- cleanup ( * e ) ;
239
+ unsafe extern $abi fn exception_cleanup( e: * mut Exception ) {
240
+ if let Some ( b ) = e . read ( ) . data {
241
+ drop ( b ) ;
235
242
super :: __rust_drop_panic( ) ;
236
243
}
237
244
}
238
245
#[ unwind( allowed) ]
239
- unsafe extern $abi fn exception_copy( _dest: * mut [ u64 ; 2 ] ,
240
- _src: * mut [ u64 ; 2 ] )
241
- -> * mut [ u64 ; 2 ] {
246
+ unsafe extern $abi fn exception_copy( _dest: * mut Exception ,
247
+ _src: * mut Exception )
248
+ -> * mut Exception {
242
249
panic!( "Rust panics cannot be copied" ) ;
243
250
}
244
251
}
@@ -258,12 +265,11 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
258
265
// need to otherwise transfer `data` to the heap. We just pass a stack
259
266
// pointer to this function.
260
267
//
261
- // The first argument is the payload being thrown (our two pointers), and
262
- // the second argument is the type information object describing the
263
- // exception (constructed above).
264
- let ptrs = mem:: transmute :: < _ , raw:: TraitObject > ( data) ;
265
- let mut ptrs = [ ptrs. data as u64 , ptrs. vtable as u64 ] ;
266
- let throw_ptr = ptrs. as_mut_ptr ( ) as * mut _ ;
268
+ // The ManuallyDrop is needed here since we don't want Exception to be
269
+ // dropped when unwinding. Instead it will be dropped by exception_cleanup
270
+ // which is invoked by the C++ runtime.
271
+ let mut exception = ManuallyDrop :: new ( Exception { data : Some ( data) } ) ;
272
+ let throw_ptr = & mut exception as * mut _ as * mut _ ;
267
273
268
274
// This... may seems surprising, and justifiably so. On 32-bit MSVC the
269
275
// pointers between these structure are just that, pointers. On 64-bit MSVC,
@@ -311,8 +317,9 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
311
317
_CxxThrowException ( throw_ptr, & mut THROW_INFO as * mut _ as * mut _ ) ;
312
318
}
313
319
314
- pub unsafe fn cleanup ( payload : [ u64 ; 2 ] ) -> Box < dyn Any + Send > {
315
- mem:: transmute ( raw:: TraitObject { data : payload[ 0 ] as * mut _ , vtable : payload[ 1 ] as * mut _ } )
320
+ pub unsafe fn cleanup ( payload : * mut u8 ) -> Box < dyn Any + Send > {
321
+ let exception = & mut * ( payload as * mut Exception ) ;
322
+ exception. data . take ( ) . unwrap ( )
316
323
}
317
324
318
325
// This is required by the compiler to exist (e.g., it's a lang item), but
0 commit comments