@ -455,6 +455,12 @@ namespace CppSharp.Generators.CSharp
var dict = $ @ "global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {
var dict = $ @ "global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {
printedClass } > ";
printedClass } > ";
WriteLine ( "internal static readonly {0} NativeToManagedMap = new {0}();" , dict ) ;
WriteLine ( "internal static readonly {0} NativeToManagedMap = new {0}();" , dict ) ;
// Add booleans to track who owns unmanaged memory for string fields
foreach ( var field in @class . Layout . Fields . Where ( f = > f . QualifiedType . Type . IsConstCharString ( ) ) )
{
WriteLine ( $"private bool __{field.Name}_OwnsNativeMemory = false;" ) ;
}
}
}
PopBlock ( NewLineKind . BeforeNextBlock ) ;
PopBlock ( NewLineKind . BeforeNextBlock ) ;
}
}
@ -871,7 +877,7 @@ namespace CppSharp.Generators.CSharp
PopBlock ( NewLineKind . BeforeNextBlock ) ;
PopBlock ( NewLineKind . BeforeNextBlock ) ;
}
}
#endregion
#endregion
private void GeneratePropertySetter < T > ( T decl ,
private void GeneratePropertySetter < T > ( T decl ,
Class @class , bool isAbstract = false , Property property = null )
Class @class , bool isAbstract = false , Property property = null )
@ -1411,13 +1417,17 @@ namespace CppSharp.Generators.CSharp
if ( templateSubstitution ! = null & & returnType . Type . IsDependent )
if ( templateSubstitution ! = null & & returnType . Type . IsDependent )
Write ( $"({templateSubstitution.ReplacedParameter.Parameter.Name}) (object) " ) ;
Write ( $"({templateSubstitution.ReplacedParameter.Parameter.Name}) (object) " ) ;
if ( ( final . IsPrimitiveType ( ) & & ! final . IsPrimitiveType ( PrimitiveType . Void ) & &
if ( ( final . IsPrimitiveType ( ) & & ! final . IsPrimitiveType ( PrimitiveType . Void ) & &
( ! final . IsPrimitiveType ( PrimitiveType . Char ) & &
( ( ! final . IsPrimitiveType ( PrimitiveType . Char ) & &
! final . IsPrimitiveType ( PrimitiveType . WideChar ) | |
! final . IsPrimitiveType ( PrimitiveType . WideChar ) & &
! final . IsPrimitiveType ( PrimitiveType . Char16 ) & &
! final . IsPrimitiveType ( PrimitiveType . Char32 ) ) | |
( ! Context . Options . MarshalCharAsManagedChar & &
( ! Context . Options . MarshalCharAsManagedChar & &
! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst ) ) & &
! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst ) ) & &
templateSubstitution = = null ) | |
templateSubstitution = = null ) | |
( ! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst & &
( ! ( ( PointerType ) field . Type ) . QualifiedPointee . Qualifiers . IsConst & &
final . IsPrimitiveType ( PrimitiveType . WideChar ) ) )
( final . IsPrimitiveType ( PrimitiveType . WideChar ) | |
final . IsPrimitiveType ( PrimitiveType . Char16 ) | |
final . IsPrimitiveType ( PrimitiveType . Char32 ) ) ) )
Write ( $"({field.Type.GetPointee().Desugar()}*) " ) ;
Write ( $"({field.Type.GetPointee().Desugar()}*) " ) ;
}
}
WriteLine ( $"{@return};" ) ;
WriteLine ( $"{@return};" ) ;
@ -1626,7 +1636,7 @@ namespace CppSharp.Generators.CSharp
PopBlock ( NewLineKind . BeforeNextBlock ) ;
PopBlock ( NewLineKind . BeforeNextBlock ) ;
}
}
#region Virtual Tables
#region Virtual Tables
public List < VTableComponent > GetUniqueVTableMethodEntries ( Class @class )
public List < VTableComponent > GetUniqueVTableMethodEntries ( Class @class )
{
{
@ -2033,9 +2043,9 @@ namespace CppSharp.Generators.CSharp
return @class . IsGenerated & & @class . IsDynamic & & GetUniqueVTableMethodEntries ( @class ) . Count > 0 ;
return @class . IsGenerated & & @class . IsDynamic & & GetUniqueVTableMethodEntries ( @class ) . Count > 0 ;
}
}
#endregion
#endregion
#region Events
#region Events
public override bool VisitEvent ( Event @event )
public override bool VisitEvent ( Event @event )
{
{
@ -2143,9 +2153,9 @@ namespace CppSharp.Generators.CSharp
UnindentAndWriteCloseBrace ( ) ;
UnindentAndWriteCloseBrace ( ) ;
}
}
#endregion
#endregion
#region Constructors
#region Constructors
public void GenerateClassConstructors ( Class @class )
public void GenerateClassConstructors ( Class @class )
{
{
@ -2266,6 +2276,21 @@ namespace CppSharp.Generators.CSharp
}
}
}
}
// If we have any fields holding references to unmanaged memory allocated here, free the
// referenced memory. Don't rely on testing if the field's IntPtr is IntPtr.Zero since
// unmanaged memory isn't always initialized and/or a reference may be owned by the
// native side.
//
// TODO: We should delegate to the dispose methods of references we hold to other
// generated type instances since those instances could also hold references to
// unmanaged memory.
foreach ( var field in @class . Layout . Fields . Where ( f = > f . QualifiedType . Type . IsConstCharString ( ) ) )
{
var ptr = $"(({Helpers.InternalStruct}*){Helpers.InstanceIdentifier})->{field.Name}" ;
WriteLine ( $"if (__{field.Name}_OwnsNativeMemory)" ) ;
WriteLineIndent ( $"Marshal.FreeHGlobal({ptr});" ) ;
}
WriteLine ( "if ({0})" , Helpers . OwnsNativeInstanceIdentifier ) ;
WriteLine ( "if ({0})" , Helpers . OwnsNativeInstanceIdentifier ) ;
WriteLineIndent ( "Marshal.FreeHGlobal({0});" , Helpers . InstanceIdentifier ) ;
WriteLineIndent ( "Marshal.FreeHGlobal({0});" , Helpers . InstanceIdentifier ) ;
@ -2482,9 +2507,9 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
WriteLineIndent ( ": this()" ) ;
WriteLineIndent ( ": this()" ) ;
}
}
#endregion
#endregion
#region Methods / Functions
#region Methods / Functions
public void GenerateFunction ( Function function , string parentName )
public void GenerateFunction ( Function function , string parentName )
{
{
@ -2898,6 +2923,21 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
var classInternal = TypePrinter . PrintNative ( @class ) ;
var classInternal = TypePrinter . PrintNative ( @class ) ;
WriteLine ( $ @ "*(({classInternal}*) {Helpers.InstanceIdentifier}) = *(({
WriteLine ( $ @ "*(({classInternal}*) {Helpers.InstanceIdentifier}) = *(({
classInternal } * ) { method . Parameters [ 0 ] . Name } . { Helpers . InstanceIdentifier } ) ; ");
classInternal } * ) { method . Parameters [ 0 ] . Name } . { Helpers . InstanceIdentifier } ) ; ");
// Copy any string references owned by the source to the new instance so we
// don't have to ref count them.
foreach ( var field in @class . Fields . Where ( f = > f . QualifiedType . Type . IsConstCharString ( ) ) )
{
var prop = @class . Properties . Where ( p = > p . Field = = field ) . FirstOrDefault ( ) ;
// If there is no property or no setter then this instance can never own the native
// memory. Worry about the case where there's only a setter (write-only) when we
// understand the use case and how it can occur.
if ( prop ! = null & & prop . HasGetter & & prop . HasSetter )
{
WriteLine ( $"if ({method.Parameters[0].Name}.__{field.OriginalName}_OwnsNativeMemory)" ) ;
WriteLineIndent ( $@"this.{prop.Name} = {method.Parameters[0].Name}.{prop.Name};" ) ;
}
}
}
}
}
}
else
else
@ -3230,7 +3270,7 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
return TypePrinter . VisitParameters ( @params , true ) . Type ;
return TypePrinter . VisitParameters ( @params , true ) . Type ;
}
}
#endregion
#endregion
public override bool VisitTypedefNameDecl ( TypedefNameDecl typedef )
public override bool VisitTypedefNameDecl ( TypedefNameDecl typedef )
{
{