From 748b7b5d0336e1e9a8e76f88a952b63c814465a1 Mon Sep 17 00:00:00 2001 From: Joe Hull Date: Mon, 3 May 2021 01:26:34 -0700 Subject: [PATCH] Add a mechanism to selectively initialize allocated unmanaged memory in the default constructor. * feature: Add a mechanism to selectively initialize unmanaged memory in the default constructor. (C# Only at this point). * Use the global:: prefix when calling InitBlock --- src/Generator/Generators/CSharp/CSharpSources.cs | 3 +++ src/Generator/Options.cs | 12 ++++++++++++ tests/CSharp/CSharp.Gen.cs | 3 +++ tests/CSharp/CSharp.Tests.cs | 13 +++++++++++++ tests/CSharp/CSharp.h | 8 ++++++++ 5 files changed, 39 insertions(+) diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index ec779186..93a07c3a 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -2942,6 +2942,9 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty } else { + if (method.IsDefaultConstructor && Context.Options.ZeroAllocatedMemory(@class)) + WriteLine($"global::System.Runtime.CompilerServices.Unsafe.InitBlock((void*){Helpers.InstanceIdentifier}, 0, (uint)sizeof({@internal}));"); + if (!method.IsDefaultConstructor || @class.HasNonTrivialDefaultConstructor) GenerateInternalFunctionCall(method); } diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index b980916f..6ea8d067 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -119,6 +119,18 @@ namespace CppSharp /// public bool GenerateFinalizers; + /// + /// If returns true for a given , + /// unmanaged memory allocated in the class' default constructor will be initialized with + /// zeros. The default implementation always returns false. Currently, C# only. + /// + /// + /// If this option returns true, you must take a reference to the nuget package + /// System.Runtime.CompilerServices.Unsafe in the project(s) containing the generated file(s) to + /// resolve the Unsafe.InitBlock method. + /// + public Func ZeroAllocatedMemory = (@class) => false; + public string IncludePrefix; public Func GenerateName; diff --git a/tests/CSharp/CSharp.Gen.cs b/tests/CSharp/CSharp.Gen.cs index 5347bdde..19ace81e 100644 --- a/tests/CSharp/CSharp.Gen.cs +++ b/tests/CSharp/CSharp.Gen.cs @@ -65,6 +65,9 @@ namespace CppSharp.Tests Name = "MacroTest", Namespace = ctx.TranslationUnits.First(u => u.IsValid && !u.IsSystemHeader) }; + + // Preserve the original semantics except for our one test class. + driver.Options.ZeroAllocatedMemory = (@class) => @class.Name == "ClassZeroAllocatedMemoryTest"; } public override void Postprocess(Driver driver, ASTContext ctx) diff --git a/tests/CSharp/CSharp.Tests.cs b/tests/CSharp/CSharp.Tests.cs index 25d5adc7..7141037f 100644 --- a/tests/CSharp/CSharp.Tests.cs +++ b/tests/CSharp/CSharp.Tests.cs @@ -708,6 +708,19 @@ public unsafe class CSharpTests Assert.That(CSharp.HasFreeConstant.AnotherUnit.STD_STRING_CONSTANT, Is.EqualTo("test")); } + [Test] + public void TestZeroAllocatedMemoryOption() + { + // We've arranged in the generator for the ZeroAllocatedMemory option to return true for this one + // class. + var test = new ClassZeroAllocatedMemoryTest(); + Assert.That(test.P1, Is.EqualTo(0)); + Assert.That(test.p2.P2p1, Is.EqualTo(0)); + Assert.That(test.p2.P2p2, Is.EqualTo(0)); + Assert.That(test.P3, Is.EqualTo(false)); + Assert.That(test.P4, Is.EqualTo('\0')); + } + [Test] public void TestAlignment() { diff --git a/tests/CSharp/CSharp.h b/tests/CSharp/CSharp.h index 5c501628..4239a0f2 100644 --- a/tests/CSharp/CSharp.h +++ b/tests/CSharp/CSharp.h @@ -1487,6 +1487,14 @@ struct TestVariableWithoutType static constexpr auto variable = create(n...); }; +struct DLL_API ClassZeroAllocatedMemoryTest +{ + int p1; + struct { int p2p1; int p2p2; } p2; + bool p3; + char p4; +}; + struct DLL_API ConversionFunctions { ConversionFunctions();