From 4c5d933cd2e74fe3da94d0f3c81f934e2ba25ff6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Wed, 11 Dec 2013 23:02:20 +0200 Subject: [PATCH] Added support for marshalling arrays of pointers to primitives. Signed-off-by: Dimitar Dobrev --- .../Generators/CSharp/CSharpMarshal.cs | 35 +++----- .../Generators/CSharp/CSharpTextTemplate.cs | 80 +++++++++++++++++-- .../Generators/CSharp/CSharpTypePrinter.cs | 7 ++ src/Generator/Passes/CheckIgnoredDecls.cs | 3 +- tests/CSharpTemp/CSharpTemp.Tests.cs | 11 +++ tests/CSharpTemp/CSharpTemp.h | 1 + 6 files changed, 103 insertions(+), 34 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 5710797f..42723af7 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -117,17 +117,12 @@ namespace CppSharp.Generators.CSharp case ArrayType.ArraySize.Constant: var supportBefore = Context.SupportBefore; string value = Generator.GeneratedIdentifier("value"); - supportBefore.WriteLine("{0}[] {1} = new {0}[{2}];", array.Type, value, array.Size); - string v = Generator.GeneratedIdentifier("v"); - supportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, v, value); + supportBefore.WriteLine("{0}[] {1} = null;", array.Type, value, array.Size); + supportBefore.WriteLine("if ({0} != null)", Context.ReturnVarName); supportBefore.WriteStartBraceIndent(); - string to = Generator.GeneratedIdentifier("to"); - supportBefore.WriteLine("{0}* {1} = {2};", array.Type, to, v); - string from = Generator.GeneratedIdentifier("from"); - supportBefore.WriteLine("{0}* {1} = {2};", - array.Type, from, Context.ReturnVarName); + supportBefore.WriteLine("{0} = new {1}[{2}];", value, array.Type, array.Size); supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); - supportBefore.WriteLineIndent("*{0}++ = *{1}++;", to, from); + supportBefore.WriteLineIndent("{0}[i] = {1}[i];", value, Context.ReturnVarName); supportBefore.WriteCloseBraceIndent(); Context.Return.Write(value); break; @@ -373,22 +368,12 @@ namespace CppSharp.Generators.CSharp { case ArrayType.ArraySize.Constant: var supportBefore = Context.SupportBefore; - supportBefore.WriteLine("if ({0}.Length != {1})", - Context.ArgName, array.Size); - supportBefore.WriteLineIndent( - "throw new ArgumentException(\"The value must be an array of size {0}.\");", - array.Size); - string v = Generator.GeneratedIdentifier("v"); - supportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, v, Context.ArgName); - supportBefore.WriteLineIndent("for (int i = 0; i < {0}; i++)", array.Size); - supportBefore.PushIndent(); - supportBefore.WriteLineIndent("*({0}->{1} + i) = *({2} + i);", - Generator.GeneratedIdentifier("ptr"), Context.ReturnVarName, v); - supportBefore.PopIndent(); - supportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, v, Context.ArgName); - supportBefore.PushIndent(); - supportBefore.Write("*"); - Context.Return.Write("*{0}", v); + supportBefore.WriteLine("if ({0} != null)", Context.ArgName); + supportBefore.WriteStartBraceIndent(); + supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); + supportBefore.WriteLineIndent("{0}[i] = {1}[i];", + Context.ReturnVarName, Context.ArgName); + supportBefore.WriteCloseBraceIndent(); break; default: Context.Return.Write("null"); diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index faee2b74..6c92d4c8 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -639,7 +639,10 @@ namespace CppSharp.Generators.CSharp if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("{0} = {1};", property.Name, marshal.Context.Return); + if (marshal.Context.Return.StringBuilder.Length > 0) + { + WriteLine("{0} = {1};", property.Name, marshal.Context.Return); + } } } @@ -648,6 +651,7 @@ namespace CppSharp.Generators.CSharp var marshalVar = Generator.GeneratedIdentifier("native"); WriteLine("var {0} = new {1}.Internal();", marshalVar, QualifiedIdentifier(@class)); + WriteLine("var {0} = &{1};", Generator.GeneratedIdentifier("ptr"), marshalVar); GenerateStructInternalMarshalingProperties(@class, marshalVar); WriteLine("return {0};", marshalVar); @@ -675,9 +679,12 @@ namespace CppSharp.Generators.CSharp private void GenerateStructInternalMarshalingProperty(Property property, string marshalVar) { + var nativeField = string.Format("{0}->{1}", + Generator.GeneratedIdentifier("ptr"), property.Field.OriginalName); var marshalCtx = new CSharpMarshalContext(Driver) { ArgName = property.Name, + ReturnVarName = nativeField, }; var marshal = new CSharpMarshalManagedToNativePrinter(marshalCtx); @@ -698,7 +705,11 @@ namespace CppSharp.Generators.CSharp if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) WriteLine(marshal.Context.SupportBefore); - WriteLine("{0}.{1} = {2};", marshalVar, property.OriginalName, marshal.Context.Return); + if (marshal.Context.Return.StringBuilder.Length > 0) + { + WriteLine("{0}.{1} = {2};", marshalVar, + property.OriginalName, marshal.Context.Return); + } if (isRef) WriteCloseBraceIndent(); @@ -902,6 +913,9 @@ namespace CppSharp.Generators.CSharp } else { + var field = decl as Field; + if (WrapSetterArrayOfPointers(decl.Name, field.Type)) + return; if (@class.IsValueType) { if (@class.IsUnion) @@ -918,28 +932,48 @@ namespace CppSharp.Generators.CSharp } WriteStartBraceIndent(); - var field = decl as Field; WriteLine("var {0} = (Internal*){1}.ToPointer();", Generator.GeneratedIdentifier("ptr"), Helpers.InstanceIdentifier); var marshal = new CSharpMarshalManagedToNativePrinter(ctx); - ctx.ReturnVarName = field.OriginalName; + ctx.ReturnVarName = string.Format("{0}->{1}", + Generator.GeneratedIdentifier("ptr"), Helpers.SafeIdentifier(field.OriginalName)); param.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - Write("{0}->{1} = {2}", Generator.GeneratedIdentifier("ptr"), - Helpers.SafeIdentifier(field.OriginalName), marshal.Context.Return); + if (marshal.Context.Return.StringBuilder.Length > 0) + { + WriteLine("{0} = {1};", ctx.ReturnVarName, marshal.Context.Return); + } - WriteLine(";"); WriteCloseBraceIndent(); } PopBlock(NewLineKind.BeforeNextBlock); } + private bool WrapSetterArrayOfPointers(string name, Type fieldType) + { + var arrayType = fieldType as ArrayType; + if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType()) + { + NewLine(); + WriteStartBraceIndent(); + WriteLine("{0} = value;", name); + WriteLine("if (!{0}{1})", name, "Initialised"); + WriteStartBraceIndent(); + WriteLine("{0}{1} = true;", name, "Initialised"); + WriteCloseBraceIndent(); + WriteCloseBraceIndent(); + PopBlock(NewLineKind.BeforeNextBlock); + return true; + } + return false; + } + private void GenerateIndexerSetter(QualifiedType returnType, Function function) { Type type; @@ -995,6 +1029,9 @@ namespace CppSharp.Generators.CSharp } else if (decl is Field) { + var field = decl as Field; + if (WrapGetterArrayOfPointers(decl.Name, field.Type)) + return; if (@class.IsValueType) { if (@class.IsUnion) @@ -1013,7 +1050,6 @@ namespace CppSharp.Generators.CSharp NewLine(); WriteStartBraceIndent(); - var field = decl as Field; WriteLine("var {0} = (Internal*){1}.ToPointer();", Generator.GeneratedIdentifier("ptr"), Helpers.InstanceIdentifier); @@ -1072,6 +1108,26 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } + private bool WrapGetterArrayOfPointers(string name, Type fieldType) + { + var arrayType = fieldType as ArrayType; + if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType()) + { + NewLine(); + WriteStartBraceIndent(); + WriteLine("if (!{0}{1})", name, "Initialised"); + WriteStartBraceIndent(); + WriteLine("{0} = null;", name); + WriteLine("{0}{1} = true;", name, "Initialised"); + WriteCloseBraceIndent(); + WriteLine("return {0};", name); + WriteCloseBraceIndent(); + PopBlock(NewLineKind.BeforeNextBlock); + return true; + } + return false; + } + public void GenerateClassMethods(Class @class) { var staticMethods = new List(); @@ -1144,6 +1200,14 @@ namespace CppSharp.Generators.CSharp if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) type = ((PointerType) prop.Type).Pointee; + ArrayType arrayType = prop.Type as ArrayType; + if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null) + { + GenerateClassField(prop.Field); + WriteLine("private bool {0};", + GeneratedIdentifier(string.Format("{0}Initialised", prop.Field.OriginalName))); + } + GenerateDeclarationCommon(prop); if (prop.ExplicitInterfaceImpl == null) { diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 9770efb1..f64e5aa5 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -113,6 +113,13 @@ namespace CppSharp.Generators.CSharp if (ContextKind == CSharpTypePrinterContextKind.Native && array.SizeType == ArrayType.ArraySize.Constant) { + if (array.Type.Desugar().IsPointerToPrimitiveType()) + { + return new CSharpTypePrinterResult + { + Type = string.Format("{0}*", array.Type.Visit(this, quals)) + }; + } return new CSharpTypePrinterResult() { Type = string.Format("fixed {0}", array.Type.Visit(this, quals)), diff --git a/src/Generator/Passes/CheckIgnoredDecls.cs b/src/Generator/Passes/CheckIgnoredDecls.cs index 7fa43044..6e696ce8 100644 --- a/src/Generator/Passes/CheckIgnoredDecls.cs +++ b/src/Generator/Passes/CheckIgnoredDecls.cs @@ -338,7 +338,8 @@ namespace CppSharp.Passes var arrayType = type as ArrayType; PrimitiveType primitive; if (arrayType != null && arrayType.SizeType == ArrayType.ArraySize.Constant && - !arrayType.Type.Desugar().IsPrimitiveType(out primitive)) + !arrayType.Type.Desugar().IsPrimitiveType(out primitive) && + !arrayType.Type.Desugar().IsPointerToPrimitiveType()) { msg = "unsupported"; return true; diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index 387f4494..b342413a 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -96,4 +96,15 @@ public class CSharpTempTests : GeneratorTestFixture dtors.Dispose(); Assert.AreEqual(0xcafe, CSharpTemp.TestDestructors.Marker); } + + [Test] + public unsafe void TestArrayOfPointersToPrimitives() + { + Bar bar = new Bar(); + void*[] array = new void*[1]; + int i = 5; + array[0] = &i; + bar.ArrayOfPrimitivePointers = array; + Assert.That(i, Is.EqualTo(*(int*) bar.ArrayOfPrimitivePointers[0])); + } } \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index f265a85b..7e187858 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -38,6 +38,7 @@ public: const Bar& operator*(int m); const Bar& operator++(); Bar operator++(int i); + void* arrayOfPrimitivePointers[1]; private: int index;