@@ -455,6 +455,12 @@ public override bool VisitClassDecl(Class @class)
455
455
var dict = $@ "global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {
456
456
printedClass } >" ;
457
457
WriteLine ( "internal static readonly {0} NativeToManagedMap = new {0}();" , dict ) ;
458
+
459
+ // Add booleans to track who owns unmanaged memory for string fields
460
+ foreach ( var field in @class . Layout . Fields . Where ( f => f . QualifiedType . Type . IsConstCharString ( ) ) )
461
+ {
462
+ WriteLine ( $ "private bool __{ field . Name } _OwnsNativeMemory = false;") ;
463
+ }
458
464
}
459
465
PopBlock ( NewLineKind . BeforeNextBlock ) ;
460
466
}
@@ -871,7 +877,7 @@ private void GenerateClassField(Field field, bool @public = false)
871
877
PopBlock ( NewLineKind . BeforeNextBlock ) ;
872
878
}
873
879
874
- #endregion
880
+ #endregion
875
881
876
882
private void GeneratePropertySetter < T > ( T decl ,
877
883
Class @class , bool isAbstract = false , Property property = null )
@@ -1411,13 +1417,17 @@ private void GenerateFieldGetter(Field field, Class @class, QualifiedType return
1411
1417
if ( templateSubstitution != null && returnType . Type . IsDependent )
1412
1418
Write ( $ "({ templateSubstitution . ReplacedParameter . Parameter . Name } ) (object) ") ;
1413
1419
if ( ( final . IsPrimitiveType ( ) && ! final . IsPrimitiveType ( PrimitiveType . Void ) &&
1414
- ( ! final . IsPrimitiveType ( PrimitiveType . Char ) &&
1415
- ! final . IsPrimitiveType ( PrimitiveType . WideChar ) ||
1420
+ ( ( ! final . IsPrimitiveType ( PrimitiveType . Char ) &&
1421
+ ! final . IsPrimitiveType ( PrimitiveType . WideChar ) &&
1422
+ ! final . IsPrimitiveType ( PrimitiveType . Char16 ) &&
1423
+ ! final . IsPrimitiveType ( PrimitiveType . Char32 ) ) ||
1416
1424
( ! Context . Options . MarshalCharAsManagedChar &&
1417
1425
! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst ) ) &&
1418
1426
templateSubstitution == null ) ||
1419
1427
( ! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst &&
1420
- final . IsPrimitiveType ( PrimitiveType . WideChar ) ) )
1428
+ ( final . IsPrimitiveType ( PrimitiveType . WideChar ) ||
1429
+ final . IsPrimitiveType ( PrimitiveType . Char16 ) ||
1430
+ final . IsPrimitiveType ( PrimitiveType . Char32 ) ) ) )
1421
1431
Write ( $ "({ field . Type . GetPointee ( ) . Desugar ( ) } *) ") ;
1422
1432
}
1423
1433
WriteLine ( $ "{ @return } ;") ;
@@ -1626,7 +1636,7 @@ private void GenerateVariable(Class @class, Variable variable)
1626
1636
PopBlock ( NewLineKind . BeforeNextBlock ) ;
1627
1637
}
1628
1638
1629
- #region Virtual Tables
1639
+ #region Virtual Tables
1630
1640
1631
1641
public List < VTableComponent > GetUniqueVTableMethodEntries ( Class @class )
1632
1642
{
@@ -2033,9 +2043,9 @@ public bool HasVirtualTables(Class @class)
2033
2043
return @class . IsGenerated && @class . IsDynamic && GetUniqueVTableMethodEntries ( @class ) . Count > 0 ;
2034
2044
}
2035
2045
2036
- #endregion
2046
+ #endregion
2037
2047
2038
- #region Events
2048
+ #region Events
2039
2049
2040
2050
public override bool VisitEvent ( Event @event )
2041
2051
{
@@ -2143,9 +2153,9 @@ private void GenerateEventRaiseWrapper(Event @event, string delegateInstance)
2143
2153
UnindentAndWriteCloseBrace ( ) ;
2144
2154
}
2145
2155
2146
- #endregion
2156
+ #endregion
2147
2157
2148
- #region Constructors
2158
+ #region Constructors
2149
2159
2150
2160
public void GenerateClassConstructors ( Class @class )
2151
2161
{
@@ -2266,6 +2276,21 @@ c is ClassTemplateSpecialization ?
2266
2276
}
2267
2277
}
2268
2278
2279
+ // If we have any fields holding references to unmanaged memory allocated here, free the
2280
+ // referenced memory. Don't rely on testing if the field's IntPtr is IntPtr.Zero since
2281
+ // unmanaged memory isn't always initialized and/or a reference may be owned by the
2282
+ // native side.
2283
+ //
2284
+ // TODO: We should delegate to the dispose methods of references we hold to other
2285
+ // generated type instances since those instances could also hold references to
2286
+ // unmanaged memory.
2287
+ foreach ( var field in @class . Layout . Fields . Where ( f => f . QualifiedType . Type . IsConstCharString ( ) ) )
2288
+ {
2289
+ var ptr = $ "(({ Helpers . InternalStruct } *){ Helpers . InstanceIdentifier } )->{ field . Name } ";
2290
+ WriteLine ( $ "if (__{ field . Name } _OwnsNativeMemory)") ;
2291
+ WriteLineIndent ( $ "Marshal.FreeHGlobal({ ptr } );") ;
2292
+ }
2293
+
2269
2294
WriteLine ( "if ({0})" , Helpers . OwnsNativeInstanceIdentifier ) ;
2270
2295
WriteLineIndent ( "Marshal.FreeHGlobal({0});" , Helpers . InstanceIdentifier ) ;
2271
2296
@@ -2482,9 +2507,9 @@ private void GenerateClassConstructorBase(Class @class, Method method)
2482
2507
WriteLineIndent ( ": this()" ) ;
2483
2508
}
2484
2509
2485
- #endregion
2510
+ #endregion
2486
2511
2487
- #region Methods / Functions
2512
+ #region Methods / Functions
2488
2513
2489
2514
public void GenerateFunction ( Function function , string parentName )
2490
2515
{
@@ -2898,6 +2923,21 @@ private void GenerateClassConstructor(Method method, Class @class)
2898
2923
var classInternal = TypePrinter . PrintNative ( @class ) ;
2899
2924
WriteLine ( $@ "*(({ classInternal } *) { Helpers . InstanceIdentifier } ) = *(({
2900
2925
classInternal } *) { method . Parameters [ 0 ] . Name } .{ Helpers . InstanceIdentifier } );" ) ;
2926
+
2927
+ // Copy any string references owned by the source to the new instance so we
2928
+ // don't have to ref count them.
2929
+ foreach ( var field in @class . Fields . Where ( f => f . QualifiedType . Type . IsConstCharString ( ) ) )
2930
+ {
2931
+ var prop = @class . Properties . Where ( p => p . Field == field ) . FirstOrDefault ( ) ;
2932
+ // If there is no property or no setter then this instance can never own the native
2933
+ // memory. Worry about the case where there's only a setter (write-only) when we
2934
+ // understand the use case and how it can occur.
2935
+ if ( prop != null && prop . HasGetter && prop . HasSetter )
2936
+ {
2937
+ WriteLine ( $ "if ({ method . Parameters [ 0 ] . Name } .__{ field . OriginalName } _OwnsNativeMemory)") ;
2938
+ WriteLineIndent ( $@ "this.{ prop . Name } = { method . Parameters [ 0 ] . Name } .{ prop . Name } ;") ;
2939
+ }
2940
+ }
2901
2941
}
2902
2942
}
2903
2943
else
@@ -3230,7 +3270,7 @@ private string FormatMethodParameters(IEnumerable<Parameter> @params)
3230
3270
return TypePrinter . VisitParameters ( @params , true ) . Type ;
3231
3271
}
3232
3272
3233
- #endregion
3273
+ #endregion
3234
3274
3235
3275
public override bool VisitTypedefNameDecl ( TypedefNameDecl typedef )
3236
3276
{
0 commit comments