Browse Source

Wrapped properties of non-primitive value types as fields.

Signed-off-by: Dimitar Dobrev <dpldobrev@yahoo.com>
pull/151/head
Dimitar Dobrev 12 years ago
parent
commit
9686187eb1
  1. 13
      src/AST/Property.cs
  2. 49
      src/Generator/Generators/CLI/CLIHeadersTemplate.cs
  3. 10
      src/Generator/Generators/CLI/CLISourcesTemplate.cs
  4. 36
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  5. 9
      src/Generator/Passes/FieldToPropertyPass.cs
  6. 7
      tests/Basic/Basic.Tests.cs

13
src/AST/Property.cs

@ -104,5 +104,18 @@ namespace CppSharp.AST
{ {
return visitor.VisitProperty(this); 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;
}
} }
} }

49
src/Generator/Generators/CLI/CLIHeadersTemplate.cs

@ -396,7 +396,7 @@ namespace CppSharp.Generators.CLI
public void GenerateClassFields(Class @class) public void GenerateClassFields(Class @class)
{ {
// Handle the case of struct (value-type) inheritance by adding the base // 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) foreach (var @base in @class.Bases)
{ {
if (!@base.IsClass) if (!@base.IsClass)
@ -404,29 +404,34 @@ namespace CppSharp.Generators.CLI
Class baseClass = @base.Class; Class baseClass = @base.Class;
if (!baseClass.IsValueType || baseClass.Ignore) if (!baseClass.IsValueType || baseClass.Ignore)
{
Log.EmitMessage("Ignored base class of value type '{0}'",
baseClass.Name);
continue; continue;
}
GenerateClassFields(baseClass); GenerateClassFields(baseClass);
} }
PushIndent(); 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; var property = @class.Properties.FirstOrDefault(p => p.Field == field);
if (property != null && !property.IsBackedByValueClassField())
GenerateDeclarationCommon(field); {
if (@class.IsUnion) GenerateField(@class, field);
WriteLine("[System::Runtime::InteropServices::FieldOffset({0})]", }
field.Offset);
WriteLine("{0} {1};", field.Type, SafeIdentifier(field.Name));
} }
PopIndent(); 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) public void GenerateClassEvents(Class @class)
{ {
foreach (var @event in @class.Events) foreach (var @event in @class.Events)
@ -560,10 +565,10 @@ namespace CppSharp.Generators.CLI
return false; 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 // 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) foreach (var @base in @class.Bases)
{ {
if (!@base.IsClass) if (!@base.IsClass)
@ -571,19 +576,19 @@ namespace CppSharp.Generators.CLI
Class baseClass = @base.Class; Class baseClass = @base.Class;
if (!baseClass.IsValueType || baseClass.Ignore) if (!baseClass.IsValueType || baseClass.Ignore)
{
Log.EmitMessage("Ignored base class of value type '{0}'",
baseClass.Name);
continue; continue;
}
GenerateClassProperties(baseClass, true); GenerateClassProperties(baseClass);
} }
PushIndent(); 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); GenerateDeclarationCommon(prop);
GenerateProperty(prop); GenerateProperty(prop);

10
src/Generator/Generators/CLI/CLISourcesTemplate.cs

@ -191,19 +191,18 @@ namespace CppSharp.Generators.CLI
PopBlock(); PopBlock();
} }
private void GenerateClassProperties(Class @class, Class realOwner, private void GenerateClassProperties(Class @class, Class realOwner)
bool onlyFieldProperties = false)
{ {
if (@class.IsValueType) if (@class.IsValueType)
{ {
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) 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( foreach (var property in @class.Properties.Where(
p => !p.Ignore && (!onlyFieldProperties || p.Field != null))) p => !p.Ignore && !p.IsBackedByValueClassField()))
GenerateProperty(property, realOwner); GenerateProperty(property, realOwner);
} }
@ -575,8 +574,7 @@ namespace CppSharp.Generators.CLI
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore); Write(marshal.Context.SupportBefore);
WriteLine("{0} = {1};", WriteLine("{0} = {1};", property.Field.Name, marshal.Context.Return);
Generator.GeneratedIdentifier(property.Field.OriginalName), marshal.Context.Return);
} }
} }

36
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) var marshalCtx = new CSharpMarshalContext(Driver)
{ {
ArgName = field.Name, ArgName = property.Name,
}; };
var marshal = new CSharpMarshalManagedToNativePrinter(marshalCtx); var marshal = new CSharpMarshalManagedToNativePrinter(marshalCtx);
field.Visit(marshal); property.Visit(marshal);
Type type; Type type;
Class @class; Class @class;
var isRef = field.Type.IsPointerTo(out type) && var isRef = property.Type.IsPointerTo(out type) &&
!(type.IsTagDecl(out @class) && @class.IsValueType) && !(type.IsTagDecl(out @class) && @class.IsValueType) &&
!type.IsPrimitiveType(); !type.IsPrimitiveType();
if (isRef) if (isRef)
{ {
WriteLine("if ({0} != null)", field.Name); WriteLine("if ({0} != null)", property.Name);
WriteStartBraceIndent(); WriteStartBraceIndent();
} }
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
WriteLine(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) if (isRef)
WriteCloseBraceIndent(); WriteCloseBraceIndent();
@ -797,7 +797,7 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
} }
private void GenerateClassField(Field field) private void GenerateClassField(Field field, bool @public = false)
{ {
PushBlock(CSharpBlockKind.Field); PushBlock(CSharpBlockKind.Field);
@ -807,7 +807,8 @@ namespace CppSharp.Generators.CSharp
if (@class.IsUnion) if (@class.IsUnion)
WriteLine("[FieldOffset({0})]", field.Offset); 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); PopBlock(NewLineKind.BeforeNextBlock);
} }
@ -913,6 +914,7 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
return; return;
} }
WriteStartBraceIndent(); WriteStartBraceIndent();
var field = decl as Field; var field = decl as Field;
@ -1006,6 +1008,7 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
return; return;
} }
NewLine(); NewLine();
WriteStartBraceIndent(); WriteStartBraceIndent();
var field = decl as Field; 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) if (@class.IsValueType)
{ {
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) 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( foreach (var prop in @class.Properties.Where(p => !p.Ignore))
p => !p.Ignore && (!onlyFieldProperties || p.Field != null)))
{ {
if (prop.IsBackedByValueClassField())
{
GenerateClassField(prop.Field, true);
continue;
}
PushBlock(CSharpBlockKind.Property); PushBlock(CSharpBlockKind.Property);
// If this is an indexer that returns an address use the real type // If this is an indexer that returns an address use the real type

9
src/Generator/Passes/FieldToPropertyPass.cs

@ -39,8 +39,13 @@ namespace CppSharp.Passes
Access = field.Access, Access = field.Access,
Field = field 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); @class.Properties.Add(prop);

7
tests/Basic/Basic.Tests.cs

@ -173,5 +173,12 @@ public class BasicTests : GeneratorTestFixture
Assert.That(nestedPublic.l, Is.EqualTo(5)); Assert.That(nestedPublic.l, Is.EqualTo(5));
Assert.That(nestedPublic.g, Is.Not.EqualTo(0)); 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));
}
} }
Loading…
Cancel
Save