Skip to content

Commit

Permalink
Array marshalling (mono#1748)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
trungnt2910 authored Jul 14, 2023
1 parent 9f3ce76 commit 357efec
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/Generator/Generators/CLI/CLIMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
break;
case ArrayType.ArraySize.Incomplete:
// const char* and const char[] are the same so we can use a string
if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
if (Context.Context.Options.MarshalConstCharArrayAsString &&
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
array.QualifiedType.Qualifiers.IsConst)
{
var pointer = new PointerType { QualifiedPointee = array.QualifiedType };
Expand Down
3 changes: 2 additions & 1 deletion src/Generator/Generators/CLI/CLITypePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public override TypePrinterResult VisitArrayType(ArrayType array,
TypeQualifiers quals)
{
// const char* and const char[] are the same so we can use a string
if (array.SizeType == ArrayType.ArraySize.Incomplete &&
if (Context.Options.MarshalConstCharArrayAsString &&
array.SizeType == ArrayType.ArraySize.Incomplete &&
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
array.QualifiedType.Qualifiers.IsConst)
return VisitCILType(new CILType(typeof(string)), quals);
Expand Down
10 changes: 8 additions & 2 deletions src/Generator/Generators/CSharp/CSharpMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ public override bool VisitArrayType(ArrayType array, TypeQualifiers quals)
break;
case ArrayType.ArraySize.Incomplete:
// const char* and const char[] are the same so we can use a string
if (array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
if (Context.Context.Options.MarshalConstCharArrayAsString &&
array.Type.Desugar().IsPrimitiveType(PrimitiveType.Char) &&
array.QualifiedType.Qualifiers.IsConst)
{
var pointer = new PointerType { QualifiedPointee = array.QualifiedType };
Expand Down Expand Up @@ -874,7 +875,8 @@ private void MarshalArray(ArrayType arrayType)

var elementType = arrayType.Type.Desugar();

if (elementType.IsPrimitiveType() ||
if ((elementType.IsPrimitiveType() &&
!(elementType.IsPrimitiveType(PrimitiveType.Char) && Context.Context.Options.MarshalCharAsManagedChar)) ||
elementType.IsPointerToPrimitiveType())
{
if (Context.Context.Options.UseSpan && !elementType.IsConstCharString())
Expand Down Expand Up @@ -916,6 +918,10 @@ private void MarshalArray(ArrayType arrayType)
Context.Before.WriteLine($@"{intermediateArray}[i] = {
element} is null ? {intPtrZero} : {element}.{Helpers.InstanceIdentifier};");
}
else if (elementType.IsPrimitiveType(PrimitiveType.Char) &&
Context.Context.Options.MarshalCharAsManagedChar)
Context.Before.WriteLine($@"{intermediateArray}[i] = global::System.Convert.ToSByte({
element});");
else
Context.Before.WriteLine($@"{intermediateArray}[i] = {
element} is null ? new {intermediateArrayType}() : *({
Expand Down
2 changes: 1 addition & 1 deletion src/Generator/Generators/CSharp/CSharpSources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ private string GeneratePointerTo(Variable var)
string ptr = Generator.GeneratedIdentifier("ptr");
if (arrayType != null)
{
if (arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant)
if (Context.Options.MarshalConstCharArrayAsString && arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && arrayType.SizeType != ArrayType.ArraySize.Constant)
WriteLine($"var {ptr} = {location};");
else
WriteLine($"var {ptr} = ({arrayType.Type.Visit(TypePrinter)}*){location};");
Expand Down
10 changes: 9 additions & 1 deletion src/Generator/Generators/CSharp/CSharpTypePrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ public override TypePrinterResult VisitArrayType(ArrayType array,
}

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

if (arrayType.IsPrimitiveType(PrimitiveType.Bool))
{
var prefix = ContextKind == TypePrinterContextKind.Managed ? string.Empty :
"[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1)] ";
return $"{prefix}bool[]";
}

if (Context.Options.UseSpan && !(array.SizeType != ArrayType.ArraySize.Constant &&
MarshalKind == MarshalKind.ReturnVariableArray))
{
Expand Down
5 changes: 3 additions & 2 deletions src/Generator/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public bool DoAllModulesHaveLibraries() =>

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

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

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

/// <summary>
/// Use Span Struct instead of Managed Array
Expand Down

0 comments on commit 357efec

Please sign in to comment.