Skip to content

Commit d8b5372

Browse files
rokupstritao
authored andcommitted
Fix for #1043 (#1044)
Field property getter returns non-value types by reference instead of by copy. Fixes #1043 * Minor code clarity cleanup in GenerateFieldSetter. No behavior changed. * Fix incorrect code generated in some cases. * Test for fields getters returning references.
1 parent 892f264 commit d8b5372

File tree

5 files changed

+48
-11
lines changed

5 files changed

+48
-11
lines changed

src/Generator/Generators/CSharp/CSharpSources.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -906,13 +906,11 @@ private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldT
906906
else
907907
{
908908
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
909-
var printed = TypePrinter.PrintNative(@class);
910-
ctx.ReturnVarName = string.Format("{0}{1}{2}",
911-
@class.IsValueType
912-
? Helpers.InstanceField
913-
: $"(({printed}*) {Helpers.InstanceIdentifier})",
914-
@class.IsValueType ? "." : "->",
915-
SafeIdentifier(name));
909+
var identifier = SafeIdentifier(name);
910+
if (@class.IsValueType)
911+
ctx.ReturnVarName = $"{Helpers.InstanceField}.{identifier}";
912+
else
913+
ctx.ReturnVarName = $"(({TypePrinter.PrintNative(@class)}*){Helpers.InstanceIdentifier})->{identifier}";
916914
}
917915
param.Visit(marshal);
918916

@@ -1174,13 +1172,24 @@ private static Property GetActualProperty(Property property, Class c)
11741172
private void GenerateFieldGetter(Field field, Class @class, QualifiedType returnType)
11751173
{
11761174
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
1175+
String returnVar;
1176+
if (@class.IsValueType)
1177+
returnVar = $@"{Helpers.InstanceField}.{SafeIdentifier(name)}";
1178+
else
1179+
{
1180+
returnVar = $@"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})->{SafeIdentifier(name)}";
1181+
// Class field getter should return a reference object instead of a copy. Wrapping `returnVar` in
1182+
// IntPtr ensures that non-copying object constructor is invoked.
1183+
Class typeClass;
1184+
if (field.Type.TryGetClass(out typeClass) && !typeClass.IsValueType)
1185+
returnVar = $"new {CSharpTypePrinter.IntPtrType}(&{returnVar})";
1186+
}
1187+
11771188
var ctx = new CSharpMarshalContext(Context)
11781189
{
11791190
ArgName = field.Name,
11801191
Declaration = field,
1181-
ReturnVarName = $@"{(@class.IsValueType ? Helpers.InstanceField :
1182-
$"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})")}{
1183-
(@class.IsValueType ? "." : "->")}{SafeIdentifier(name)}",
1192+
ReturnVarName = returnVar,
11841193
ReturnType = returnType
11851194
};
11861195
ctx.PushMarshalKind(MarshalKind.NativeField);

src/Generator/Generators/CSharp/CSharpSourcesExtensions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,14 @@ private static void ThrowException(CSharpSources gen, Class @class)
132132
s.Arguments.Select(typePrinter.VisitTemplateArgument))}>"));
133133
var typeArguments = string.Join(", ", @class.TemplateParameters.Select(p => p.Name));
134134
var managedTypes = string.Join(", ", @class.TemplateParameters.Select(p => $"typeof({p.Name}).FullName"));
135+
136+
if (managedTypes.Length > 0)
137+
managedTypes = $@"string.Join("", "", new[] {{ {managedTypes} }})";
138+
else
139+
managedTypes = "\"\"";
140+
135141
gen.WriteLine($"throw new ArgumentOutOfRangeException(\"{typeArguments}\", "
136-
+ $@"string.Join("", "", new[] {{ {managedTypes} }}), "
142+
+ $@"{managedTypes}, "
137143
+ $"\"{@class.Visit(typePrinter)} maps a C++ template class and therefore it only supports a limited set of types and their subclasses: {supportedTypes}.\");");
138144
}
139145
}

tests/Common/Common.Tests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,15 @@ public unsafe void TestSingleArgumentCtorToCastOperator()
525525
Assert.AreEqual(classB.Value, classC.Value);
526526
}
527527

528+
[Test]
529+
public unsafe void TestFieldRef()
530+
{
531+
var classD = new ClassD(10);
532+
var fieldRef = classD.Field;
533+
fieldRef.Value = 20;
534+
Assert.AreEqual(20, classD.Field.Value);
535+
}
536+
528537
[Test]
529538
public unsafe void TestDecltype()
530539
{

tests/Common/Common.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,11 @@ ClassC::ClassC(const ClassB &x)
469469
Value = x.Value;
470470
}
471471

472+
ClassD::ClassD(int value)
473+
: Field(value)
474+
{
475+
}
476+
472477
void DelegateNamespace::Nested::f1(void (*)())
473478
{
474479
}

tests/Common/Common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,14 @@ class DLL_API ClassC
771771
int Value;
772772
};
773773

774+
class DLL_API ClassD
775+
{
776+
public:
777+
ClassD(int value);
778+
// Accessing this field should return reference, not a copy.
779+
ClassA Field;
780+
};
781+
774782
// Test decltype
775783
int Expr = 0;
776784
DLL_API decltype(Expr) TestDecltype()

0 commit comments

Comments
 (0)