diff --git a/src/Generator/Generators/CLI/CLIMarshal.cs b/src/Generator/Generators/CLI/CLIMarshal.cs index 04f4fa1c7e..aaa505fd51 100644 --- a/src/Generator/Generators/CLI/CLIMarshal.cs +++ b/src/Generator/Generators/CLI/CLIMarshal.cs @@ -431,14 +431,26 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) switch (array.SizeType) { case ArrayType.ArraySize.Constant: - var supportBefore = Context.SupportBefore; - supportBefore.WriteLine("if ({0} != nullptr)", Context.ArgName); - supportBefore.WriteStartBraceIndent(); - supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); - supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};", - Context.ReturnVarName, Context.ArgName, - array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) ? ".ToPointer()" : string.Empty); - supportBefore.WriteCloseBraceIndent(); + if (string.IsNullOrEmpty(Context.ReturnVarName)) + { + const string pinnedPtr = "__pinnedPtr"; + Context.SupportBefore.WriteLine("cli::pin_ptr<{0}> {1} = &{2}[0];", + array.Type, pinnedPtr, Context.Parameter.Name); + const string arrayPtr = "__arrayPtr"; + Context.SupportBefore.WriteLine("{0}* {1} = {2};", array.Type, arrayPtr, pinnedPtr); + Context.Return.Write("({0} (&)[{1}]) {2}", array.Type, array.Size, arrayPtr); + } + else + { + var supportBefore = Context.SupportBefore; + supportBefore.WriteLine("if ({0} != nullptr)", Context.ArgName); + supportBefore.WriteStartBraceIndent(); + supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size); + supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};", + Context.ReturnVarName, Context.ArgName, + array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) ? ".ToPointer()" : string.Empty); + supportBefore.WriteCloseBraceIndent(); + } break; default: Context.Return.Write("null"); diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 00536736fc..15067223d7 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -30,6 +30,7 @@ public CSharpMarshalContext(Driver driver) public TextGenerator ArgumentPrefix { get; private set; } public TextGenerator Cleanup { get; private set; } + public bool HasFixedBlock { get; set; } } public abstract class CSharpMarshalPrinter : MarshalPrinter @@ -390,28 +391,43 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) if (!VisitType(array, quals)) return false; - Class @class; switch (array.SizeType) { case ArrayType.ArraySize.Constant: - var supportBefore = Context.SupportBefore; - supportBefore.WriteLine("if ({0} != null)", Context.ArgName); - supportBefore.WriteStartBraceIndent(); - if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) + if (string.IsNullOrEmpty(Context.ReturnVarName)) { - supportBefore.WriteLine("if (value.Length != {0})", array.Size); - supportBefore.WriteLineIndent("throw new ArgumentOutOfRangeException(\"{0}\", \"The provided array's dimensions doesn't match the required size.\");", - Context.Parameter.Name); + 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.WriteStartBraceIndent(); + Context.Return.Write("new global::System.IntPtr({0})", ptr); + CSharpContext.HasFixedBlock = true; } - 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); - supportBefore.WriteCloseBraceIndent(); + { + var supportBefore = Context.SupportBefore; + supportBefore.WriteLine("if ({0} != null)", Context.ArgName); + supportBefore.WriteStartBraceIndent(); + Class @class; + if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType) + { + supportBefore.WriteLine("if (value.Length != {0})", array.Size); + ThrowArgumentOutOfRangeException(); + } + 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); + supportBefore.WriteCloseBraceIndent(); + } break; default: Context.Return.Write("null"); @@ -420,6 +436,14 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) return true; } + private void ThrowArgumentOutOfRangeException() + { + Context.SupportBefore.WriteLineIndent( + "throw new ArgumentOutOfRangeException(\"{0}\", " + + "\"The dimensions of the provided array don't match the required size.\");", + Context.Parameter.Name); + } + public bool VisitDelegateType(FunctionType function, string type) { Context.Return.Write("{0} == null ? global::System.IntPtr.Zero : Marshal.GetFunctionPointerForDelegate({0})", @@ -432,6 +456,17 @@ public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals) if (!VisitType(pointer, quals)) return false; + if (Context.Function != null && pointer.IsPrimitiveTypeConvertibleToRef()) + { + string refParamPtr = string.Format("__refParamPtr{0}", Context.ParameterIndex); + Context.SupportBefore.WriteLine("fixed ({0} {1} = &{2})", + pointer, refParamPtr, Context.Parameter.Name); + CSharpContext.HasFixedBlock = true; + Context.SupportBefore.WriteStartBraceIndent(); + Context.Return.Write(refParamPtr); + return true; + } + var param = Context.Parameter; var isRefParam = param != null && (param.IsInOut || param.IsOut); diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 61ecca85a3..460eb524ba 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -2693,7 +2693,7 @@ private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramInde if ((paramType.GetFinalPointee() ?? paramType).Desugar().TryGetClass(out @class)) { var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier( - @class.OriginalClass ?? @class); + @class.OriginalClass ?? @class); WriteLine("{0} = new {1}();", name, qualifiedIdentifier); } } @@ -2707,26 +2707,20 @@ private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramInde }; paramMarshal.Context = ctx; + var marshal = new CSharpMarshalManagedToNativePrinter(ctx); + param.CSharpMarshalToNative(marshal); + paramMarshal.HasFixedBlock = ctx.HasFixedBlock; - if (param.Type.IsPrimitiveTypeConvertibleToRef()) - { - WriteLine("fixed ({0} {1} = &{2})", param.Type.CSharpType(TypePrinter), argName, name); - paramMarshal.HasFixedBlock = true; - WriteStartBraceIndent(); - } - else - { - var marshal = new CSharpMarshalManagedToNativePrinter(ctx); - param.CSharpMarshalToNative(marshal); + if (string.IsNullOrEmpty(marshal.Context.Return)) + throw new Exception("Cannot marshal argument of function"); - if (string.IsNullOrEmpty(marshal.Context.Return)) - throw new Exception("Cannot marshal argument of function"); + if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) + Write(marshal.Context.SupportBefore); - if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) - Write(marshal.Context.SupportBefore); + if (paramMarshal.HasFixedBlock) + PushIndent(); - WriteLine("var {0} = {1};", argName, marshal.Context.Return); - } + WriteLine("var {0} = {1};", argName, marshal.Context.Return); return paramMarshal; } diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index b0b71d0c33..5651101156 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -276,7 +276,8 @@ public CSharpTypePrinterResult VisitPointerType(PointerType pointer, } Class @class; - if ((desugared.IsDependent || desugared.TryGetClass(out @class)) + if ((desugared.IsDependent || desugared.TryGetClass(out @class) || + (desugared is ArrayType && Context.Parameter != null)) && ContextKind == CSharpTypePrinterContextKind.Native) { return "global::System.IntPtr"; diff --git a/tests/CSharp/CSharp.Tests.cs b/tests/CSharp/CSharp.Tests.cs index 66c1bd87ac..0fbbdf7438 100644 --- a/tests/CSharp/CSharp.Tests.cs +++ b/tests/CSharp/CSharp.Tests.cs @@ -415,7 +415,7 @@ public void TestFixedArrayRefType() foosMore[1] = new Foo(); var ex = Assert.Throws(() => bar.Foos = foosMore); Assert.AreEqual("value", ex.ParamName); - Assert.AreEqual("The provided array's dimensions doesn't match the required size." + Environment.NewLine +"Parameter name: value", ex.Message); + Assert.AreEqual("The dimensions of the provided array don't match the required size." + Environment.NewLine + "Parameter name: value", ex.Message); } [Test] @@ -443,4 +443,14 @@ public unsafe void TestSizeOfDerivesFromTemplateInstantiation() { Assert.That(sizeof(DerivesFromTemplateInstantiation.Internal), Is.EqualTo(sizeof(int))); } + + [Test] + public void TestReferenceToArrayWithConstSize() + { + int[] incorrectlySizedArray = { 1 }; + Assert.Catch(() => CSharp.CSharp.PassConstantArrayRef(incorrectlySizedArray)); + int[] array = { 1, 2 }; + var result = CSharp.CSharp.PassConstantArrayRef(array); + Assert.That(result, Is.EqualTo(array[0])); + } } diff --git a/tests/CSharp/CSharp.cpp b/tests/CSharp/CSharp.cpp index 0b5d74846f..e30ae4fc92 100644 --- a/tests/CSharp/CSharp.cpp +++ b/tests/CSharp/CSharp.cpp @@ -798,3 +798,8 @@ TemplateWithDependentField::TemplateWithDependentField() DerivesFromTemplateInstantiation::DerivesFromTemplateInstantiation() { } + +int PassConstantArrayRef(int(&arr)[2]) +{ + return arr[0]; +} diff --git a/tests/CSharp/CSharp.h b/tests/CSharp/CSharp.h index a570c8974d..b6cb696a2a 100644 --- a/tests/CSharp/CSharp.h +++ b/tests/CSharp/CSharp.h @@ -731,3 +731,5 @@ class DerivesFromTemplateInstantiation : public TemplateWithDependentField public: DerivesFromTemplateInstantiation(); }; + +DLL_API int PassConstantArrayRef(int(&arr)[2]);