Browse Source

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.
pull/1135/head
Rokas Kupstys 8 years ago committed by João Matos
parent
commit
d8b53721ef
  1. 29
      src/Generator/Generators/CSharp/CSharpSources.cs
  2. 8
      src/Generator/Generators/CSharp/CSharpSourcesExtensions.cs
  3. 9
      tests/Common/Common.Tests.cs
  4. 5
      tests/Common/Common.cpp
  5. 8
      tests/Common/Common.h

29
src/Generator/Generators/CSharp/CSharpSources.cs

@ -906,13 +906,11 @@ namespace CppSharp.Generators.CSharp
else else
{ {
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name; var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
var printed = TypePrinter.PrintNative(@class); var identifier = SafeIdentifier(name);
ctx.ReturnVarName = string.Format("{0}{1}{2}", if (@class.IsValueType)
@class.IsValueType ctx.ReturnVarName = $"{Helpers.InstanceField}.{identifier}";
? Helpers.InstanceField else
: $"(({printed}*) {Helpers.InstanceIdentifier})", ctx.ReturnVarName = $"(({TypePrinter.PrintNative(@class)}*){Helpers.InstanceIdentifier})->{identifier}";
@class.IsValueType ? "." : "->",
SafeIdentifier(name));
} }
param.Visit(marshal); param.Visit(marshal);
@ -1174,13 +1172,24 @@ namespace CppSharp.Generators.CSharp
private void GenerateFieldGetter(Field field, Class @class, QualifiedType returnType) private void GenerateFieldGetter(Field field, Class @class, QualifiedType returnType)
{ {
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name; var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
String returnVar;
if (@class.IsValueType)
returnVar = $@"{Helpers.InstanceField}.{SafeIdentifier(name)}";
else
{
returnVar = $@"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})->{SafeIdentifier(name)}";
// Class field getter should return a reference object instead of a copy. Wrapping `returnVar` in
// IntPtr ensures that non-copying object constructor is invoked.
Class typeClass;
if (field.Type.TryGetClass(out typeClass) && !typeClass.IsValueType)
returnVar = $"new {CSharpTypePrinter.IntPtrType}(&{returnVar})";
}
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context)
{ {
ArgName = field.Name, ArgName = field.Name,
Declaration = field, Declaration = field,
ReturnVarName = $@"{(@class.IsValueType ? Helpers.InstanceField : ReturnVarName = returnVar,
$"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})")}{
(@class.IsValueType ? "." : "->")}{SafeIdentifier(name)}",
ReturnType = returnType ReturnType = returnType
}; };
ctx.PushMarshalKind(MarshalKind.NativeField); ctx.PushMarshalKind(MarshalKind.NativeField);

8
src/Generator/Generators/CSharp/CSharpSourcesExtensions.cs

@ -132,8 +132,14 @@ namespace CppSharp.Generators.CSharp
s.Arguments.Select(typePrinter.VisitTemplateArgument))}>")); s.Arguments.Select(typePrinter.VisitTemplateArgument))}>"));
var typeArguments = string.Join(", ", @class.TemplateParameters.Select(p => p.Name)); var typeArguments = string.Join(", ", @class.TemplateParameters.Select(p => p.Name));
var managedTypes = string.Join(", ", @class.TemplateParameters.Select(p => $"typeof({p.Name}).FullName")); var managedTypes = string.Join(", ", @class.TemplateParameters.Select(p => $"typeof({p.Name}).FullName"));
if (managedTypes.Length > 0)
managedTypes = $@"string.Join("", "", new[] {{ {managedTypes} }})";
else
managedTypes = "\"\"";
gen.WriteLine($"throw new ArgumentOutOfRangeException(\"{typeArguments}\", " gen.WriteLine($"throw new ArgumentOutOfRangeException(\"{typeArguments}\", "
+ $@"string.Join("", "", new[] {{ {managedTypes} }}), " + $@"{managedTypes}, "
+ $"\"{@class.Visit(typePrinter)} maps a C++ template class and therefore it only supports a limited set of types and their subclasses: {supportedTypes}.\");"); + $"\"{@class.Visit(typePrinter)} maps a C++ template class and therefore it only supports a limited set of types and their subclasses: {supportedTypes}.\");");
} }
} }

9
tests/Common/Common.Tests.cs

@ -525,6 +525,15 @@ public class CommonTests : GeneratorTestFixture
Assert.AreEqual(classB.Value, classC.Value); Assert.AreEqual(classB.Value, classC.Value);
} }
[Test]
public unsafe void TestFieldRef()
{
var classD = new ClassD(10);
var fieldRef = classD.Field;
fieldRef.Value = 20;
Assert.AreEqual(20, classD.Field.Value);
}
[Test] [Test]
public unsafe void TestDecltype() public unsafe void TestDecltype()
{ {

5
tests/Common/Common.cpp

@ -469,6 +469,11 @@ ClassC::ClassC(const ClassB &x)
Value = x.Value; Value = x.Value;
} }
ClassD::ClassD(int value)
: Field(value)
{
}
void DelegateNamespace::Nested::f1(void (*)()) void DelegateNamespace::Nested::f1(void (*)())
{ {
} }

8
tests/Common/Common.h

@ -771,6 +771,14 @@ public:
int Value; int Value;
}; };
class DLL_API ClassD
{
public:
ClassD(int value);
// Accessing this field should return reference, not a copy.
ClassA Field;
};
// Test decltype // Test decltype
int Expr = 0; int Expr = 0;
DLL_API decltype(Expr) TestDecltype() DLL_API decltype(Expr) TestDecltype()

Loading…
Cancel
Save