From 98ab8935a1864df46a770bcde9d796238fd6a77b Mon Sep 17 00:00:00 2001 From: Andrew Boyarshin Date: Sun, 11 Apr 2021 23:04:31 +0700 Subject: [PATCH] Provide more information in Desc structures Drop the only use of OutputMode in PInvokeGenerator binding generation code. This is no place for hardcoding IOutputBuilder-specific logic. --- .../Abstractions/FunctionOrDelegateDesc.cs | 17 ++ .../Abstractions/IOutputBuilder.VisitDecl.cs | 1 - .../Abstractions/StructDesc.cs | 49 +++++- .../Abstractions/StructFlags.cs | 3 +- .../CSharp/CSharpOutputBuilder.VisitDecl.cs | 20 +-- .../FunctionOrDelegateFlags.cs | 4 +- .../PInvokeGenerator.VisitDecl.cs | 155 ++++++++++-------- .../XML/XmlOutputBuilder.VisitDecl.cs | 14 +- 8 files changed, 169 insertions(+), 94 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs index 983689cd..0b2b8f81 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs @@ -13,6 +13,7 @@ internal struct FunctionOrDelegateDesc public string EscapedName { get; set; } public string EntryPoint { get; set; } public string LibraryPath { get; set; } + public string ReturnType { get; set; } public CallingConvention CallingConvention { get; set; } public FunctionOrDelegateFlags Flags { get; set; } public long? VtblIndex { get; set; } @@ -197,6 +198,22 @@ public bool? IsStatic } } + public bool NeedsReturnFixup + { + get => (Flags & FunctionOrDelegateFlags.NeedsReturnFixup) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.NeedsReturnFixup + : Flags & ~FunctionOrDelegateFlags.NeedsReturnFixup; + } + + public bool IsCxxConstructor + { + get => (Flags & FunctionOrDelegateFlags.IsCxxConstructor) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsCxxConstructor + : Flags & ~FunctionOrDelegateFlags.IsCxxConstructor; + } + public Action WriteCustomAttrs { get; set; } public TCustomAttrGeneratorData CustomAttrGeneratorData { get; set; } } diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs index ac5c34d3..094c81bb 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs @@ -31,7 +31,6 @@ internal partial interface IOutputBuilder void BeginFunctionOrDelegate(in FunctionOrDelegateDesc info, ref bool isMethodClassUnsafe); - void WriteReturnType(string typeString); void BeginFunctionInnerPrototype(string escapedName); void BeginParameter(in ParameterDesc info); void BeginParameterDefault(); diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs index 8d8af8f9..b810d838 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft and Contributors. All rights reserved. Licensed under the University of Illinois/NCSA Open Source License. See LICENSE.txt in the project root for license information. using System; +using System.Diagnostics; using System.Runtime.InteropServices; using ClangSharp.Interop; @@ -12,7 +13,7 @@ internal struct StructDesc public string EscapedName { get; set; } public string NativeType { get; set; } public string NativeInheritance { get; set; } - public StructLayoutAttribute Layout { get; set; } + public LayoutDesc Layout { get; set; } public Guid? Uuid { get; set; } public StructFlags Flags { get; set; } public CXSourceLocation? Location { get; set; } @@ -43,7 +44,53 @@ public bool HasVtbl } } + public bool IsUnion + { + get => (Flags & StructFlags.IsUnion) != 0; + set => Flags = value ? Flags | StructFlags.IsUnion : Flags & ~StructFlags.IsUnion; + } + public Action WriteCustomAttrs { get; set; } public TCustomAttrGeneratorData CustomAttrGeneratorData { get; set; } + + public StructLayoutAttribute LayoutAttribute + { + get + { + var layout = Layout; + + if (IsUnion) + { + Debug.Assert(layout.Kind == LayoutKind.Explicit); + + StructLayoutAttribute attribute = new(layout.Kind); + + if (layout.Pack < layout.MaxFieldAlignment) + { + attribute.Pack = (int)layout.Pack; + } + + return attribute; + } + + if (layout.Pack < layout.MaxFieldAlignment) + { + return new StructLayoutAttribute(layout.Kind) {Pack = (int)layout.Pack}; + } + + return null; + } + } + + public struct LayoutDesc + { + public long Alignment32 { get; set; } + public long Alignment64 { get; set; } + public long Size32 { get; set; } + public long Size64 { get; set; } + public long Pack { get; set; } + public long MaxFieldAlignment { get; set; } + public LayoutKind Kind { get; set; } + } } } diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs index 637b5986..46db5feb 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs @@ -8,6 +8,7 @@ namespace ClangSharp.Abstractions public enum StructFlags { IsUnsafe = 1 << 0, - HasVtbl = 1 << 1 + HasVtbl = 1 << 1, + IsUnion = 1 << 2, } } diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs index 9c2a425d..f287e11a 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs @@ -279,6 +279,12 @@ public void BeginFunctionOrDelegate(in FunctionOrDeleg } } } + + if (!desc.IsCxxConstructor) + { + Write(desc.ReturnType); + Write(' '); + } } private void WriteSourceLocation(CXSourceLocation location, bool inline) @@ -304,12 +310,6 @@ private void WriteSourceLocation(CXSourceLocation location, bool inline) Write(' '); } - public void WriteReturnType(string typeString) - { - Write(typeString); - Write(' '); - } - public void BeginFunctionInnerPrototype(string escapedName) { Write(escapedName); @@ -433,15 +433,15 @@ public void EndFunctionOrDelegate(bool isVirtual, bool isBodyless) public void BeginStruct(in StructDesc info) { - if (info.Layout is not null) + if (info.LayoutAttribute is { } attribute) { AddUsingDirective("System.Runtime.InteropServices"); WriteIndented("[StructLayout(LayoutKind."); - Write(info.Layout.Value); - if (info.Layout.Pack != default) + Write(attribute.Value); + if (attribute.Pack != default) { Write(", Pack = "); - Write(info.Layout.Pack); + Write(attribute.Pack); } WriteLine(")]"); diff --git a/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs index ec6f5af3..107e92f0 100644 --- a/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs +++ b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs @@ -17,6 +17,8 @@ public enum FunctionOrDelegateFlags IsCxxRecordCtxUnsafe = 1 << 9, IsMemberFunction = 1 << 10, IsStatic = 1 << 11, - IsNotStatic = 1 << 12 + IsNotStatic = 1 << 12, + NeedsReturnFixup = 1 << 13, + IsCxxConstructor = 1 << 14, } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 097e5dfe..4981160f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -469,6 +469,8 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) : null; var isDllImport = body is null && !isVirtual; + var needsReturnFixup = isVirtual && NeedsReturnFixup(cxxMethodDecl); + var desc = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> { AccessSpecifier = accessSppecifier, @@ -496,18 +498,14 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) x.This.WithUsings(x.Name); }, CustomAttrGeneratorData = (name, this), + NeedsReturnFixup = needsReturnFixup, + ReturnType = needsReturnFixup ? $"{returnTypeName}*" : returnTypeName, + IsCxxConstructor = functionDecl is CXXConstructorDecl, Location = functionDecl.Location }; _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); - var needsReturnFixup = isVirtual && NeedsReturnFixup(cxxMethodDecl); - - if ((functionDecl is not CXXConstructorDecl) || (_config.OutputMode == PInvokeGeneratorOutputMode.Xml)) - { - _outputBuilder.WriteReturnType(needsReturnFixup ? $"{returnTypeName}*" : returnTypeName); - } - _outputBuilder.BeginFunctionInnerPrototype(escapedName); if (isVirtual) @@ -1046,7 +1044,9 @@ private void VisitRecordDecl(RecordDecl recordDecl) var alignment = Math.Max(recordDecl.TypeForDecl.Handle.AlignOf, 1); var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => Math.Max(fieldDecl.Type.Handle.AlignOf, 1)) : alignment; - if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && !(recordDecl.DeclContext is RecordDecl)) + var generateTestsClass = _testOutputBuilder != null && !recordDecl.IsAnonymousStructOrUnion && recordDecl.DeclContext is not RecordDecl; + + if (generateTestsClass) { _testOutputBuilder.WriteIndented("/// Provides validation of the { }, @@ -1221,7 +1226,7 @@ private void VisitRecordDecl(RecordDecl recordDecl) } } - if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && !(recordDecl.DeclContext is RecordDecl)) + if (generateTestsClass) { _testOutputBuilder.WriteIndented("/// Validates that the Validates that the { @@ -1572,12 +1577,13 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod IsCtxCxxRecord = true, IsCxxRecordCtxUnsafe = IsUnsafe(cxxRecordDecl), IsUnsafe = true, + NeedsReturnFixup = needsReturnFixup, + ReturnType = returnTypeName, VtblIndex = _config.GenerateVtblIndexAttribute ? cxxMethodDecl.VtblIndex : -1, Location = cxxMethodDecl.Location }; _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); - _outputBuilder.WriteReturnType(returnTypeName); _outputBuilder.BeginFunctionInnerPrototype(desc.EscapedName); Visit(cxxMethodDecl.Parameters); @@ -1585,7 +1591,6 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod _outputBuilder.EndFunctionInnerPrototype(); _outputBuilder.BeginBody(); - var needsReturnFixup = false; var cxxRecordDeclName = GetRemappedCursorName(cxxRecordDecl); var escapedCXXRecordDeclName = EscapeName(cxxRecordDeclName); @@ -1601,21 +1606,19 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod body.WriteIndentation(); } - if (returnType.Kind != CXTypeKind.CXType_Void) + if (needsReturnFixup) { - needsReturnFixup = NeedsReturnFixup(cxxMethodDecl); - - if (needsReturnFixup) - { - body.BeginMarker("fixup", new KeyValuePair("type", "*result")); - body.Write(returnTypeName); - body.EndMarker("fixup"); - body.Write(" result"); - body.WriteSemicolon(); - body.WriteNewline(); - body.WriteIndentation(); - } + body.BeginMarker("fixup", new KeyValuePair("type", "*result")); + body.Write(returnTypeName); + body.EndMarker("fixup"); + body.Write(" result"); + body.WriteSemicolon(); + body.WriteNewline(); + body.WriteIndentation(); + } + if (returnType.Kind != CXTypeKind.CXType_Void) + { body.Write("return "); } @@ -2163,14 +2166,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) var alignment = Math.Max(recordDecl.TypeForDecl.Handle.AlignOf, 1); var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => Math.Max(fieldDecl.Type.Handle.AlignOf, 1)) : alignment; - StructLayoutAttribute layout = null; - if (alignment < maxAlignm) - { - layout = new StructLayoutAttribute(LayoutKind.Sequential) { - Pack = (int)alignment - }; - } - var accessSpecifier = GetAccessSpecifier(constantArray); var canonicalElementType = type.ElementType.CanonicalType; var isUnsafeElementType = @@ -2180,19 +2175,6 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) var name = GetArtificialFixedSizedBufferName(constantArray); var escapedName = EscapeName(name); - var desc = new StructDesc<(string Name, PInvokeGenerator This)> - { - AccessSpecifier = accessSpecifier, - CustomAttrGeneratorData = (name, this), - EscapedName = escapedName, - IsUnsafe = isUnsafeElementType, - Layout = layout, - WriteCustomAttrs = static _ => {}, - Location = constantArray.Location - }; - - _outputBuilder.BeginStruct(in desc); - var totalSize = Math.Max(type.Size, 1); var sizePerDimension = new List<(long index, long size)>() {(0, type.Size)}; @@ -2205,6 +2187,37 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) elementType = subConstantArrayType.ElementType; } + long alignment32 = -1; + long alignment64 = -1; + + GetTypeSize(constantArray, constantArray.Type, ref alignment32, ref alignment64, out var size32, + out var size64); + + if ((size32 == 0 || size64 == 0) && _testOutputBuilder != null) + { + AddDiagnostic(DiagnosticLevel.Info, $"{escapedName} (constant array field) has a size of 0", constantArray); + } + + var desc = new StructDesc<(string Name, PInvokeGenerator This)> { + AccessSpecifier = accessSpecifier, + CustomAttrGeneratorData = (name, this), + EscapedName = escapedName, + IsUnsafe = isUnsafeElementType, + Layout = new() { + Alignment32 = alignment32, + Alignment64 = alignment64, + Size32 = size32, + Size64 = size64, + Pack = alignment, + MaxFieldAlignment = maxAlignm, + Kind = LayoutKind.Sequential + }, + WriteCustomAttrs = static _ => { }, + Location = constantArray.Location + }; + + _outputBuilder.BeginStruct(in desc); + var firstFieldName = ""; for (long i = 0; i < totalSize; i++) @@ -2343,11 +2356,11 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) WriteCustomAttrs = static _ => {}, IsStatic = false, IsMemberFunction = true, + ReturnType = $"Span<{typeName}>", Location = constantArray.Location }; _outputBuilder.BeginFunctionOrDelegate(in function, ref _isMethodClassUnsafe); - _outputBuilder.WriteReturnType($"Span<{typeName}>"); _outputBuilder.BeginFunctionInnerPrototype("AsSpan"); if (type.Size == 1) @@ -2433,11 +2446,11 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro IsUnsafe = IsUnsafe(typedefDecl, functionProtoType), NativeTypeName = nativeTypeName, WriteCustomAttrs = static _ => {}, + ReturnType = returnTypeName, Location = typedefDecl.Location }; _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); - _outputBuilder.WriteReturnType(returnTypeName); _outputBuilder.BeginFunctionInnerPrototype(escapedName); Visit(typedefDecl.CursorChildren.OfType()); diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs index c0ed12fd..6acd859d 100644 --- a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs @@ -142,11 +142,7 @@ public void BeginFunctionOrDelegate( } _ = _sb.Append('>'); - } - - public void WriteReturnType(string typeString) - { - _ = _sb.Append(EscapeText(typeString)); + _ = _sb.Append(EscapeText(desc.ReturnType)); _ = _sb.Append(""); } @@ -252,15 +248,15 @@ public void BeginStruct(in StructDesc