Skip to content

Commit 357efec

Browse files
authored
Array marshalling (mono#1748)
* Generator: Customization for const char[] Allow the user to choose whether `const char[]` should be marshalled as `string` or a normal `char` array in C#. A new option `MarshalConstCharArrayAsString` is added, and is `true` by default. This helps in situations where the original C++ API distinguishes between C-strings and char arrays using the two different notations. * CSharpMarshal: Fix unknown length array marshal For unknown length arrays, also run a conversion loop if the primitive type encountered needs conversion (e.g. `char` to `sbyte`). * CSharpTypePrinter: Fix for boolean arrays
1 parent 9f3ce76 commit 357efec

File tree

6 files changed

+25
-8
lines changed

6 files changed

+25
-8
lines changed

src/Generator/Generators/CLI/CLIMarshal.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
6161
break;
6262
case ArrayType.ArraySize.Incomplete:
6363
// const char* and const char[] are the same so we can use a string
64-
if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
64+
if (Context.Context.Options.MarshalConstCharArrayAsString &&
65+
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
6566
array.QualifiedType.Qualifiers.IsConst)
6667
{
6768
var pointer = new PointerType { QualifiedPointee = array.QualifiedType };

src/Generator/Generators/CLI/CLITypePrinter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public override TypePrinterResult VisitArrayType(ArrayType array,
3535
TypeQualifiers quals)
3636
{
3737
// const char* and const char[] are the same so we can use a string
38-
if (array.SizeType == ArrayType.ArraySize.Incomplete &&
38+
if (Context.Options.MarshalConstCharArrayAsString &&
39+
array.SizeType == ArrayType.ArraySize.Incomplete &&
3940
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
4041
array.QualifiedType.Qualifiers.IsConst)
4142
return VisitCILType(new CILType(typeof(string)), quals);

src/Generator/Generators/CSharp/CSharpMarshal.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
116116
break;
117117
case ArrayType.ArraySize.Incomplete:
118118
// const char* and const char[] are the same so we can use a string
119-
if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
119+
if (Context.Context.Options.MarshalConstCharArrayAsString &&
120+
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
120121
array.QualifiedType.Qualifiers.IsConst)
121122
{
122123
var pointer = new PointerType { QualifiedPointee = array.QualifiedType };
@@ -874,7 +875,8 @@ private void MarshalArray(ArrayType arrayType)
874875

875876
var elementType = arrayType.Type.Desugar();
876877

877-
if (elementType.IsPrimitiveType() ||
878+
if ((elementType.IsPrimitiveType() &&
879+
!(elementType.IsPrimitiveType(PrimitiveType.Char) && Context.Context.Options.MarshalCharAsManagedChar)) ||
878880
elementType.IsPointerToPrimitiveType())
879881
{
880882
if (Context.Context.Options.UseSpan && !elementType.IsConstCharString())
@@ -916,6 +918,10 @@ private void MarshalArray(ArrayType arrayType)
916918
Context.Before.WriteLine($@"{intermediateArray}[i] = {
917919
element} is null ? {intPtrZero} : {element}.{Helpers.InstanceIdentifier};");
918920
}
921+
else if (elementType.IsPrimitiveType(PrimitiveType.Char) &&
922+
Context.Context.Options.MarshalCharAsManagedChar)
923+
Context.Before.WriteLine($@"{intermediateArray}[i] = global::System.Convert.ToSByte({
924+
element});");
919925
else
920926
Context.Before.WriteLine($@"{intermediateArray}[i] = {
921927
element} is null ? new {intermediateArrayType}() : *({

src/Generator/Generators/CSharp/CSharpSources.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ private string GeneratePointerTo(Variable var)
972972
string ptr = Generator.GeneratedIdentifier("ptr");
973973
if (arrayType != null)
974974
{
975-
if (arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant)
975+
if (Context.Options.MarshalConstCharArrayAsString && arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant)
976976
WriteLine($"var {ptr} = {location};");
977977
else
978978
WriteLine($"var {ptr} = ({arrayType.Type.Visit(TypePrinter)}*){location};");

src/Generator/Generators/CSharp/CSharpTypePrinter.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ public override TypePrinterResult VisitArrayType(ArrayType array,
111111
}
112112

113113
// const char* and const char[] are the same so we can use a string
114-
if (array.SizeType == ArrayType.ArraySize.Incomplete &&
114+
if (Context.Options.MarshalConstCharArrayAsString &&
115+
array.SizeType == ArrayType.ArraySize.Incomplete &&
115116
arrayType.IsPrimitiveType(PrimitiveType.Char) &&
116117
array.QualifiedType.Qualifiers.IsConst)
117118
return "string";
@@ -123,6 +124,13 @@ public override TypePrinterResult VisitArrayType(ArrayType array,
123124
return $"{prefix}string[]";
124125
}
125126

127+
if (arrayType.IsPrimitiveType(PrimitiveType.Bool))
128+
{
129+
var prefix = ContextKind == TypePrinterContextKind.Managed ? string.Empty :
130+
"[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1)] ";
131+
return $"{prefix}bool[]";
132+
}
133+
126134
if (Context.Options.UseSpan && !(array.SizeType != ArrayType.ArraySize.Constant &&
127135
MarshalKind == MarshalKind.ReturnVariableArray))
128136
{

src/Generator/Options.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public bool DoAllModulesHaveLibraries() =>
115115

116116
/// <summary>
117117
/// Enable this option to enable generation of finalizers. Works in both CLI and
118-
/// C# backends.
118+
/// C# backends.
119119
/// </summary>
120120
/// <remarks>
121121
/// Use <see cref="GenerateFinalizersFilter"/> to specify a filter so that
@@ -125,7 +125,7 @@ public bool DoAllModulesHaveLibraries() =>
125125

126126
/// <summary>
127127
/// A filter that can restrict the classes for which finalizers are generated when
128-
/// <see cref="GenerateFinalizers"/> is <c>true</c>.
128+
/// <see cref="GenerateFinalizers"/> is <c>true</c>.
129129
/// </summary>
130130
/// <remarks>
131131
/// The default filter performs no filtering so that whenever <see
@@ -189,6 +189,7 @@ public bool DoAllModulesHaveLibraries() =>
189189

190190
public readonly List<string> DependentNameSpaces = new List<string>();
191191
public bool MarshalCharAsManagedChar { get; set; }
192+
public bool MarshalConstCharArrayAsString { get; set; } = true;
192193

193194
/// <summary>
194195
/// Use Span Struct instead of Managed Array

0 commit comments

Comments
 (0)