Skip to content

Commit b84df55

Browse files
committed
Added support for parameters with type a reference to a fixed-size array.
Signed-off-by: Dimitar Dobrev <[email protected]>
1 parent 7de598b commit b84df55

File tree

7 files changed

+95
-43
lines changed

7 files changed

+95
-43
lines changed

src/Generator/Generators/CLI/CLIMarshal.cs

+20-8
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,26 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
431431
switch (array.SizeType)
432432
{
433433
case ArrayType.ArraySize.Constant:
434-
var supportBefore = Context.SupportBefore;
435-
supportBefore.WriteLine("if ({0} != nullptr)", Context.ArgName);
436-
supportBefore.WriteStartBraceIndent();
437-
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
438-
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
439-
Context.ReturnVarName, Context.ArgName,
440-
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) ? ".ToPointer()" : string.Empty);
441-
supportBefore.WriteCloseBraceIndent();
434+
if (string.IsNullOrEmpty(Context.ReturnVarName))
435+
{
436+
const string pinnedPtr = "__pinnedPtr";
437+
Context.SupportBefore.WriteLine("cli::pin_ptr<{0}> {1} = &{2}[0];",
438+
array.Type, pinnedPtr, Context.Parameter.Name);
439+
const string arrayPtr = "__arrayPtr";
440+
Context.SupportBefore.WriteLine("{0}* {1} = {2};", array.Type, arrayPtr, pinnedPtr);
441+
Context.Return.Write("({0} (&)[{1}]) {2}", array.Type, array.Size, arrayPtr);
442+
}
443+
else
444+
{
445+
var supportBefore = Context.SupportBefore;
446+
supportBefore.WriteLine("if ({0} != nullptr)", Context.ArgName);
447+
supportBefore.WriteStartBraceIndent();
448+
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
449+
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
450+
Context.ReturnVarName, Context.ArgName,
451+
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) ? ".ToPointer()" : string.Empty);
452+
supportBefore.WriteCloseBraceIndent();
453+
}
442454
break;
443455
default:
444456
Context.Return.Write("null");

src/Generator/Generators/CSharp/CSharpMarshal.cs

+44-16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public CSharpMarshalContext(Driver driver)
3030

3131
public TextGenerator ArgumentPrefix { get; private set; }
3232
public TextGenerator Cleanup { get; private set; }
33+
public bool HasFixedBlock { get; set; }
3334
}
3435

3536
public abstract class CSharpMarshalPrinter : MarshalPrinter
@@ -390,28 +391,44 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
390391
if (!VisitType(array, quals))
391392
return false;
392393

393-
Class @class;
394394
switch (array.SizeType)
395395
{
396396
case ArrayType.ArraySize.Constant:
397-
var supportBefore = Context.SupportBefore;
398-
supportBefore.WriteLine("if ({0} != null)", Context.ArgName);
399-
supportBefore.WriteStartBraceIndent();
400-
if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType)
397+
if (string.IsNullOrEmpty(Context.ReturnVarName))
401398
{
402-
supportBefore.WriteLine("if (value.Length != {0})", array.Size);
403-
supportBefore.WriteLineIndent("throw new ArgumentOutOfRangeException(\"{0}\", \"The provided array's dimensions doesn't match the required size.\");",
404-
Context.Parameter.Name);
399+
const string ptr = "__ptr";
400+
Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, ptr, Context.Parameter.Name);
401+
Context.SupportBefore.WriteStartBraceIndent();
402+
Context.Return.Write("new global::System.IntPtr({0})", ptr);
403+
CSharpContext.HasFixedBlock = true;
405404
}
406-
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
407-
if (@class != null && @class.IsRefType)
408-
supportBefore.WriteLineIndent("{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);",
409-
Context.ReturnVarName, Context.ArgName, array.Type);
410405
else
411-
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
412-
Context.ReturnVarName, Context.ArgName,
413-
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void) ? ".ToPointer()" : string.Empty);
414-
supportBefore.WriteCloseBraceIndent();
406+
{
407+
var supportBefore = Context.SupportBefore;
408+
supportBefore.WriteLine("if ({0} != null)", Context.ArgName);
409+
supportBefore.WriteStartBraceIndent();
410+
Class @class;
411+
if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType)
412+
{
413+
supportBefore.WriteLine("if (value.Length != {0})", array.Size);
414+
supportBefore.WriteLineIndent(
415+
"throw new ArgumentOutOfRangeException(\"{0}\"," +
416+
"\"The provided array's dimensions doesn't match the required size.\");",
417+
Context.Parameter.Name);
418+
}
419+
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
420+
if (@class != null && @class.IsRefType)
421+
supportBefore.WriteLineIndent(
422+
"{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);",
423+
Context.ReturnVarName, Context.ArgName, array.Type);
424+
else
425+
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
426+
Context.ReturnVarName, Context.ArgName,
427+
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void)
428+
? ".ToPointer()"
429+
: string.Empty);
430+
supportBefore.WriteCloseBraceIndent();
431+
}
415432
break;
416433
default:
417434
Context.Return.Write("null");
@@ -432,6 +449,17 @@ public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
432449
if (!VisitType(pointer, quals))
433450
return false;
434451

452+
if (Context.Function != null && pointer.IsPrimitiveTypeConvertibleToRef())
453+
{
454+
string refParamPtr = string.Format("__refParamPtr{0}", Context.ParameterIndex);
455+
Context.SupportBefore.WriteLine("fixed ({0} {1} = &{2})",
456+
pointer, refParamPtr, Context.Parameter.Name);
457+
CSharpContext.HasFixedBlock = true;
458+
Context.SupportBefore.WriteStartBraceIndent();
459+
Context.Return.Write(refParamPtr);
460+
return true;
461+
}
462+
435463
var param = Context.Parameter;
436464
var isRefParam = param != null && (param.IsInOut || param.IsOut);
437465

src/Generator/Generators/CSharp/CSharpTextTemplate.cs

+11-17
Original file line numberDiff line numberDiff line change
@@ -2693,7 +2693,7 @@ private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramInde
26932693
if ((paramType.GetFinalPointee() ?? paramType).Desugar().TryGetClass(out @class))
26942694
{
26952695
var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier(
2696-
@class.OriginalClass ?? @class);
2696+
@class.OriginalClass ?? @class);
26972697
WriteLine("{0} = new {1}();", name, qualifiedIdentifier);
26982698
}
26992699
}
@@ -2707,26 +2707,20 @@ private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramInde
27072707
};
27082708

27092709
paramMarshal.Context = ctx;
2710+
var marshal = new CSharpMarshalManagedToNativePrinter(ctx);
2711+
param.CSharpMarshalToNative(marshal);
2712+
paramMarshal.HasFixedBlock = ctx.HasFixedBlock;
27102713

2711-
if (param.Type.IsPrimitiveTypeConvertibleToRef())
2712-
{
2713-
WriteLine("fixed ({0} {1} = &{2})", param.Type.CSharpType(TypePrinter), argName, name);
2714-
paramMarshal.HasFixedBlock = true;
2715-
WriteStartBraceIndent();
2716-
}
2717-
else
2718-
{
2719-
var marshal = new CSharpMarshalManagedToNativePrinter(ctx);
2720-
param.CSharpMarshalToNative(marshal);
2714+
if (string.IsNullOrEmpty(marshal.Context.Return))
2715+
throw new Exception("Cannot marshal argument of function");
27212716

2722-
if (string.IsNullOrEmpty(marshal.Context.Return))
2723-
throw new Exception("Cannot marshal argument of function");
2717+
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
2718+
Write(marshal.Context.SupportBefore);
27242719

2725-
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
2726-
Write(marshal.Context.SupportBefore);
2720+
if (paramMarshal.HasFixedBlock)
2721+
PushIndent();
27272722

2728-
WriteLine("var {0} = {1};", argName, marshal.Context.Return);
2729-
}
2723+
WriteLine("var {0} = {1};", argName, marshal.Context.Return);
27302724

27312725
return paramMarshal;
27322726
}

src/Generator/Generators/CSharp/CSharpTypePrinter.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ public CSharpTypePrinterResult VisitPointerType(PointerType pointer,
276276
}
277277

278278
Class @class;
279-
if ((desugared.IsDependent || desugared.TryGetClass(out @class))
279+
if ((desugared.IsDependent || desugared.TryGetClass(out @class) ||
280+
(desugared is ArrayType && Context.Parameter != null))
280281
&& ContextKind == CSharpTypePrinterContextKind.Native)
281282
{
282283
return "global::System.IntPtr";

tests/CSharp/CSharp.Tests.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ public void TestFixedArrayRefType()
415415
foosMore[1] = new Foo();
416416
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => bar.Foos = foosMore);
417417
Assert.AreEqual("value", ex.ParamName);
418-
Assert.AreEqual("The provided array's dimensions doesn't match the required size." + Environment.NewLine +"Parameter name: value", ex.Message);
418+
Assert.AreEqual("The dimensions of the provided array don't match the required size." + Environment.NewLine + "Parameter name: value", ex.Message);
419419
}
420420

421421
[Test]
@@ -443,4 +443,14 @@ public unsafe void TestSizeOfDerivesFromTemplateInstantiation()
443443
{
444444
Assert.That(sizeof(DerivesFromTemplateInstantiation.Internal), Is.EqualTo(sizeof(int)));
445445
}
446+
447+
[Test]
448+
public void TestReferenceToArrayWithConstSize()
449+
{
450+
int[] incorrectlySizedArray = { 1 };
451+
Assert.Catch<ArgumentOutOfRangeException>(() => CSharp.CSharp.PassConstantArrayRef(incorrectlySizedArray));
452+
int[] array = { 1, 2 };
453+
var result = CSharp.CSharp.PassConstantArrayRef(array);
454+
Assert.That(result, Is.EqualTo(array[0]));
455+
}
446456
}

tests/CSharp/CSharp.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -798,3 +798,8 @@ TemplateWithDependentField<T>::TemplateWithDependentField()
798798
DerivesFromTemplateInstantiation::DerivesFromTemplateInstantiation()
799799
{
800800
}
801+
802+
int PassConstantArrayRef(int(&arr)[2])
803+
{
804+
return arr[0];
805+
}

tests/CSharp/CSharp.h

+2
Original file line numberDiff line numberDiff line change
@@ -731,3 +731,5 @@ class DerivesFromTemplateInstantiation : public TemplateWithDependentField<int>
731731
public:
732732
DerivesFromTemplateInstantiation();
733733
};
734+
735+
DLL_API int PassConstantArrayRef(int(&arr)[2]);

0 commit comments

Comments
 (0)