From 9686187eb16e5e3f40a9abe54437dbb5fdf4c42f Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Mon, 16 Dec 2013 23:47:01 +0200 Subject: [PATCH] Wrapped properties of non-primitive value types as fields. Signed-off-by: Dimitar Dobrev --- src/AST/Property.cs | 13 +++++ .../Generators/CLI/CLIHeadersTemplate.cs | 49 ++++++++++--------- .../Generators/CLI/CLISourcesTemplate.cs | 10 ++-- .../Generators/CSharp/CSharpTextTemplate.cs | 36 ++++++++------ src/Generator/Passes/FieldToPropertyPass.cs | 9 +++- tests/Basic/Basic.Tests.cs | 7 +++ 6 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/AST/Property.cs b/src/AST/Property.cs index bad67004..3654ab94 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -104,5 +104,18 @@ namespace CppSharp.AST { return visitor.VisitProperty(this); } + + public bool IsBackedByValueClassField() + { + if (Field == null) + return false; + + Type type; + Field.Type.IsPointerTo(out type); + type = type ?? Field.Type; + + Class decl; + return type.IsTagDecl(out decl) && decl.IsValueType; + } } } \ No newline at end of file diff --git a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs index 5144026f..a6a8ca9a 100644 --- a/src/Generator/Generators/CLI/CLIHeadersTemplate.cs +++ b/src/Generator/Generators/CLI/CLIHeadersTemplate.cs @@ -396,7 +396,7 @@ namespace CppSharp.Generators.CLI public void GenerateClassFields(Class @class) { // Handle the case of struct (value-type) inheritance by adding the base - // fields to the managed value subtypes. + // properties to the managed value subtypes. foreach (var @base in @class.Bases) { if (!@base.IsClass) @@ -404,29 +404,34 @@ namespace CppSharp.Generators.CLI Class baseClass = @base.Class; if (!baseClass.IsValueType || baseClass.Ignore) - { - Log.EmitMessage("Ignored base class of value type '{0}'", - baseClass.Name); continue; - } GenerateClassFields(baseClass); } PushIndent(); - foreach (var field in @class.Fields) + // check for value types because some of the ignored fields may back properties; + // not the case for ref types because the NativePtr pattern is used there + foreach (var field in @class.Fields.Where(f => !f.Ignore || @class.IsValueType)) { - if (ASTUtils.CheckIgnoreField(field) && !@class.IsValueType) continue; - - GenerateDeclarationCommon(field); - if (@class.IsUnion) - WriteLine("[System::Runtime::InteropServices::FieldOffset({0})]", - field.Offset); - WriteLine("{0} {1};", field.Type, SafeIdentifier(field.Name)); + var property = @class.Properties.FirstOrDefault(p => p.Field == field); + if (property != null && !property.IsBackedByValueClassField()) + { + GenerateField(@class, field); + } } PopIndent(); } + private void GenerateField(Class @class, Field field) + { + GenerateDeclarationCommon(field); + if (@class.IsUnion) + WriteLine("[System::Runtime::InteropServices::FieldOffset({0})]", + field.Offset); + WriteLine("{0} {1};", field.Type, SafeIdentifier(field.Name)); + } + public void GenerateClassEvents(Class @class) { foreach (var @event in @class.Events) @@ -560,10 +565,10 @@ namespace CppSharp.Generators.CLI return false; } - public void GenerateClassProperties(Class @class, bool onlyFieldProperties = false) + public void GenerateClassProperties(Class @class) { // Handle the case of struct (value-type) inheritance by adding the base - // fields to the managed value subtypes. + // properties to the managed value subtypes. foreach (var @base in @class.Bases) { if (!@base.IsClass) @@ -571,19 +576,19 @@ namespace CppSharp.Generators.CLI Class baseClass = @base.Class; if (!baseClass.IsValueType || baseClass.Ignore) - { - Log.EmitMessage("Ignored base class of value type '{0}'", - baseClass.Name); continue; - } - GenerateClassProperties(baseClass, true); + GenerateClassProperties(baseClass); } PushIndent(); - foreach (var prop in @class.Properties) + foreach (var prop in @class.Properties.Where(prop => !prop.Ignore)) { - if (prop.Ignore || (onlyFieldProperties && prop.Field == null)) continue; + if (prop.IsBackedByValueClassField()) + { + GenerateField(@class, prop.Field); + continue; + } GenerateDeclarationCommon(prop); GenerateProperty(prop); diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 51b9e445..cc613699 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -191,19 +191,18 @@ namespace CppSharp.Generators.CLI PopBlock(); } - private void GenerateClassProperties(Class @class, Class realOwner, - bool onlyFieldProperties = false) + private void GenerateClassProperties(Class @class, Class realOwner) { if (@class.IsValueType) { foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) { - GenerateClassProperties(@base.Class, realOwner, true); + GenerateClassProperties(@base.Class, realOwner); } } foreach (var property in @class.Properties.Where( - p => !p.Ignore && (!onlyFieldProperties || p.Field != null))) + p => !p.Ignore && !p.IsBackedByValueClassField())) GenerateProperty(property, realOwner); } @@ -575,8 +574,7 @@ namespace CppSharp.Generators.CLI if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("{0} = {1};", - Generator.GeneratedIdentifier(property.Field.OriginalName), marshal.Context.Return); + WriteLine("{0} = {1};", property.Field.Name, marshal.Context.Return); } } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 906d78bb..7fd0268c 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -672,32 +672,32 @@ namespace CppSharp.Generators.CSharp } } - private void GenerateStructInternalMarshalingProperty(Property field, string marshalVar) + private void GenerateStructInternalMarshalingProperty(Property property, string marshalVar) { var marshalCtx = new CSharpMarshalContext(Driver) { - ArgName = field.Name, + ArgName = property.Name, }; var marshal = new CSharpMarshalManagedToNativePrinter(marshalCtx); - field.Visit(marshal); + property.Visit(marshal); Type type; Class @class; - var isRef = field.Type.IsPointerTo(out type) && + var isRef = property.Type.IsPointerTo(out type) && !(type.IsTagDecl(out @class) && @class.IsValueType) && !type.IsPrimitiveType(); if (isRef) { - WriteLine("if ({0} != null)", field.Name); + WriteLine("if ({0} != null)", property.Name); WriteStartBraceIndent(); } if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) WriteLine(marshal.Context.SupportBefore); - WriteLine("{0}.{1} = {2};", marshalVar, field.OriginalName, marshal.Context.Return); + WriteLine("{0}.{1} = {2};", marshalVar, property.OriginalName, marshal.Context.Return); if (isRef) WriteCloseBraceIndent(); @@ -797,7 +797,7 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } - private void GenerateClassField(Field field) + private void GenerateClassField(Field field, bool @public = false) { PushBlock(CSharpBlockKind.Field); @@ -807,7 +807,8 @@ namespace CppSharp.Generators.CSharp if (@class.IsUnion) WriteLine("[FieldOffset({0})]", field.Offset); - WriteLine("private {0} {1};", field.Type, SafeIdentifier(field.Name)); + WriteLine("{0} {1} {2};", @public ? "public" : "private", + field.Type, SafeIdentifier(field.Name)); PopBlock(NewLineKind.BeforeNextBlock); } @@ -913,6 +914,7 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); return; } + WriteStartBraceIndent(); var field = decl as Field; @@ -1006,6 +1008,7 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); return; } + NewLine(); WriteStartBraceIndent(); var field = decl as Field; @@ -1108,24 +1111,29 @@ namespace CppSharp.Generators.CSharp } } - private void GenerateClassProperties(Class @class, bool onlyFieldProperties = false) + private void GenerateClassProperties(Class @class) { if (@class.IsValueType) { foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) { - GenerateClassProperties(@base.Class, true); + GenerateClassProperties(@base.Class); } } - GenerateProperties(@class, onlyFieldProperties); + GenerateProperties(@class); } - private void GenerateProperties(Class @class, bool onlyFieldProperties = false) + private void GenerateProperties(Class @class) { - foreach (var prop in @class.Properties.Where( - p => !p.Ignore && (!onlyFieldProperties || p.Field != null))) + foreach (var prop in @class.Properties.Where(p => !p.Ignore)) { + if (prop.IsBackedByValueClassField()) + { + GenerateClassField(prop.Field, true); + continue; + } + PushBlock(CSharpBlockKind.Property); // If this is an indexer that returns an address use the real type diff --git a/src/Generator/Passes/FieldToPropertyPass.cs b/src/Generator/Passes/FieldToPropertyPass.cs index 66057cd1..76113aac 100644 --- a/src/Generator/Passes/FieldToPropertyPass.cs +++ b/src/Generator/Passes/FieldToPropertyPass.cs @@ -39,8 +39,13 @@ namespace CppSharp.Passes Access = field.Access, Field = field }; - field.Name = Generator.GeneratedIdentifier(field.Name); - @class.Properties.Add(prop); + + // do not rename value-class fields because they would be + // generated as fields later on even though they are wrapped by properties; + // that is, in turn, because it's cleaner to write + // the struct marshalling logic just for properties + if (!prop.IsBackedByValueClassField()) + field.Name = Generator.GeneratedIdentifier(field.Name); @class.Properties.Add(prop); diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index f6ee0a47..e5a613a0 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -173,5 +173,12 @@ public class BasicTests : GeneratorTestFixture Assert.That(nestedPublic.l, Is.EqualTo(5)); Assert.That(nestedPublic.g, Is.Not.EqualTo(0)); } + + public void TestPropertyChains() + { + var bar2 = new Bar2(); + bar2.pointerToStruct.A = 15; + Assert.That(bar2.pointerToStruct.A, Is.EqualTo(15)); + } } \ No newline at end of file