Skip to content

Commit 80643e0

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 80643e0

File tree

7 files changed

+102
-43
lines changed

7 files changed

+102
-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

+51-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,43 @@ 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+
Context.SupportBefore.WriteLine("if ({0} == null || {0}.Length != {1})", Context.Parameter.Name, array.Size);
400+
ThrowArgumentOutOfRangeException();
401+
const string ptr = "__ptr";
402+
Context.SupportBefore.WriteLine("fixed ({0}* {1} = {2})", array.Type, ptr, Context.Parameter.Name);
403+
Context.SupportBefore.WriteStartBraceIndent();
404+
Context.Return.Write("new global::System.IntPtr({0})", ptr);
405+
CSharpContext.HasFixedBlock = true;
405406
}
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);
410407
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();
408+
{
409+
var supportBefore = Context.SupportBefore;
410+
supportBefore.WriteLine("if ({0} != null)", Context.ArgName);
411+
supportBefore.WriteStartBraceIndent();
412+
Class @class;
413+
if (array.Type.Desugar().TryGetClass(out @class) && @class.IsRefType)
414+
{
415+
supportBefore.WriteLine("if (value.Length != {0})", array.Size);
416+
ThrowArgumentOutOfRangeException();
417+
}
418+
supportBefore.WriteLine("for (int i = 0; i < {0}; i++)", array.Size);
419+
if (@class != null && @class.IsRefType)
420+
supportBefore.WriteLineIndent(
421+
"{0}[i * sizeof({2}.Internal)] = *((byte*)({2}.Internal*){1}[i].__Instance);",
422+
Context.ReturnVarName, Context.ArgName, array.Type);
423+
else
424+
supportBefore.WriteLineIndent("{0}[i] = {1}[i]{2};",
425+
Context.ReturnVarName, Context.ArgName,
426+
array.Type.IsPointerToPrimitiveType(PrimitiveType.Void)
427+
? ".ToPointer()"
428+
: string.Empty);
429+
supportBefore.WriteCloseBraceIndent();
430+
}
415431
break;
416432
default:
417433
Context.Return.Write("null");
@@ -420,6 +436,14 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
420436
return true;
421437
}
422438

439+
private void ThrowArgumentOutOfRangeException()
440+
{
441+
Context.SupportBefore.WriteLineIndent(
442+
"throw new ArgumentOutOfRangeException(\"{0}\", " +
443+
"\"The dimensions of the provided array don't match the required size.\");",
444+
Context.Parameter.Name);
445+
}
446+
423447
public bool VisitDelegateType(FunctionType function, string type)
424448
{
425449
Context.Return.Write("{0} == null ? global::System.IntPtr.Zero : Marshal.GetFunctionPointerForDelegate({0})",
@@ -432,6 +456,17 @@ public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
432456
if (!VisitType(pointer, quals))
433457
return false;
434458

459+
if (Context.Function != null && pointer.IsPrimitiveTypeConvertibleToRef())
460+
{
461+
string refParamPtr = string.Format("__refParamPtr{0}", Context.ParameterIndex);
462+
Context.SupportBefore.WriteLine("fixed ({0} {1} = &{2})",
463+
pointer, refParamPtr, Context.Parameter.Name);
464+
CSharpContext.HasFixedBlock = true;
465+
Context.SupportBefore.WriteStartBraceIndent();
466+
Context.Return.Write(refParamPtr);
467+
return true;
468+
}
469+
435470
var param = Context.Parameter;
436471
var isRefParam = param != null && (param.IsInOut || param.IsOut);
437472

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)