@@ -781,7 +781,7 @@ public override void GenerateClassSpecifier(Class @class)
781
781
}
782
782
}
783
783
784
- if ( @class . IsGenerated && isBindingGen && @class . IsRefType && ! @class . IsOpaque )
784
+ if ( @class . IsGenerated && isBindingGen && NeedsDispose ( @class ) && ! @class . IsOpaque )
785
785
{
786
786
bases . Add ( "IDisposable" ) ;
787
787
}
@@ -790,6 +790,12 @@ public override void GenerateClassSpecifier(Class @class)
790
790
Write ( " : {0}" , string . Join ( ", " , bases ) ) ;
791
791
}
792
792
793
+ private bool NeedsDispose ( Class @class )
794
+ {
795
+ return @class . IsRefType || @class . IsValueType &&
796
+ ( @class . GetConstCharFieldProperties ( ) . Any ( ) || @class . HasNonTrivialDestructor ) ;
797
+ }
798
+
793
799
private bool CanUseSequentialLayout ( Class @class )
794
800
{
795
801
if ( @class . IsUnion || @class . HasUnionFields )
@@ -2223,25 +2229,24 @@ public void GenerateClassConstructors(Class @class)
2223
2229
GenerateMethod ( ctor , @class ) ;
2224
2230
}
2225
2231
2226
- if ( @class . IsRefType )
2227
- {
2228
- // We used to always call GenerateClassFinalizer here. However, the
2229
- // finalizer calls Dispose which is conditionally implemented below.
2230
- // Instead, generate the finalizer only if Dispose is also implemented.
2232
+ // We used to always call GenerateClassFinalizer here. However, the
2233
+ // finalizer calls Dispose which is conditionally implemented below.
2234
+ // Instead, generate the finalizer only if Dispose is also implemented.
2231
2235
2232
- // ensure any virtual dtor in the chain is called
2233
- var dtor = @class . Destructors . FirstOrDefault ( d => d . Access != AccessSpecifier . Private ) ;
2234
- if ( ShouldGenerateClassNativeField ( @class ) ||
2235
- ( dtor != null && ( dtor . IsVirtual || @class . HasNonTrivialDestructor ) ) ||
2236
- // virtual destructors in abstract classes may lack a pointer in the v-table
2237
- // so they have to be called by symbol; thus we need an explicit Dispose override
2238
- @class . IsAbstract )
2239
- if ( ! @class . IsOpaque )
2240
- {
2236
+ // ensure any virtual dtor in the chain is called
2237
+ var dtor = @class . Destructors . FirstOrDefault ( d => d . Access != AccessSpecifier . Private ) ;
2238
+ if ( ShouldGenerateClassNativeField ( @class ) ||
2239
+ ( dtor != null && ( dtor . IsVirtual || @class . HasNonTrivialDestructor ) ) ||
2240
+ // virtual destructors in abstract classes may lack a pointer in the v-table
2241
+ // so they have to be called by symbol; thus we need an explicit Dispose override
2242
+ @class . IsAbstract )
2243
+ if ( ! @class . IsOpaque )
2244
+ {
2245
+ if ( @class . IsRefType )
2241
2246
GenerateClassFinalizer ( @class ) ;
2247
+ if ( NeedsDispose ( @class ) )
2242
2248
GenerateDisposeMethods ( @class ) ;
2243
- }
2244
- }
2249
+ }
2245
2250
}
2246
2251
2247
2252
private void GenerateClassFinalizer ( Class @class )
@@ -2250,19 +2255,23 @@ private void GenerateClassFinalizer(Class @class)
2250
2255
return ;
2251
2256
2252
2257
using ( PushWriteBlock ( BlockKind . Finalizer , $ "~{ @class . Name } ()", NewLineKind . BeforeNextBlock ) )
2253
- WriteLine ( $ "Dispose(false, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2258
+ WriteLine ( $ "Dispose(false, callNativeDtor: { Helpers . OwnsNativeInstanceIdentifier } );") ;
2254
2259
}
2255
2260
2256
2261
private void GenerateDisposeMethods ( Class @class )
2257
2262
{
2258
2263
var hasBaseClass = @class . HasBaseClass && @class . BaseClass . IsRefType ;
2259
2264
2265
+ var hasDtorParam = @class . IsRefType ;
2266
+
2260
2267
// Generate the IDispose Dispose() method.
2261
2268
if ( ! hasBaseClass )
2262
2269
{
2263
2270
using ( PushWriteBlock ( BlockKind . Method , "public void Dispose()" , NewLineKind . BeforeNextBlock ) )
2264
2271
{
2265
- WriteLine ( $ "Dispose(disposing: true, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2272
+ WriteLine ( hasDtorParam
2273
+ ? $ "Dispose(disposing: true, callNativeDtor: { Helpers . OwnsNativeInstanceIdentifier } );"
2274
+ : "Dispose(disposing: true);" ) ;
2266
2275
if ( Options . GenerateFinalizerFor ( @class ) )
2267
2276
WriteLine ( "GC.SuppressFinalize(this);" ) ;
2268
2277
}
@@ -2276,7 +2285,10 @@ private void GenerateDisposeMethods(Class @class)
2276
2285
2277
2286
// Generate Dispose(bool, bool) method
2278
2287
var ext = ! @class . IsValueType ? ( hasBaseClass ? "override " : "virtual " ) : string . Empty ;
2279
- using var _ = PushWriteBlock ( BlockKind . Method , $ "internal protected { ext } void Dispose(bool disposing, bool callNativeDtor )", NewLineKind . BeforeNextBlock ) ;
2288
+ var protectionLevel = @class . IsValueType ? "private" : "internal protected" ;
2289
+ using var _ = PushWriteBlock ( BlockKind . Method ,
2290
+ $ "{ protectionLevel } { ext } void Dispose(bool disposing{ ( hasDtorParam ? ", bool callNativeDtor" : "" ) } )",
2291
+ NewLineKind . BeforeNextBlock ) ;
2280
2292
2281
2293
if ( @class . IsRefType )
2282
2294
{
@@ -2323,7 +2335,7 @@ private void GenerateDisposeMethods(Class @class)
2323
2335
// instance from the NativeToManagedMap. Of course, this is somewhat half-hearted
2324
2336
// since we can't/don't do this when there's no virtual dtor available to detour.
2325
2337
// Anyway, we must be able to call the native dtor in this case even if we don't
2326
- /// own the underlying native instance.
2338
+ // own the underlying native instance.
2327
2339
//
2328
2340
// 2. When we we pass a disposable object to a function "by value" then the callee
2329
2341
// calls the dtor on the argument so our marshalling code must have a way from preventing
@@ -2335,41 +2347,67 @@ private void GenerateDisposeMethods(Class @class)
2335
2347
// }
2336
2348
//
2337
2349
// IDisposable.Dispose() and Object.Finalize() set callNativeDtor = Helpers.OwnsNativeInstanceIdentifier
2338
- WriteLine ( "if (callNativeDtor)" ) ;
2339
- if ( @class . IsDependent || dtor . IsVirtual )
2340
- WriteOpenBraceAndIndent ( ) ;
2341
- else
2342
- Indent ( ) ;
2350
+ if ( hasDtorParam )
2351
+ {
2352
+ WriteLine ( "if (callNativeDtor)" ) ;
2353
+ if ( @class . IsDependent || dtor . IsVirtual )
2354
+ WriteOpenBraceAndIndent ( ) ;
2355
+ else
2356
+ Indent ( ) ;
2357
+ }
2358
+
2343
2359
if ( dtor . IsVirtual )
2344
2360
this . GenerateMember ( @class , c => GenerateDestructorCall (
2345
2361
c is ClassTemplateSpecialization ?
2346
2362
c . Methods . First ( m => m . InstantiatedFrom == dtor ) : dtor ) ) ;
2347
2363
else
2348
2364
this . GenerateMember ( @class , c => GenerateMethodBody ( c , dtor ) ) ;
2349
- if ( @class . IsDependent || dtor . IsVirtual )
2350
- UnindentAndWriteCloseBrace ( ) ;
2351
- else
2352
- Unindent ( ) ;
2365
+
2366
+ if ( hasDtorParam )
2367
+ {
2368
+ if ( @class . IsDependent || dtor . IsVirtual )
2369
+ UnindentAndWriteCloseBrace ( ) ;
2370
+ else
2371
+ Unindent ( ) ;
2372
+ }
2353
2373
}
2354
2374
}
2355
2375
2356
2376
// If we have any fields holding references to unmanaged memory allocated here, free the
2357
2377
// referenced memory. Don't rely on testing if the field's IntPtr is IntPtr.Zero since
2358
2378
// unmanaged memory isn't always initialized and/or a reference may be owned by the
2359
2379
// native side.
2360
- //
2380
+
2381
+ string ptr ;
2382
+ if ( @class . IsValueType )
2383
+ {
2384
+ ptr = $ "{ Helpers . InstanceIdentifier } Ptr";
2385
+ WriteLine ( $ "fixed ({ Helpers . InternalStruct } * { ptr } = &{ Helpers . InstanceIdentifier } )") ;
2386
+ WriteOpenBraceAndIndent ( ) ;
2387
+ }
2388
+ else
2389
+ {
2390
+ ptr = $ "(({ Helpers . InternalStruct } *){ Helpers . InstanceIdentifier } )";
2391
+ }
2392
+
2361
2393
foreach ( var prop in @class . GetConstCharFieldProperties ( ) )
2362
2394
{
2363
2395
string name = prop . Field . OriginalName ;
2364
- var ptr = $ "(({ Helpers . InternalStruct } *){ Helpers . InstanceIdentifier } )->{ name } ";
2365
2396
WriteLine ( $ "if (__{ name } _OwnsNativeMemory)") ;
2366
- WriteLineIndent ( $ "Marshal.FreeHGlobal({ ptr } );") ;
2397
+ WriteLineIndent ( $ "Marshal.FreeHGlobal({ ptr } -> { name } );") ;
2367
2398
}
2368
2399
2369
- WriteLine ( "if ({0})" , Helpers . OwnsNativeInstanceIdentifier ) ;
2370
- WriteLineIndent ( "Marshal.FreeHGlobal({0});" , Helpers . InstanceIdentifier ) ;
2400
+ if ( @class . IsValueType )
2401
+ {
2402
+ UnindentAndWriteCloseBrace ( ) ;
2403
+ }
2404
+ else
2405
+ {
2406
+ WriteLine ( "if ({0})" , Helpers . OwnsNativeInstanceIdentifier ) ;
2407
+ WriteLineIndent ( "Marshal.FreeHGlobal({0});" , Helpers . InstanceIdentifier ) ;
2371
2408
2372
- WriteLine ( "{0} = IntPtr.Zero;" , Helpers . InstanceIdentifier ) ;
2409
+ WriteLine ( "{0} = IntPtr.Zero;" , Helpers . InstanceIdentifier ) ;
2410
+ }
2373
2411
}
2374
2412
2375
2413
private bool GenerateDestructorCall ( Method dtor )
0 commit comments