Browse Source

Fix the generation of sequential layouts

pull/1473/head
josetr 5 years ago committed by Dimitar Dobrev
parent
commit
0d32121eee
  1. 1
      src/Generator.Tests/GeneratorTest.cs
  2. 90
      src/Generator/Generators/CSharp/CSharpSources.cs
  3. 18
      tests/CSharp/CSharp.Tests.cs

1
src/Generator.Tests/GeneratorTest.cs

@ -29,6 +29,7 @@ namespace CppSharp.Utils @@ -29,6 +29,7 @@ namespace CppSharp.Utils
options.OutputDir = Path.Combine(GetOutputDirectory(), "gen", name);
options.Quiet = true;
options.GenerateDebugOutput = true;
options.GenerateSequentialLayout = true;
var testModule = options.AddModule(name);
testModule.SharedLibraryName = $"{name}.Native";

90
src/Generator/Generators/CSharp/CSharpSources.cs

@ -531,19 +531,24 @@ namespace CppSharp.Generators.CSharp @@ -531,19 +531,24 @@ namespace CppSharp.Generators.CSharp
public void GenerateClassInternals(Class @class)
{
var sequentialLayout = Options.GenerateSequentialLayout && CanUseSequentialLayout(@class);
PushBlock(BlockKind.InternalsClass);
if (!Options.GenerateSequentialLayout || @class.IsUnion)
WriteLine($"[StructLayout(LayoutKind.Explicit, Size = {@class.Layout.GetSize()})]");
else if (@class.MaxFieldAlignment > 0)
WriteLine($"[StructLayout(LayoutKind.Sequential, Pack = {@class.MaxFieldAlignment})]");
if (@class.Layout.GetSize() > 0)
{
var layout = sequentialLayout ? "Sequential" : "Explicit";
var pack = @class.MaxFieldAlignment > 0 ? $", Pack = {@class.MaxFieldAlignment}" : string.Empty;
WriteLine($"[StructLayout(LayoutKind.{layout}, Size = {@class.Layout.GetSize()}{pack})]");
}
GenerateClassInternalHead(@class);
WriteOpenBraceAndIndent();
TypePrinter.PushContext(TypePrinterContextKind.Native);
foreach (var field in @class.Layout.Fields)
GenerateClassInternalsField(field, @class);
GenerateClassInternalsFields(@class, sequentialLayout);
if (@class.IsGenerated)
{
var functions = GatherClassInternalFunctions(@class);
@ -764,24 +769,71 @@ namespace CppSharp.Generators.CSharp @@ -764,24 +769,71 @@ namespace CppSharp.Generators.CSharp
Write(" : {0}", string.Join(", ", bases));
}
private void GenerateClassInternalsField(LayoutField field, Class @class)
private bool CanUseSequentialLayout(Class @class)
{
TypePrinterResult retType = TypePrinter.VisitFieldDecl(
new Field { Name = field.Name, QualifiedType = field.QualifiedType });
if (@class.IsUnion)
return false;
PushBlock(BlockKind.Field);
var fields = @class.Layout.Fields;
if (!Options.GenerateSequentialLayout || @class.IsUnion)
WriteLine($"[FieldOffset({field.Offset})]");
Write($"internal {retType}");
if (field.Expression != null)
{
var fieldValuePrinted = field.Expression.CSharpValue(ExpressionPrinter);
Write($" = {fieldValuePrinted}");
if (fields.Count > 1)
{
for (var i = 1; i < fields.Count; ++i)
{
if (fields[i].Offset == fields[i - 1].Offset)
return false;
}
}
WriteLine(";");
PopBlock(NewLineKind.BeforeNextBlock);
return true;
}
private void GenerateClassInternalsFields(Class @class, bool sequentalLayout)
{
var fields = @class.Layout.Fields;
for (var i = 0; i < fields.Count; ++i)
{
var field = fields[i];
TypePrinterResult retType = TypePrinter.VisitFieldDecl(
new Field { Name = field.Name, QualifiedType = field.QualifiedType });
PushBlock(BlockKind.Field);
if (!sequentalLayout)
WriteLine($"[FieldOffset({field.Offset})]");
Write($"internal {retType}");
if (field.Expression != null)
{
var fieldValuePrinted = field.Expression.CSharpValue(ExpressionPrinter);
Write($" = {fieldValuePrinted}");
}
WriteLine(";");
// HACK: work around the lack of alignment in StructLayout
// it's been requested multiple times to no avail:
// https://github.com/dotnet/runtime/issues/5931, https://github.com/dotnet/runtime/issues/9089, https://github.com/dotnet/runtime/issues/22990
// Sometimes padding is needed due to aligment.
// The linux 32 bit target pads at the end the structure
// which is already handled by using [StructLayout(Size = n)].
// However the windows 32 bit target pads at the front,
// right after the vtable pointer, which is what we are handling here
if (sequentalLayout && fields[i].IsVTablePtr)
{
var nativePointerSize = Context.TargetInfo.PointerWidth / 8;
if (i + 1 < fields.Count)
{
var padding = fields[i + 1].Offset - field.Offset - nativePointerSize;
if (padding > 0 && padding <= @class.Layout.Size)
WriteLine($"internal fixed byte {field.Name}Padding[{padding}];");
}
}
PopBlock(sequentalLayout && i + 1 != fields.Count ? NewLineKind.Never : NewLineKind.BeforeNextBlock);
}
}
private void GenerateClassField(Field field, bool @public = false)

18
tests/CSharp/CSharp.Tests.cs

@ -721,8 +721,11 @@ public unsafe class CSharpTests : GeneratorTestFixture @@ -721,8 +721,11 @@ public unsafe class CSharpTests : GeneratorTestFixture
{
var independentFields = internalType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var fieldOffset = (FieldOffsetAttribute) independentFields[0].GetCustomAttribute(typeof(FieldOffsetAttribute));
Assert.That(fieldOffset.Value, Is.EqualTo(0));
if (fieldOffset != null)
Assert.That(fieldOffset.Value, Is.EqualTo(0));
Assert.That((int)Marshal.OffsetOf(internalType, independentFields[0].Name), Is.EqualTo(0));
}
foreach (var internalType in new Type[]
{
typeof(CSharp.TwoTemplateArgs.__Internal_Ptr),
@ -733,12 +736,21 @@ public unsafe class CSharpTests : GeneratorTestFixture @@ -733,12 +736,21 @@ public unsafe class CSharpTests : GeneratorTestFixture
var independentFields = internalType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(independentFields.Length, Is.EqualTo(2));
var fieldOffsetKey = (FieldOffsetAttribute) independentFields[0].GetCustomAttribute(typeof(FieldOffsetAttribute));
Assert.That(fieldOffsetKey.Value, Is.EqualTo(0));
if (fieldOffsetKey != null)
Assert.That(fieldOffsetKey.Value, Is.EqualTo(0));
Assert.That((int)Marshal.OffsetOf(internalType, independentFields[0].Name), Is.EqualTo(0));
var fieldOffsetValue = (FieldOffsetAttribute) independentFields[1].GetCustomAttribute(typeof(FieldOffsetAttribute));
Assert.That(fieldOffsetValue.Value, Is.EqualTo(Marshal.SizeOf(IntPtr.Zero)));
if (fieldOffsetValue != null)
Assert.That(fieldOffsetValue.Value, Is.EqualTo(Marshal.SizeOf(IntPtr.Zero)));
Assert.That((int)Marshal.OffsetOf(internalType, independentFields[1].Name), Is.EqualTo(Marshal.SizeOf(IntPtr.Zero)));
}
}
public void TestClassSize()
{
Assert.That(Marshal.SizeOf<HasSecondaryBaseWithAbstractWithDefaultArg.__Internal>, Is.EqualTo(Marshal.SizeOf<IntPtr>() * 2));
}
[Test]
public void TestConstantArray()
{

Loading…
Cancel
Save