Skip to content

Commit

Permalink
Added support for parameters with type a reference to a fixed-size ar…
Browse files Browse the repository at this point in the history
…ray.

Signed-off-by: Dimitar Dobrev <[email protected]>
  • Loading branch information
ddobrev committed Sep 21, 2015
1 parent 7de598b commit b84df55
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 43 deletions.
28 changes: 20 additions & 8 deletions src/Generator/Generators/CLI/CLIMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
60 changes: 44 additions & 16 deletions src/Generator/Generators/CSharp/CSharpMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -390,28 +391,44 @@ 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);
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);
supportBefore.WriteLineIndent(
"throw new ArgumentOutOfRangeException(\"{0}\"," +
"\"The provided array's dimensions doesn't match the required size.\");",
Context.Parameter.Name);
}
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");
Expand All @@ -432,6 +449,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);

Expand Down
28 changes: 11 additions & 17 deletions src/Generator/Generators/CSharp/CSharpTextTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand All @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Generator/Generators/CSharp/CSharpTypePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
12 changes: 11 additions & 1 deletion tests/CSharp/CSharp.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ public void TestFixedArrayRefType()
foosMore[1] = new Foo();
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => 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]
Expand Down Expand Up @@ -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<ArgumentOutOfRangeException>(() => CSharp.CSharp.PassConstantArrayRef(incorrectlySizedArray));
int[] array = { 1, 2 };
var result = CSharp.CSharp.PassConstantArrayRef(array);
Assert.That(result, Is.EqualTo(array[0]));
}
}
5 changes: 5 additions & 0 deletions tests/CSharp/CSharp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,8 @@ TemplateWithDependentField<T>::TemplateWithDependentField()
DerivesFromTemplateInstantiation::DerivesFromTemplateInstantiation()
{
}

int PassConstantArrayRef(int(&arr)[2])
{
return arr[0];
}
2 changes: 2 additions & 0 deletions tests/CSharp/CSharp.h
Original file line number Diff line number Diff line change
Expand Up @@ -731,3 +731,5 @@ class DerivesFromTemplateInstantiation : public TemplateWithDependentField<int>
public:
DerivesFromTemplateInstantiation();
};

DLL_API int PassConstantArrayRef(int(&arr)[2]);

0 comments on commit b84df55

Please sign in to comment.