@@ -1879,7 +1879,7 @@ private void GenerateVTableManagedCall(Method method)
1879
1879
{
1880
1880
if ( method . IsDestructor )
1881
1881
{
1882
- WriteLine ( "{0}.Dispose(true);" , Helpers . TargetIdentifier ) ;
1882
+ WriteLine ( "{0}.Dispose(disposing: true, callNativeDtor: true);" , Helpers . TargetIdentifier ) ;
1883
1883
return ;
1884
1884
}
1885
1885
@@ -2200,7 +2200,7 @@ private void GenerateClassFinalizer(INamedDecl @class)
2200
2200
2201
2201
WriteLine ( "~{0}()" , @class . Name ) ;
2202
2202
WriteOpenBraceAndIndent ( ) ;
2203
- WriteLine ( "Dispose(false);" ) ;
2203
+ WriteLine ( $ "Dispose(false, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2204
2204
UnindentAndWriteCloseBrace ( ) ;
2205
2205
2206
2206
PopBlock ( NewLineKind . BeforeNextBlock ) ;
@@ -2217,21 +2217,27 @@ private void GenerateDisposeMethods(Class @class)
2217
2217
WriteLine ( "public void Dispose()" ) ;
2218
2218
WriteOpenBraceAndIndent ( ) ;
2219
2219
2220
- WriteLine ( "Dispose(disposing: true);" ) ;
2220
+ WriteLine ( $ "Dispose(disposing: true, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2221
2221
if ( Options . GenerateFinalizers )
2222
2222
WriteLine ( "GC.SuppressFinalize(this);" ) ;
2223
2223
2224
2224
UnindentAndWriteCloseBrace ( ) ;
2225
2225
PopBlock ( NewLineKind . BeforeNextBlock ) ;
2226
2226
}
2227
2227
2228
- // Generate Dispose(bool) method
2228
+ // Declare partial method that the partial class can implement to participate
2229
+ // in dispose.
2229
2230
PushBlock ( BlockKind . Method ) ;
2230
- Write ( "public " ) ;
2231
+ WriteLine ( "partial void DisposePartial(bool disposing);" ) ;
2232
+ PopBlock ( NewLineKind . BeforeNextBlock ) ;
2233
+
2234
+ // Generate Dispose(bool, bool) method
2235
+ PushBlock ( BlockKind . Method ) ;
2236
+ Write ( "internal protected " ) ;
2231
2237
if ( ! @class . IsValueType )
2232
2238
Write ( hasBaseClass ? "override " : "virtual " ) ;
2233
2239
2234
- WriteLine ( "void Dispose(bool disposing)" ) ;
2240
+ WriteLine ( "void Dispose(bool disposing, bool callNativeDtor )" ) ;
2235
2241
WriteOpenBraceAndIndent ( ) ;
2236
2242
2237
2243
if ( @class . IsRefType )
@@ -2251,6 +2257,13 @@ private void GenerateDisposeMethods(Class @class)
2251
2257
}
2252
2258
}
2253
2259
2260
+ // TODO: if disposing == true we should delegate to the dispose methods of references
2261
+ // we hold to other generated type instances since those instances could also hold
2262
+ // references to unmanaged memory.
2263
+ //
2264
+ // Delegate to partial method if implemented
2265
+ WriteLine ( "DisposePartial(disposing);" ) ;
2266
+
2254
2267
var dtor = @class . Destructors . FirstOrDefault ( ) ;
2255
2268
if ( dtor != null && dtor . Access != AccessSpecifier . Private &&
2256
2269
@class . HasNonTrivialDestructor && ! @class . IsAbstract )
@@ -2259,7 +2272,29 @@ private void GenerateDisposeMethods(Class @class)
2259
2272
if ( ! Options . CheckSymbols ||
2260
2273
Context . Symbols . FindLibraryBySymbol ( dtor . Mangled , out library ) )
2261
2274
{
2262
- WriteLine ( "if (disposing)" ) ;
2275
+ // Normally, calling the native dtor should be controlled by whether or not we
2276
+ // we own the underlying instance. (i.e. Helpers.OwnsNativeInstanceIdentifier).
2277
+ // However, there are 2 situations when the caller needs to have direct control
2278
+ //
2279
+ // 1. When we have a virtual dtor on the native side we detour the vtable entry
2280
+ // even when we don't own the underlying native instance. I think we do this
2281
+ // so that the managed side can null out the __Instance pointer and remove the
2282
+ // instance from the NativeToManagedMap. Of course, this is somewhat half-hearted
2283
+ // since we can't/don't do this when there's no virtual dtor available to detour.
2284
+ // Anyway, we must be able to call the native dtor in this case even if we don't
2285
+ /// own the underlying native instance.
2286
+ //
2287
+ // 2. When we we pass a disposable object to a function "by value" then the callee
2288
+ // calls the dtor on the argument so our marshalling code must have a way from preventing
2289
+ // a duplicate call. Here's a native function that exhibits this behavior:
2290
+ // void f(std::string f)
2291
+ // {
2292
+ // ....
2293
+ // compiler generates call to f.dtor() at the end of function
2294
+ // }
2295
+ //
2296
+ // IDisposable.Dispose() and Object.Finalize() set callNativeDtor = Helpers.OwnsNativeInstanceIdentifier
2297
+ WriteLine ( $ "if (callNativeDtor)") ;
2263
2298
if ( @class . IsDependent || dtor . IsVirtual )
2264
2299
WriteOpenBraceAndIndent ( ) ;
2265
2300
else
@@ -2282,9 +2317,6 @@ c is ClassTemplateSpecialization ?
2282
2317
// unmanaged memory isn't always initialized and/or a reference may be owned by the
2283
2318
// native side.
2284
2319
//
2285
- // TODO: We should delegate to the dispose methods of references we hold to other
2286
- // generated type instances since those instances could also hold references to
2287
- // unmanaged memory.
2288
2320
foreach ( var field in @class . Layout . Fields . Where ( f => f . QualifiedType . Type . IsConstCharString ( ) ) )
2289
2321
{
2290
2322
var ptr = $ "(({ Helpers . InternalStruct } *){ Helpers . InstanceIdentifier } )->{ field . Name } ";
0 commit comments