Skip to content

Commit f201b43

Browse files
committed
generate calls to cctor when passing paramters by value
1 parent 8e22170 commit f201b43

File tree

5 files changed

+90
-7
lines changed

5 files changed

+90
-7
lines changed

src/Generator/Generators/CSharp/CSharpMarshal.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -768,11 +768,35 @@ private void MarshalRefClass(Class @class)
768768
{
769769
if (Context.Parameter.IsIndirect)
770770
{
771-
Context.Before.WriteLine($"if (ReferenceEquals({Context.Parameter.Name}, null))");
772-
Context.Before.WriteLineIndent(
773-
$@"throw new global::System.ArgumentNullException(""{
774-
Context.Parameter.Name}"", ""Cannot be null because it is passed by value."");");
775-
Context.Return.Write(paramInstance);
771+
Method cctor = @class.HasNonTrivialCopyConstructor ? @class.Methods.First(c => c.IsCopyConstructor) : null;
772+
if (cctor != null && cctor.IsGenerated)
773+
{
774+
Context.Before.WriteLine($"if (ReferenceEquals({Context.Parameter.Name}, null))");
775+
Context.Before.WriteLineIndent(
776+
$@"throw new global::System.ArgumentNullException(""{
777+
Context.Parameter.Name}"", ""Cannot be null because it is passed by value."");");
778+
779+
var nativeClass = typePrinter.PrintNative(@class);
780+
var cctorName = CSharpSources.GetFunctionNativeIdentifier(Context.Context, cctor);
781+
Context.Before.WriteLine($"byte* __{Context.Parameter.Name}Memory = stackalloc byte[sizeof({nativeClass})];");
782+
Context.Before.WriteLine($"__IntPtr __{Context.Parameter.Name}Ptr = (__IntPtr)__{Context.Parameter.Name}Memory;");
783+
Context.Before.WriteLine($"{nativeClass}.{cctorName}(__{Context.Parameter.Name}Ptr, {Context.Parameter.Name}.__Instance);");
784+
Context.Return.Write($"__{Context.Parameter.Name}Ptr");
785+
786+
if (Context.Context.ParserOptions.IsItaniumLikeAbi && @class.HasNonTrivialDestructor)
787+
{
788+
Method dtor = @class.Destructors.FirstOrDefault();
789+
if (dtor != null)
790+
{
791+
// todo: virtual destructors?
792+
Context.Cleanup.WriteLine($"{nativeClass}.dtor(__{Context.Parameter.Name}Ptr);");
793+
}
794+
}
795+
}
796+
else
797+
{
798+
Context.Return.Write(paramInstance);
799+
}
776800
}
777801
else
778802
{

src/Generator/Generators/CSharp/CSharpSources.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,6 +3464,12 @@ public static string GetFunctionIdentifier(Function function)
34643464

34653465
public string GetFunctionNativeIdentifier(Function function,
34663466
bool isForDelegate = false)
3467+
{
3468+
return GetFunctionNativeIdentifier(Context, function, isForDelegate);
3469+
}
3470+
3471+
public static string GetFunctionNativeIdentifier(BindingContext context, Function function,
3472+
bool isForDelegate = false)
34673473
{
34683474
var identifier = new StringBuilder();
34693475

@@ -3494,12 +3500,12 @@ public string GetFunctionNativeIdentifier(Function function,
34943500
identifier.Append(Helpers.GetSuffixFor(specialization));
34953501

34963502
var internalParams = function.GatherInternalParams(
3497-
Context.ParserOptions.IsItaniumLikeAbi);
3503+
context.ParserOptions.IsItaniumLikeAbi);
34983504
var overloads = function.Namespace.GetOverloads(function)
34993505
.Where(f => (!f.Ignore ||
35003506
(f.OriginalFunction != null && !f.OriginalFunction.Ignore)) &&
35013507
(isForDelegate || internalParams.SequenceEqual(
3502-
f.GatherInternalParams(Context.ParserOptions.IsItaniumLikeAbi),
3508+
f.GatherInternalParams(context.ParserOptions.IsItaniumLikeAbi),
35033509
new MarshallingParamComparer()))).ToList();
35043510
var index = -1;
35053511
if (overloads.Count > 1)

tests/dotnet/CSharp/CSharp.Tests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,4 +1995,19 @@ public void TestPointerToClass()
19951995
Assert.IsTrue(CSharp.CSharp.PointerToClass.IsDefaultInstance);
19961996
Assert.IsTrue(CSharp.CSharp.PointerToClass.IsValid);
19971997
}
1998+
1999+
[Test]
2000+
public void TestObjectPassByValue1451()
2001+
{
2002+
using (var s = new Issue1451())
2003+
{
2004+
s.A = 500;
2005+
Assert.IsTrue(CSharp.CSharp.TestObjectPassByValue(s));
2006+
Assert.That(s.A, Is.EqualTo(500));
2007+
}
2008+
2009+
Assert.That(Issue1451.ConstructorCalls, Is.EqualTo(1));
2010+
Assert.That(Issue1451.CopyConstructorCalls, Is.EqualTo(1));
2011+
Assert.That(Issue1451.DestructorCalls, Is.EqualTo(2));
2012+
}
19982013
}

tests/dotnet/CSharp/CSharp.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,3 +1791,29 @@ bool PointerTester::IsValid()
17911791
}
17921792

17931793
PointerTester* PointerToClass = &internalPointerTesterInstance;
1794+
int Issue1451::constructorCalls = 0;
1795+
int Issue1451::destructorCalls = 0;
1796+
int Issue1451::copyConstructorCalls = 0;
1797+
1798+
Issue1451::Issue1451()
1799+
{
1800+
a = 0;
1801+
constructorCalls++;
1802+
}
1803+
1804+
Issue1451::Issue1451(const Issue1451& other)
1805+
{
1806+
a = other.a;
1807+
copyConstructorCalls++;
1808+
}
1809+
1810+
Issue1451::~Issue1451()
1811+
{
1812+
destructorCalls++;
1813+
}
1814+
1815+
bool TestObjectPassByValue(Issue1451 s)
1816+
{
1817+
s.a = 99999;
1818+
return true;
1819+
}

tests/dotnet/CSharp/CSharp.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,3 +1603,15 @@ class DLL_API PointerTester
16031603
};
16041604

16051605
DLL_API extern PointerTester* PointerToClass;
1606+
struct DLL_API Issue1451 {
1607+
int a;
1608+
static int constructorCalls;
1609+
static int destructorCalls;
1610+
static int copyConstructorCalls;
1611+
1612+
Issue1451();
1613+
~Issue1451();
1614+
Issue1451(const Issue1451& other);
1615+
};
1616+
1617+
bool DLL_API TestObjectPassByValue(Issue1451 s);

0 commit comments

Comments
 (0)