@@ -781,7 +781,7 @@ public override void GenerateClassSpecifier(Class @class)
781781 }
782782 }
783783
784- if ( @class . IsGenerated && isBindingGen && @class . IsRefType && ! @class . IsOpaque )
784+ if ( @class . IsGenerated && isBindingGen && NeedsDispose ( @class ) && ! @class . IsOpaque )
785785 {
786786 bases . Add ( "IDisposable" ) ;
787787 }
@@ -790,6 +790,12 @@ public override void GenerateClassSpecifier(Class @class)
790790 Write ( " : {0}" , string . Join ( ", " , bases ) ) ;
791791 }
792792
793+ private bool NeedsDispose ( Class @class )
794+ {
795+ return @class . IsRefType || @class . IsValueType &&
796+ ( @class . GetConstCharFieldProperties ( ) . Any ( ) || @class . HasNonTrivialDestructor ) ;
797+ }
798+
793799 private bool CanUseSequentialLayout ( Class @class )
794800 {
795801 if ( @class . IsUnion || @class . HasUnionFields )
@@ -2223,25 +2229,24 @@ public void GenerateClassConstructors(Class @class)
22232229 GenerateMethod ( ctor , @class ) ;
22242230 }
22252231
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.
22312235
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 )
22412246 GenerateClassFinalizer ( @class ) ;
2247+ if ( NeedsDispose ( @class ) )
22422248 GenerateDisposeMethods ( @class ) ;
2243- }
2244- }
2249+ }
22452250 }
22462251
22472252 private void GenerateClassFinalizer ( Class @class )
@@ -2250,19 +2255,23 @@ private void GenerateClassFinalizer(Class @class)
22502255 return ;
22512256
22522257 using ( PushWriteBlock ( BlockKind . Finalizer , $ "~{ @class . Name } ()", NewLineKind . BeforeNextBlock ) )
2253- WriteLine ( $ "Dispose(false, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2258+ WriteLine ( $ "Dispose(false, callNativeDtor: { Helpers . OwnsNativeInstanceIdentifier } );") ;
22542259 }
22552260
22562261 private void GenerateDisposeMethods ( Class @class )
22572262 {
22582263 var hasBaseClass = @class . HasBaseClass && @class . BaseClass . IsRefType ;
22592264
2265+ var hasDtorParam = @class . IsRefType ;
2266+
22602267 // Generate the IDispose Dispose() method.
22612268 if ( ! hasBaseClass )
22622269 {
22632270 using ( PushWriteBlock ( BlockKind . Method , "public void Dispose()" , NewLineKind . BeforeNextBlock ) )
22642271 {
2265- WriteLine ( $ "Dispose(disposing: true, callNativeDtor : { Helpers . OwnsNativeInstanceIdentifier } );") ;
2272+ WriteLine ( hasDtorParam
2273+ ? $ "Dispose(disposing: true, callNativeDtor: { Helpers . OwnsNativeInstanceIdentifier } );"
2274+ : "Dispose(disposing: true);" ) ;
22662275 if ( Options . GenerateFinalizerFor ( @class ) )
22672276 WriteLine ( "GC.SuppressFinalize(this);" ) ;
22682277 }
@@ -2276,7 +2285,10 @@ private void GenerateDisposeMethods(Class @class)
22762285
22772286 // Generate Dispose(bool, bool) method
22782287 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 ) ;
22802292
22812293 if ( @class . IsRefType )
22822294 {
@@ -2323,7 +2335,7 @@ private void GenerateDisposeMethods(Class @class)
23232335 // instance from the NativeToManagedMap. Of course, this is somewhat half-hearted
23242336 // since we can't/don't do this when there's no virtual dtor available to detour.
23252337 // 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.
23272339 //
23282340 // 2. When we we pass a disposable object to a function "by value" then the callee
23292341 // 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)
23352347 // }
23362348 //
23372349 // 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+
23432359 if ( dtor . IsVirtual )
23442360 this . GenerateMember ( @class , c => GenerateDestructorCall (
23452361 c is ClassTemplateSpecialization ?
23462362 c . Methods . First ( m => m . InstantiatedFrom == dtor ) : dtor ) ) ;
23472363 else
23482364 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+ }
23532373 }
23542374 }
23552375
23562376 // If we have any fields holding references to unmanaged memory allocated here, free the
23572377 // referenced memory. Don't rely on testing if the field's IntPtr is IntPtr.Zero since
23582378 // unmanaged memory isn't always initialized and/or a reference may be owned by the
23592379 // 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+
23612393 foreach ( var prop in @class . GetConstCharFieldProperties ( ) )
23622394 {
23632395 string name = prop . Field . OriginalName ;
2364- var ptr = $ "(({ Helpers . InternalStruct } *){ Helpers . InstanceIdentifier } )->{ name } ";
23652396 WriteLine ( $ "if (__{ name } _OwnsNativeMemory)") ;
2366- WriteLineIndent ( $ "Marshal.FreeHGlobal({ ptr } );") ;
2397+ WriteLineIndent ( $ "Marshal.FreeHGlobal({ ptr } -> { name } );") ;
23672398 }
23682399
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 ) ;
23712408
2372- WriteLine ( "{0} = IntPtr.Zero;" , Helpers . InstanceIdentifier ) ;
2409+ WriteLine ( "{0} = IntPtr.Zero;" , Helpers . InstanceIdentifier ) ;
2410+ }
23732411 }
23742412
23752413 private bool GenerateDestructorCall ( Method dtor )
0 commit comments