From 0f76dc0090fd7db40494384dbd987a4fe5f847ce Mon Sep 17 00:00:00 2001 From: triton Date: Wed, 22 Jan 2014 19:49:42 +0000 Subject: [PATCH] Added experimental destructors/finalizers support. This has exposed some underlying bugs on some pieces of generated code, so I've put it under an option temporarily. Fixes #148. --- .../Generators/CLI/CLIHeadersTemplate.cs | 54 +++++++------ .../Generators/CLI/CLISourcesTemplate.cs | 80 ++++++++++++++----- .../Generators/CLI/CLITextTemplate.cs | 2 + .../Generators/CSharp/CSharpTextTemplate.cs | 40 +++++++--- src/Generator/Options.cs | 7 ++ tests/Basic/Basic.h | 5 ++ 6 files changed, 134 insertions(+), 54 deletions(-) diff --git a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs index a6a8ca9a..b7bff611 100644 --- a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs +++ b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using CppSharp.AST; +using CppSharp.Generators.CSharp; using CppSharp.Types; namespace CppSharp.Generators.CLI @@ -252,10 +253,8 @@ namespace CppSharp.Generators.CLI var nativeType = string.Format("::{0}*", @class.QualifiedOriginalName); - if (@class.IsRefType) - { + if (CSharpTextTemplate.ShouldGenerateClassNativeField(@class)) GenerateClassNativeField(@class, nativeType); - } GenerateClassConstructors(@class, nativeType); @@ -279,23 +278,8 @@ namespace CppSharp.Generators.CLI WriteLine("};"); } - internal static bool HasRefBase(Class @class) - { - Class baseClass = null; - - if (@class.HasBaseClass) - baseClass = @class.Bases[0].Class; - - var hasRefBase = baseClass != null && baseClass.IsRefType - && !baseClass.Ignore; - - return hasRefBase; - } - public void GenerateClassNativeField(Class @class, string nativeType) { - if (HasRefBase(@class)) return; - WriteLineIndent("property {0} NativePtr;", nativeType); PushIndent(); @@ -380,19 +364,41 @@ namespace CppSharp.Generators.CLI foreach (var ctor in @class.Constructors) { - if (ctor.IsCopyConstructor || ctor.IsMoveConstructor) - continue; - - // Default constructors are not supported in .NET value types. - if (ctor.Parameters.Count == 0 && @class.IsValueType) + if (ASTUtils.CheckIgnoreMethod(ctor)) continue; GenerateMethod(ctor); } + if (@class.IsRefType) + { + GenerateClassDestructor(@class); + GenerateClassFinalizer(@class); + } + PopIndent(); } + private void GenerateClassDestructor(Class @class) + { + if (!Options.GenerateFinalizers) + return; + + PushBlock(CLIBlockKind.Destructor); + WriteLine("~{0}();", @class.Name); + PopBlock(NewLineKind.BeforeNextBlock); + } + + private void GenerateClassFinalizer(Class @class) + { + if (!Options.GenerateFinalizers) + return; + + PushBlock(CLIBlockKind.Finalizer); + WriteLine("!{0}();", @class.Name); + PopBlock(NewLineKind.BeforeNextBlock); + } + public void GenerateClassFields(Class @class) { // Handle the case of struct (value-type) inheritance by adding the base @@ -552,7 +558,7 @@ namespace CppSharp.Generators.CLI return true; } - if (HasRefBase(@class)) + if (CSharpTextTemplate.HasRefBase(@class)) Write(" : {0}", QualifiedIdentifier(@class.Bases[0].Class)); else if (@class.IsRefType) Write(" : ICppInstance"); diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index cc613699..e6caf952 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Linq; using CppSharp.AST; +using CppSharp.Generators.CSharp; using CppSharp.Types; using Type = CppSharp.AST.Type; @@ -134,6 +135,12 @@ namespace CppSharp.Generators.CLI GenerateClassConstructor(@class, isIntPtr: false); GenerateClassConstructor(@class, isIntPtr: true); + if (@class.IsRefType) + { + GenerateClassDestructor(@class); + GenerateClassFinalizer(@class); + } + foreach (var method in @class.Methods) { if (ASTUtils.CheckIgnoreMethod(method)) @@ -142,27 +149,24 @@ namespace CppSharp.Generators.CLI GenerateMethod(method, @class); } - if (@class.IsRefType) + if (CSharpTextTemplate.ShouldGenerateClassNativeField(@class)) { - if (!CLIHeadersTemplate.HasRefBase(@class)) - { - PushBlock(CLIBlockKind.Method); - WriteLine("System::IntPtr {0}::Instance::get()", - QualifiedIdentifier(@class)); - WriteStartBraceIndent(); - WriteLine("return System::IntPtr(NativePtr);"); - WriteCloseBraceIndent(); - PopBlock(NewLineKind.BeforeNextBlock); - - PushBlock(CLIBlockKind.Method); - WriteLine("void {0}::Instance::set(System::IntPtr object)", - QualifiedIdentifier(@class)); - WriteStartBraceIndent(); - var nativeType = string.Format("::{0}*", @class.QualifiedOriginalName); - WriteLine("NativePtr = ({0})object.ToPointer();", nativeType); - WriteCloseBraceIndent(); - PopBlock(NewLineKind.BeforeNextBlock); - } + PushBlock(CLIBlockKind.Method); + WriteLine("System::IntPtr {0}::Instance::get()", + QualifiedIdentifier(@class)); + WriteStartBraceIndent(); + WriteLine("return System::IntPtr(NativePtr);"); + WriteCloseBraceIndent(); + PopBlock(NewLineKind.BeforeNextBlock); + + PushBlock(CLIBlockKind.Method); + WriteLine("void {0}::Instance::set(System::IntPtr object)", + QualifiedIdentifier(@class)); + WriteStartBraceIndent(); + var nativeType = string.Format("::{0}*", @class.QualifiedOriginalName); + WriteLine("NativePtr = ({0})object.ToPointer();", nativeType); + WriteCloseBraceIndent(); + PopBlock(NewLineKind.BeforeNextBlock); } GenerateClassProperties(@class, @class); @@ -206,6 +210,42 @@ namespace CppSharp.Generators.CLI GenerateProperty(property, realOwner); } + private void GenerateClassDestructor(Class @class) + { + if (!Options.GenerateFinalizers) + return; + + PushBlock(CLIBlockKind.Destructor); + + WriteLine("{0}::~{1}()", QualifiedIdentifier(@class), @class.Name); + WriteStartBraceIndent(); + + if (CSharpTextTemplate.ShouldGenerateClassNativeField(@class)) + WriteLine("delete NativePtr;"); + + WriteCloseBraceIndent(); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + private void GenerateClassFinalizer(Class @class) + { + if (!Options.GenerateFinalizers) + return; + + PushBlock(CLIBlockKind.Finalizer); + + WriteLine("{0}::!{1}()", QualifiedIdentifier(@class), @class.Name); + WriteStartBraceIndent(); + + if (CSharpTextTemplate.ShouldGenerateClassNativeField(@class)) + WriteLine("delete NativePtr;"); + + WriteCloseBraceIndent(); + + PopBlock(NewLineKind.BeforeNextBlock); + } + private void GenerateFunctionTemplate(FunctionTemplate template) { var printer = TypePrinter; diff --git a/src/Generator/Generators/CLI/CLITextTemplate.cs b/src/Generator/Generators/CLI/CLITextTemplate.cs index 1a98aa9e..e0c5c35f 100644 --- a/src/Generator/Generators/CLI/CLITextTemplate.cs +++ b/src/Generator/Generators/CLI/CLITextTemplate.cs @@ -43,6 +43,8 @@ namespace CppSharp.Generators.CLI public const int Typedef = BlockKind.LAST + 14; public const int Variable = BlockKind.LAST + 15; public const int Template = BlockKind.LAST + 16; + public static int Destructor = BlockKind.LAST + 17; + public static int Finalizer = BlockKind.LAST + 18; } /// diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 7fd0268c..69246b7a 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -96,6 +96,7 @@ namespace CppSharp.Generators.CSharp public const int VTableDelegate = FIRST + 16; public const int Region = FIRST + 17; public const int Interface = FIRST + 18; + public const int Finalizer = FIRST + 19; } public class CSharpTextTemplate : Template @@ -703,21 +704,22 @@ namespace CppSharp.Generators.CSharp WriteCloseBraceIndent(); } - public bool ShouldGenerateClassNativeField(Class @class) + public static bool HasRefBase(Class @class) { - if (!@class.IsRefType) - return false; - Class baseClass = null; if (@class.HasBaseClass) baseClass = @class.Bases[0].Class; - var hasRefBase = baseClass != null && baseClass.IsRefType && !baseClass.Ignore; + var hasRefBase = baseClass != null && baseClass.IsRefType + && !baseClass.Ignore; - var hasIgnoredBase = baseClass != null && baseClass.Ignore; + return hasRefBase; + } - return !@class.HasBase || !hasRefBase || hasIgnoredBase; + public static bool ShouldGenerateClassNativeField(Class @class) + { + return @class.IsRefType && (!@class.HasBase || !HasRefBase(@class)); } public void GenerateClassProlog(Class @class) @@ -1657,7 +1659,25 @@ namespace CppSharp.Generators.CSharp } if (@class.IsRefType) + { + GenerateClassFinalizer(@class); GenerateDisposeMethods(@class); + } + } + + private void GenerateClassFinalizer(Class @class) + { + if (!Options.GenerateFinalizers) + return; + + PushBlock(CSharpBlockKind.Finalizer); + + WriteLine("~{0}()", @class.Name); + WriteStartBraceIndent(); + WriteLine("Dispose(false);"); + WriteCloseBraceIndent(); + + PopBlock(NewLineKind.BeforeNextBlock); } private void GenerateDisposeMethods(Class @class) @@ -1682,12 +1702,12 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Method); if (@class.IsValueType) { - this.Write("private "); + Write("private "); } else { - this.Write("protected "); - this.Write(hasBaseClass ? "override " : "virtual "); + Write("protected "); + Write(hasBaseClass ? "override " : "virtual "); } WriteLine("void Dispose(bool disposing)"); diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index d7046774..6a91d9fb 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -89,6 +89,13 @@ namespace CppSharp public bool GenerateInterfacesForMultipleInheritance; public bool GenerateProperties; public bool GenerateInternalImports; + + /// + /// Enable this option to enable generation of finalizers. + /// Works in both CLI and C# backends. + /// + public bool GenerateFinalizers; + public string IncludePrefix; public bool WriteOnlyWhenChanged; public Func GenerateName; diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index f09a6896..10ee3529 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -244,4 +244,9 @@ struct DLL_API TestMemoryLeaks TestMemoryLeaks(const char* name) {} }; +// Tests that finalizers are generated +/* CLI: ~TestFinalizers() */ +struct DLL_API TestFinalizers +{ +};