From c1be105d466687d295f23ced307f804c17c886c2 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Tue, 19 Jul 2016 14:20:31 +0300 Subject: [PATCH] Added marshalling of fixed char arrays. Signed-off-by: Dimitar Dobrev --- .../Generators/CLI/CLISourcesTemplate.cs | 3 +- .../Generators/CSharp/CSharpMarshal.cs | 65 +++++++++++++++---- .../Generators/CSharp/CSharpTextTemplate.cs | 24 +------ tests/Common/Common.Tests.cs | 14 +++- tests/Common/Common.h | 1 + 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 864afa3f..752e90ec 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -1101,8 +1101,9 @@ namespace CppSharp.Generators.CLI p => p.Type.IsPrimitiveType(PrimitiveType.Char))) { WriteLine("if ({0} < System::Char::MinValue || {0} > System::SByte::MaxValue)", param.Name); + // C++/CLI can actually handle char -> sbyte in all case, this is for compatibility with the C# generator WriteLineIndent( - "throw gcnew System::ArgumentException(\"{0} must be in the range {1} - {2}.\");", + "throw gcnew System::OverflowException(\"{0} must be in the range {1} - {2}.\");", param.Name, (int) char.MinValue, sbyte.MaxValue); } } diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 58b5533f..9b238101 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -106,12 +106,27 @@ namespace CppSharp.Generators.CSharp value, Context.ReturnVarName); else { + var arrayType = array.Type.Desugar(); Class @class; - if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) - supportBefore.WriteLineIndent("{0}[i] = {1}.{2}(*(({1}.Internal*)&({3}[i * sizeof({1}.Internal)])));", + if (arrayType.TryGetClass(out @class) && @class.IsRefType) + supportBefore.WriteLineIndent( + "{0}[i] = {1}.{2}(*(({1}.Internal*)&({3}[i * sizeof({1}.Internal)])));", value, array.Type, Helpers.CreateInstanceIdentifier, Context.ReturnVarName); else - supportBefore.WriteLineIndent("{0}[i] = {1}[i];", value, Context.ReturnVarName); + { + if (arrayType.IsPrimitiveType(PrimitiveType.Char) && + Context.Driver.Options.MarshalCharAsManagedChar) + { + supportBefore.WriteLineIndent( + "{0}[i] = global::System.Convert.ToChar({1}[i]);", + value, Context.ReturnVarName); + } + else + { + supportBefore.WriteLineIndent("{0}[i] = {1}[i];", + value, Context.ReturnVarName); + } + } } supportBefore.WriteCloseBraceIndent(); Context.Return.Write(value); @@ -203,7 +218,11 @@ namespace CppSharp.Generators.CSharp case PrimitiveType.Char: // returned structs must be blittable and char isn't if (Context.Driver.Options.MarshalCharAsManagedChar) - Context.Return.Write("(char) "); + { + Context.Return.Write("global::System.Convert.ToChar({0})", + Context.ReturnVarName); + return true; + } goto default; case PrimitiveType.Char16: return false; @@ -393,10 +412,12 @@ namespace CppSharp.Generators.CSharp case ArrayType.ArraySize.Constant: if (string.IsNullOrEmpty(Context.ReturnVarName)) { - Context.SupportBefore.WriteLine("if ({0} == null || {0}.Length != {1})", Context.Parameter.Name, array.Size); + Context.SupportBefore.WriteLine("if ({0} == null || {0}.Length != {1})", + Context.Parameter.Name, array.Size); ThrowArgumentOutOfRangeException(); const string ptr = "__ptr"; - Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, ptr, Context.Parameter.Name); + Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})", + array.Type, ptr, Context.Parameter.Name); Context.SupportBefore.WriteStartBraceIndent(); Context.Return.Write("new global::System.IntPtr({0})", ptr); Context.HasFixedBlock = true; @@ -407,6 +428,7 @@ namespace CppSharp.Generators.CSharp supportBefore.WriteLine("if ({0} != null)", Context.ArgName); supportBefore.WriteStartBraceIndent(); Class @class; + var arrayType = array.Type.Desugar(); if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) { supportBefore.WriteLine("if (value.Length != {0})", array.Size); @@ -414,15 +436,30 @@ namespace CppSharp.Generators.CSharp } supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); if (@class != null && @class.IsRefType) + { supportBefore.WriteLineIndent( "{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);", Context.ReturnVarName, Context.ArgName, array.Type); + } else - supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};", - Context.ReturnVarName, Context.ArgName, - array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) - ? ".ToPointer()" - : string.Empty); + { + if (arrayType.IsPrimitiveType(PrimitiveType.Char) && + Context.Driver.Options.MarshalCharAsManagedChar) + { + supportBefore.WriteLineIndent( + "{0}[i] = global::System.Convert.ToSByte({1}[i]);", + Context.ReturnVarName, Context.ArgName); + } + else + { + supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};", + Context.ReturnVarName, + Context.ArgName, + array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) + ? ".ToPointer()" + : string.Empty); + } + } supportBefore.WriteCloseBraceIndent(); } break; @@ -578,7 +615,11 @@ namespace CppSharp.Generators.CSharp case PrimitiveType.Char: // returned structs must be blittable and char isn't if (Context.Driver.Options.MarshalCharAsManagedChar) - Context.Return.Write("(sbyte) "); + { + Context.Return.Write("global::System.Convert.ToSByte({0})", + Context.Parameter.Name); + return true; + } goto default; case PrimitiveType.Char16: return false; diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 560c2463..136cedbc 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -993,8 +993,7 @@ namespace CppSharp.Generators.CSharp private string HandleValueArray(ArrayType arrayType, Field field) { var originalType = new PointerType(new QualifiedType(arrayType.Type)); - var arrPtrIden = Generator.GeneratedIdentifier("arrPtr"); - var arrPtr = arrPtrIden; + var arrPtr = Generator.GeneratedIdentifier("arrPtr"); var finalElementType = (arrayType.Type.GetFinalPointee() ?? arrayType.Type); var isChar = finalElementType.IsPrimitiveType(PrimitiveType.Char); @@ -1004,7 +1003,6 @@ namespace CppSharp.Generators.CSharp var typePrinter = new CSharpTypePrinter(Driver); typePrinter.PushContext(CSharpTypePrinterContextKind.Native); type = originalType.Visit(typePrinter).Type; - arrPtrIden = Generator.GeneratedIdentifier(arrPtrIden); } else { @@ -1014,10 +1012,8 @@ namespace CppSharp.Generators.CSharp var name = ((Class) field.Namespace).Layout.Fields.First( f => f.FieldPtr == field.OriginalPtr).Name; WriteLine(string.Format("fixed ({0} {1} = {2}.{3})", - type, arrPtrIden, Helpers.InstanceField, Helpers.SafeIdentifier(name))); + type, arrPtr, Helpers.InstanceField, Helpers.SafeIdentifier(name))); WriteStartBraceIndent(); - if (Driver.Options.MarshalCharAsManagedChar && isChar) - WriteLine("var {0} = ({1}) {2};", arrPtr, originalType, arrPtrIden); return arrPtr; } @@ -2414,21 +2410,6 @@ namespace CppSharp.Generators.CSharp } } - private void CheckArgumentRange(Function method) - { - if (Driver.Options.MarshalCharAsManagedChar) - { - foreach (var param in method.Parameters.Where( - p => p.Type.IsPrimitiveType(PrimitiveType.Char))) - { - WriteLine("if ({0} < char.MinValue || {0} > sbyte.MaxValue)", param.Name); - WriteLineIndent( - "throw new global::System.ArgumentException(\"{0} must be in the range {1} - {2}.\");", - param.Name, (int) char.MinValue, sbyte.MaxValue); - } - } - } - private static AccessSpecifier GetValidMethodAccess(Method method) { if (!method.IsOverride) @@ -2601,7 +2582,6 @@ namespace CppSharp.Generators.CSharp !templateSpecialization.IsSupportedStdType() ? (templateSpecialization.Namespace.OriginalName + '.') : string.Empty; - CheckArgumentRange(function); var functionName = string.Format("{0}Internal.{1}", @namespace, GetFunctionNativeIdentifier(function.OriginalFunction ?? function)); GenerateFunctionCall(functionName, parameters, function, returnType); diff --git a/tests/Common/Common.Tests.cs b/tests/Common/Common.Tests.cs index 76b4775b..43b86ddf 100644 --- a/tests/Common/Common.Tests.cs +++ b/tests/Common/Common.Tests.cs @@ -337,7 +337,7 @@ public class CommonTests : GeneratorTestFixture Foo2 foo2 = new Foo2(); for (char c = char.MinValue; c <= sbyte.MaxValue; c++) Assert.That(foo2.testCharMarshalling(c), Is.EqualTo(c)); - Assert.Catch(() => foo2.testCharMarshalling('ж')); + Assert.Catch(() => foo2.testCharMarshalling('ж')); } [Test] @@ -623,6 +623,18 @@ public class CommonTests : GeneratorTestFixture Assert.IsTrue(NonTrivialDtor.DtorCalled); } + [Test] + public void TestFixedCharArray() + { + using (var foo = new Foo()) + { + foo.fixedCharArray = new char[] { 'a', 'b', 'c' }; + Assert.That(foo.fixedCharArray[0], Is.EqualTo('a')); + Assert.That(foo.fixedCharArray[1], Is.EqualTo('b')); + Assert.That(foo.fixedCharArray[2], Is.EqualTo('c')); + } + } + private class CustomDerivedFromVirtual : AbstractWithVirtualDtor { public override void @abstract() diff --git a/tests/Common/Common.h b/tests/Common/Common.h index 961c4615..aa0fcb7f 100644 --- a/tests/Common/Common.h +++ b/tests/Common/Common.h @@ -45,6 +45,7 @@ public: float B; IgnoredType ignoredType; int fixedArray[3]; + char fixedCharArray[3]; void* ptr; static const int unsafe; static const char charArray[];