diff --git a/.gitignore b/.gitignore index ac146a31..1c33a1f3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ obj/ ############################################################################### .vs/ *.csproj.user + +############################################################################### +# Rider +############################################################################### +.idea/ +*.DotSettings.user diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/AccessSpecifier.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/AccessSpecifier.cs new file mode 100644 index 00000000..7503d241 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/AccessSpecifier.cs @@ -0,0 +1,13 @@ +namespace ClangSharp.Abstractions +{ + public enum AccessSpecifier : byte + { + None, + Public, + Protected, + ProtectedInternal, + Internal, + PrivateProtected, + Private + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantDesc.cs new file mode 100644 index 00000000..a6431816 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantDesc.cs @@ -0,0 +1,11 @@ +namespace ClangSharp.Abstractions +{ + internal struct ConstantDesc + { + public AccessSpecifier AccessSpecifier { get; set; } + public string TypeName { get; set; } + public string EscapedName { get; set; } + public string NativeTypeName { get; set; } + public ConstantKind Kind { get; set; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantKind.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantKind.cs new file mode 100644 index 00000000..ba20d75c --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ConstantKind.cs @@ -0,0 +1,14 @@ +using System; + +namespace ClangSharp.Abstractions +{ + [Flags] + internal enum ConstantKind + { + None = 0, + ReadOnly = 1 << 0, + Enumerator = 1 << 1, + PrimitiveConstant = 1 << 2, + NonPrimitiveConstant = 1 << 3 + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs new file mode 100644 index 00000000..91e026df --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FieldDesc.cs @@ -0,0 +1,12 @@ +namespace ClangSharp.Abstractions +{ + internal struct FieldDesc + { + public AccessSpecifier AccessSpecifier { get; set; } + public string NativeTypeName { get; set; } + public string EscapedName { get; set; } + public int? Offset { get; set; } + public bool NeedsNewKeyword { get; set; } + public string InheritedFrom { get; set; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs new file mode 100644 index 00000000..8547992c --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/FunctionOrDelegateDesc.cs @@ -0,0 +1,115 @@ +using System; +using System.Runtime.InteropServices; + +namespace ClangSharp.Abstractions +{ + internal struct FunctionOrDelegateDesc + { + public AccessSpecifier AccessSpecifier { get; set; } + public string NativeTypeName { get; set; } + public string EscapedName { get; set; } + public string EntryPoint { get; set; } + public string LibraryPath { get; set; } + public CallingConvention CallingConvention { get; set; } + public FunctionOrDelegateFlags Flags { get; set; } + + public bool IsVirtual + { + get => (Flags & FunctionOrDelegateFlags.IsVirtual) != 0; + set => Flags = + value ? Flags | FunctionOrDelegateFlags.IsVirtual : Flags & ~FunctionOrDelegateFlags.IsVirtual; + } + + public bool IsDllImport + { + get => (Flags & FunctionOrDelegateFlags.IsDllImport) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsDllImport + : Flags & ~FunctionOrDelegateFlags.IsDllImport; + } + + public bool HasFnPtrCodeGen + { + get => (Flags & FunctionOrDelegateFlags.HasFnPtrCodeGen) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.HasFnPtrCodeGen + : Flags & ~FunctionOrDelegateFlags.HasFnPtrCodeGen; + } + + public bool IsAggressivelyInlined + { + get => (Flags & FunctionOrDelegateFlags.IsAggressivelyInlined) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsAggressivelyInlined + : Flags & ~FunctionOrDelegateFlags.IsAggressivelyInlined; + } + + public bool SetLastError + { + get => (Flags & FunctionOrDelegateFlags.SetLastError) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.SetLastError + : Flags & ~FunctionOrDelegateFlags.SetLastError; + } + + public bool IsCxx + { + get => (Flags & FunctionOrDelegateFlags.IsCxx) != 0; + set => Flags = value ? Flags | FunctionOrDelegateFlags.IsCxx : Flags & ~FunctionOrDelegateFlags.IsCxx; + } + + public bool NeedsNewKeyword + { + get => (Flags & FunctionOrDelegateFlags.NeedsNewKeyword) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.NeedsNewKeyword + : Flags & ~FunctionOrDelegateFlags.NeedsNewKeyword; + } + + public bool IsUnsafe + { + get => (Flags & FunctionOrDelegateFlags.IsUnsafe) != 0; + set => Flags = value ? Flags | FunctionOrDelegateFlags.IsUnsafe : Flags & ~FunctionOrDelegateFlags.IsUnsafe; + } + + public bool IsCtxCxxRecord + { + get => (Flags & FunctionOrDelegateFlags.IsCtxCxxRecord) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsCtxCxxRecord + : Flags & ~FunctionOrDelegateFlags.IsCtxCxxRecord; + } + + public bool IsCxxRecordCtxUnsafe + { + get => (Flags & FunctionOrDelegateFlags.IsCxxRecordCtxUnsafe) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsCxxRecordCtxUnsafe + : Flags & ~FunctionOrDelegateFlags.IsCxxRecordCtxUnsafe; + } + + public bool IsMemberFunction + { + get => (Flags & FunctionOrDelegateFlags.IsMemberFunction) != 0; + set => Flags = value + ? Flags | FunctionOrDelegateFlags.IsMemberFunction + : Flags & ~FunctionOrDelegateFlags.IsMemberFunction; + } + + public bool? IsStatic + { + get => (Flags & FunctionOrDelegateFlags.IsStatic) != 0 ? true : + (Flags & FunctionOrDelegateFlags.IsNotStatic) != 0 ? false : null; + set => Flags = value switch + { + // true - static, false - not static, null - infer + true => Flags | FunctionOrDelegateFlags.IsStatic & ~FunctionOrDelegateFlags.IsNotStatic, + false => Flags & ~FunctionOrDelegateFlags.IsStatic | FunctionOrDelegateFlags.IsNotStatic, + null => Flags & ~FunctionOrDelegateFlags.IsStatic & ~FunctionOrDelegateFlags.IsNotStatic + }; + } + + public Action WriteCustomAttrs { get; set; } + public TCustomAttrGeneratorData CustomAttrGeneratorData { get; set; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.Visit.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.Visit.cs new file mode 100644 index 00000000..bb0aac8a --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.Visit.cs @@ -0,0 +1,12 @@ +namespace ClangSharp.Abstractions +{ + internal partial interface IOutputBuilder + { + void WriteDivider(bool force = false); + void SuppressDivider(); + + void WriteCustomAttribute(string attribute); + void WriteIid(string iidName, string iidValue); + void EmitUsingDirective(string directive); + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs new file mode 100644 index 00000000..c2c58e33 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.VisitDecl.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using ClangSharp.CSharp; + +namespace ClangSharp.Abstractions +{ + internal partial interface IOutputBuilder + { + void BeginInnerValue(); + void EndInnerValue(); + + void BeginInnerCast(); + void WriteCastType(string targetTypeName); + void EndInnerCast(); + + void BeginUnchecked(); + void EndUnchecked(); + + void BeginConstant(in ConstantDesc desc); + void BeginConstantValue(bool isGetOnlyProperty = false); + void WriteConstantValue(long value); + void WriteConstantValue(ulong value); + void EndConstantValue(); + void EndConstant(bool isConstant); + + void BeginEnum(AccessSpecifier accessSpecifier, string typeName, string escapedName, string nativeTypeName); + void EndEnum(); + + void BeginField(in FieldDesc desc); + void WriteFixedCountField(string typeName, string escapedName, string fixedName, string count); + void WriteRegularField(string typeName, string escapedName); + void EndField(bool isBodyless = true); + + void BeginFunctionOrDelegate(in FunctionOrDelegateDesc info, + ref bool isMethodClassUnsafe); + void WriteReturnType(string typeString); + void BeginFunctionInnerPrototype(string escapedName); + void BeginParameter(in ParameterDesc info); + void BeginParameterDefault(); + void EndParameterDefault(); + void EndParameter(); + void WriteParameterSeparator(); + void EndFunctionInnerPrototype(); + void BeginConstructorInitializer(string memberRefName, string memberInitName); + void EndConstructorInitializer(); + void BeginBody(bool isExpressionBody = false); + void BeginConstructorInitializers(); + void EndConstructorInitializers(); + void BeginInnerFunctionBody(); + void EndInnerFunctionBody(); + void EndBody(bool isExpressionBody = false); + void EndFunctionOrDelegate(bool isVirtual, bool isBodyless); + + void BeginStruct(in StructDesc info); + void BeginExplicitVtbl(); + void EndExplicitVtbl(); + void EndStruct(); + + void EmitCompatibleCodeSupport(); + void EmitFnPtrSupport(); + void EmitSystemSupport(); + + CSharpOutputBuilder BeginCSharpCode(); + void EndCSharpCode(CSharpOutputBuilder output); + + void BeginGetter(bool aggressivelyInlined); + void EndGetter(); + void BeginSetter(bool aggressivelyInlined); + void EndSetter(); + + void BeginIndexer(AccessSpecifier accessSpecifier, bool isUnsafe); + void WriteIndexer(string typeName); + void BeginIndexerParameters(); + void EndIndexerParameters(); + void EndIndexer(); + + void BeginDereference(); + void EndDereference(); + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.cs new file mode 100644 index 00000000..75ed5187 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/IOutputBuilder.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ClangSharp.Abstractions +{ + internal partial interface IOutputBuilder + { + bool IsTestOutput { get; } + string Name { get; } + string Extension { get; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ParameterDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ParameterDesc.cs new file mode 100644 index 00000000..10f367e4 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ParameterDesc.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace ClangSharp.Abstractions +{ + internal struct ParameterDesc + { + public string Type { get; set; } + public string Name { get; set; } + public string NativeTypeName { get; set; } + public IEnumerable CppAttributes { get; set; } + public Action WriteCustomAttrs { get; set; } + public TCustomAttrGeneratorData CustomAttrGeneratorData { get; set; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs new file mode 100644 index 00000000..7c29684e --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructDesc.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.InteropServices; + +namespace ClangSharp.Abstractions +{ + internal struct StructDesc + { + public AccessSpecifier AccessSpecifier { get; set; } + public string EscapedName { get; set; } + public string NativeType { get; set; } + public string NativeInheritance { get; set; } + public StructLayoutAttribute Layout { get; set; } + public Guid? Uuid { get; set; } + public StructFlags Flags { get; set; } + + public bool IsUnsafe + { + get => (Flags & StructFlags.IsUnsafe) != 0; + set => Flags = value ? Flags | StructFlags.IsUnsafe : Flags & ~StructFlags.IsUnsafe; + } + + public bool HasVtbl + { + get => (Flags & StructFlags.HasVtbl) != 0; + set => Flags = value ? Flags | StructFlags.HasVtbl : Flags & ~StructFlags.HasVtbl; + } + + public Action WriteCustomAttrs { get; set; } + public TCustomAttrGeneratorData CustomAttrGeneratorData { get; set; } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs new file mode 100644 index 00000000..34991433 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/StructFlags.cs @@ -0,0 +1,11 @@ +using System; + +namespace ClangSharp.Abstractions +{ + [Flags] + public enum StructFlags + { + IsUnsafe = 1 << 0, + HasVtbl = 1 << 1 + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs new file mode 100644 index 00000000..dd0c12df --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs @@ -0,0 +1,67 @@ +namespace ClangSharp.CSharp +{ + internal partial class CSharpOutputBuilder + { + private bool _customAttrIsForParameter = false; + public void WriteCustomAttribute(string attribute) + { + if (attribute.Equals("Flags") || attribute.Equals("Obsolete")) + { + AddUsingDirective("System"); + } + else if (attribute.Equals("EditorBrowsable") || attribute.StartsWith("EditorBrowsable(")) + { + AddUsingDirective("System.ComponentModel"); + } + else if (attribute.StartsWith("Guid(")) + { + AddUsingDirective("System.Runtime.InteropServices"); + } + + if (!_customAttrIsForParameter) + { + WriteIndented('['); + Write(attribute); + WriteLine(']'); + } + else + { + Write('['); + Write(attribute); + Write(']'); + Write(' '); + } + } + + public void WriteDivider(bool force = false) + { + if (force) + { + WriteNewline(); + } + else + { + NeedsNewline = true; + } + } + + public void SuppressDivider() + { + NeedsNewline = false; + } + + public void WriteIid(string iidName, string iidValue) + { + AddUsingDirective("System"); + WriteIndented("public static readonly Guid "); + Write(iidName); + Write(" = new Guid("); + Write(iidValue); + Write(")"); + WriteSemicolon(); + WriteNewline(); + } + + public void EmitUsingDirective(string directive) => AddUsingDirective(directive); + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs new file mode 100644 index 00000000..c6893284 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs @@ -0,0 +1,572 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using ClangSharp.Abstractions; + +namespace ClangSharp.CSharp +{ + internal partial class CSharpOutputBuilder : IOutputBuilder + { + public void BeginInnerValue() => Write('('); + public void EndInnerValue() => Write(')'); + + public void BeginInnerCast() => Write('('); + public void WriteCastType(string targetTypeName) => Write(targetTypeName); + public void EndInnerCast() => Write(')'); + + public void BeginUnchecked() => Write("unchecked"); + public void EndUnchecked() + { + // nop, used only by XML + } + + public void BeginConstant(in ConstantDesc desc) + { + if (desc.NativeTypeName is not null) + { + AddNativeTypeNameAttribute(desc.NativeTypeName); + } + + WriteIndentation(); + + if ((desc.Kind & ConstantKind.PrimitiveConstant) != 0) + { + Write(desc.AccessSpecifier.AsString()); + Write(" const "); + Write(desc.TypeName); + Write(' '); + } + else if ((desc.Kind & ConstantKind.NonPrimitiveConstant) != 0) + { + Write(desc.AccessSpecifier.AsString()); + Write(" static "); + if ((desc.Kind & ConstantKind.ReadOnly) != 0) + { + Write("readonly "); + } + + Write(desc.TypeName); + Write(' '); + } + + Write(desc.EscapedName); + } + + public void BeginConstantValue(bool isGetOnlyProperty = false) + { + Write(isGetOnlyProperty ? " => " : " = "); + } + + public void WriteConstantValue(long value) => Write(value); + public void WriteConstantValue(ulong value) => Write(value); + + public void EndConstantValue() + { + // nop, used only by the XML backend + } + + public void EndConstant(bool isConstant) => WriteLine(isConstant ? ';' : ','); + + public void BeginEnum(AccessSpecifier accessSpecifier, string typeName, string escapedName, string nativeTypeName) + { + if (nativeTypeName is not null) + { + AddNativeTypeNameAttribute(nativeTypeName); + } + + WriteIndented(accessSpecifier.AsString()); + Write(" enum "); + Write(escapedName); + + if (!typeName.Equals("int")) + { + Write(" : "); + Write(typeName); + } + + NeedsNewline = true; + WriteBlockStart(); + } + + public void EndEnum() => WriteBlockEnd(); + + public void BeginField(in FieldDesc desc) + { + if (desc.Offset is not null) + { + WriteIndentedLine($"[FieldOffset({desc.Offset})]"); + } + + if (desc.NativeTypeName is not null) + { + AddNativeTypeNameAttribute(desc.NativeTypeName); + } + + WriteIndented(desc.AccessSpecifier.AsString()); + Write(' '); + + if (desc.NeedsNewKeyword) + { + Write("new "); + } + } + + public void WriteFixedCountField(string typeName, string escapedName, string fixedName, string count) + { + if (PInvokeGenerator.IsSupportedFixedSizedBufferType(typeName)) + { + Write("fixed "); + Write(typeName); + Write(' '); + Write(escapedName); + Write('['); + Write(count); + Write(']'); + } + else + { + Write(fixedName); + Write(' '); + Write(escapedName); + } + } + + public void WriteRegularField(string typeName, string escapedName) + { + Write(typeName); + Write(' '); + Write(escapedName); + } + + public void EndField(bool isBodyless = true) + { + if (isBodyless) + { + WriteSemicolon(); + WriteNewline(); + NeedsNewline = true; + } + } + + public void BeginFunctionOrDelegate( + in FunctionOrDelegateDesc desc, + ref bool isMethodClassUnsafe) + { + desc.WriteCustomAttrs(desc.CustomAttrGeneratorData); + if (desc.IsVirtual) + { + Debug.Assert(!desc.HasFnPtrCodeGen); + + AddUsingDirective("System.Runtime.InteropServices"); + WriteIndented("[UnmanagedFunctionPointer"); + + if (desc.CallingConvention != CallingConvention.Winapi) + { + Write("(CallingConvention."); + Write(desc.CallingConvention); + Write(')'); + } + + WriteLine(']'); + } + else if (desc.IsDllImport) + { + AddUsingDirective("System.Runtime.InteropServices"); + + WriteIndented("[DllImport("); + + Write('"'); + Write(desc.LibraryPath); + Write('"'); + + Write(", "); + + if (desc.CallingConvention != CallingConvention.Winapi) + { + Write("CallingConvention = CallingConvention."); + Write(desc.CallingConvention); + Write(", "); + } + + if (desc.EntryPoint != desc.EscapedName) + { + Write("EntryPoint = \""); + Write(desc.EntryPoint); + Write("\", "); + } + + Write("ExactSpelling = true"); + if (desc.SetLastError) + { + Write(", SetLastError = true"); + } + WriteLine(")]"); + } + + if (desc.IsAggressivelyInlined) + { + AddUsingDirective("System.Runtime.CompilerServices"); + WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); + } + + if (desc.NativeTypeName is not null) + { + AddNativeTypeNameAttribute(desc.NativeTypeName, attributePrefix: "return: "); + } + + WriteIndented(desc.AccessSpecifier.AsString()); + + if (!desc.IsMemberFunction) + { + if (desc.IsVirtual) + { + if (desc.IsUnsafe && !desc.IsCxxRecordCtxUnsafe) + { + Write(" unsafe"); + } + Write(" delegate"); + } + else if (desc.IsStatic ?? (desc.IsDllImport || !desc.IsCxx)) + { + Write(" static"); + + if (desc.IsDllImport) + { + Write(" extern"); + } + } + } + + Write(' '); + + if (!desc.IsVirtual) + { + //if (NeedsNewKeyword(escapedName, functionDecl.Parameters)) + if (desc.NeedsNewKeyword) + { + Write("new "); + } + + if (desc.IsUnsafe) + { + //if (cxxRecordDecl is null) + if (!desc.IsCtxCxxRecord) + { + isMethodClassUnsafe = true; + } + //else if (!IsUnsafe(cxxRecordDecl)) + else if (!desc.IsCxxRecordCtxUnsafe) + { + Write("unsafe "); + } + } + } + } + + public void WriteReturnType(string typeString) + { + Write(typeString); + Write(' '); + } + + public void BeginFunctionInnerPrototype(string escapedName) + { + Write(escapedName); + Write('('); + } + + public void BeginParameter(in ParameterDesc info) + { + if (info.NativeTypeName is not null) + { + AddNativeTypeNameAttribute(info.NativeTypeName, prefix: "", postfix: " "); + } + + if (info.CppAttributes is not null) + { + AddCppAttributes(info.CppAttributes, prefix: "", postfix: " "); + } + + _customAttrIsForParameter = true; + info.WriteCustomAttrs(info.CustomAttrGeneratorData); + _customAttrIsForParameter = false; + Write(info.Type); + Write(' '); + Write(info.Name); + } + + public void BeginParameterDefault() + { + Write(" = "); + } + + public void EndParameterDefault() + { + // nop, used only by XML + } + + public void EndParameter() + { + // nop, used only by XML + } + + public void WriteParameterSeparator() + { + Write(','); + Write(' '); + } + + public void EndFunctionInnerPrototype() + { + Write(')'); + } + + public void BeginConstructorInitializer(string memberRefName, string memberInitName) + { + WriteIndentation(); + if (memberRefName.Equals(memberInitName)) + { + Write("this"); + Write('.'); + } + + Write(memberRefName); + Write(' '); + Write('='); + Write(' '); + } + + public void EndConstructorInitializer() + { + WriteSemicolon(); + WriteNewline(); + } + + public void BeginBody(bool isExpressionBody = false) + { + if (isExpressionBody) + { + Write(" => "); + } + else + { + NeedsNewline = true; + WriteBlockStart(); + } + } + + public void BeginConstructorInitializers() + { + // nop, method only exists for consistency and/or future use + } + + public void EndConstructorInitializers() + { + // nop, method only exists for consistency and/or future use + } + + public void BeginInnerFunctionBody() + { + WriteIndentation(); + } + + public void EndInnerFunctionBody() + { + // nop, used only by XML + } + + public void EndBody(bool isExpressionBody = false) + { + WriteSemicolonIfNeeded(); + WriteNewlineIfNeeded(); + if (!isExpressionBody) + { + WriteBlockEnd(); + } + } + + public void EndFunctionOrDelegate(bool isVirtual, bool isBodyless) + { + if (isBodyless) + { + WriteSemicolon(); + WriteNewline(); + } + + NeedsNewline = true; + } + + public void BeginStruct(in StructDesc info) + { + if (info.Layout is not null) + { + AddUsingDirective("System.Runtime.InteropServices"); + WriteIndented("[StructLayout(LayoutKind."); + Write(info.Layout.Value); + if (info.Layout.Pack != default) + { + Write(", Pack = "); + Write(info.Layout.Pack); + } + + WriteLine(")]"); + } + + if (info.Uuid is not null) + { + AddUsingDirective("System.Runtime.InteropServices"); + + WriteIndented("[Guid(\""); + Write(info.Uuid.Value.ToString("D", CultureInfo.InvariantCulture).ToUpperInvariant()); + WriteLine("\")]"); + } + + if (info.NativeType is not null) + { + AddNativeTypeNameAttribute(info.NativeType); + } + + if (info.NativeInheritance is not null) + { + AddNativeInheritanceAttribute(info.NativeInheritance); + } + + WriteIndented(info.AccessSpecifier.AsString()); + Write(' '); + + if (info.IsUnsafe) + { + Write("unsafe "); + } + + Write("partial struct "); + Write(info.EscapedName); + WriteNewline(); + WriteBlockStart(); + } + + public void BeginExplicitVtbl() + { + NeedsNewline = true; + WriteIndentedLine("public partial struct Vtbl"); + WriteBlockStart(); + } + + public void EmitCompatibleCodeSupport() + { + AddUsingDirective("System.Runtime.CompilerServices"); + } + + public void EmitFnPtrSupport() + { + AddUsingDirective("System"); + AddUsingDirective("System.Runtime.InteropServices"); + } + + public void EmitSystemSupport() + { + AddUsingDirective("System"); + } + + public void EndStruct() + { + WriteBlockEnd(); + } + + public void EndExplicitVtbl() + { + WriteBlockEnd(); + } + + public CSharpOutputBuilder BeginCSharpCode() + { + // just write directly to this buffer + return this; + } + + public void EndCSharpCode(CSharpOutputBuilder output) + { + // nop, used only by XML + } + + public void BeginGetter(bool aggressivelyInlined) + { + if (aggressivelyInlined) + { + AddUsingDirective("System.Runtime.CompilerServices"); + WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); + } + + WriteIndentedLine("get"); + WriteBlockStart(); + } + + public void EndGetter() + { + WriteBlockEnd(); + } + + public void BeginSetter(bool aggressivelyInlined) + { + WriteNewline(); + if (aggressivelyInlined) + { + AddUsingDirective("System.Runtime.CompilerServices"); + WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); + } + + WriteIndentedLine("set"); + WriteBlockStart(); + } + + public void EndSetter() + { + WriteBlockEnd(); + NeedsNewline = false; + } + + public void BeginIndexer(AccessSpecifier accessSpecifier, bool isUnsafe) + { + NeedsNewline = true; + WriteIndented(accessSpecifier.AsString()); + Write(' '); + if (isUnsafe) + { + Write("unsafe "); + } + } + + public void WriteIndexer(string typeName) + { + Write(typeName); + Write(" this"); + } + + public void BeginIndexerParameters() + { + Write('['); + } + + public void EndIndexerParameters() + { + Write(']'); + } + + public void EndIndexer() + { + NeedsNewline = true; + } + + public void BeginDereference() + { + Write('&'); + } + + public void EndDereference() + { + // nop, used only by XML + } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs new file mode 100644 index 00000000..0b131455 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.cs @@ -0,0 +1,312 @@ +// 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.Collections.Generic; +using System.Text; + +namespace ClangSharp.CSharp +{ + internal sealed partial class CSharpOutputBuilder + { + public const string DefaultIndentationString = " "; + + private readonly string _name; + private readonly List _contents; + private readonly StringBuilder _currentLine; + private readonly SortedSet _usingDirectives; + private readonly SortedSet _staticUsingDirectives; + private readonly string _indentationString; + private readonly bool _isTestOutput; + + private int _indentationLevel; + private MarkerMode _markerMode; + + public CSharpOutputBuilder(string name, string indentationString = DefaultIndentationString, bool isTestOutput = false, MarkerMode markerMode = MarkerMode.None) + { + _name = name; + _contents = new List(); + _currentLine = new StringBuilder(); + _usingDirectives = new SortedSet(); + _staticUsingDirectives = new SortedSet(); + _indentationString = indentationString; + _isTestOutput = isTestOutput; + _markerMode = markerMode; + } + + public IEnumerable Contents => _contents; + + public string IndentationString => _indentationString; + + public bool IsTestOutput => _isTestOutput; + + public string Name => _name; + + public string Extension { get; } = ".cs"; + + public bool NeedsNewline { get; set; } + + public bool NeedsSemicolon { get; set; } + + public IEnumerable StaticUsingDirectives => _staticUsingDirectives; + + public IEnumerable UsingDirectives => _usingDirectives; + + public void AddUsingDirective(string namespaceName) + { + if (namespaceName.StartsWith("static ")) + { + _staticUsingDirectives.Add(namespaceName); + } + else + { + _usingDirectives.Add(namespaceName); + } + } + + public void DecreaseIndentation() + { + if (_indentationLevel == 0) + { + throw new InvalidOperationException(); + } + + _indentationLevel--; + } + + public void IncreaseIndentation() + { + _indentationLevel++; + } + + public void WriteBlockStart() + { + WriteIndentedLine('{'); + IncreaseIndentation(); + } + + public void WriteBlockEnd() + { + // We don't need a newline if immediately closing the scope + NeedsNewline = false; + + // We don't need a semicolon if immediately closing the scope + NeedsSemicolon = false; + + DecreaseIndentation(); + WriteIndentedLine('}'); + } + + public void Write(T value) + { + _currentLine.Append(value); + } + + public void WriteIndentation() + { + WriteNewlineIfNeeded(); + + for (var i = 0; i < _indentationLevel; i++) + { + _currentLine.Append(_indentationString); + } + } + + public void WriteIndented(T value) + { + WriteIndentation(); + Write(value); + } + + public void WriteIndentedLine(T value) + { + WriteIndentation(); + WriteLine(value); + } + + public void WriteLine(T value) + { + Write(value); + WriteNewline(); + } + + public void WriteNewline() + { + _contents.Add(_currentLine.ToString()); + _currentLine.Clear(); + NeedsNewline = false; + } + + public void WriteNewlineIfNeeded() + { + if (NeedsNewline) + { + WriteNewline(); + } + } + + public void WritePendingLine() + { + if (_currentLine.Length > 0) + { + WriteNewline(); + } + } + + public void WriteSemicolon() + { + Write(';'); + NeedsSemicolon = false; + NeedsNewline = true; + } + + public void WriteSemicolonIfNeeded() + { + if (NeedsSemicolon) + { + WriteSemicolon(); + } + } + + public void BeginMarker(string kind, params KeyValuePair[] attributes) + { + if (_markerMode != MarkerMode.Xml) + { + return; + } + + Write("/*M*/<"); + Write(kind); + foreach (var kvp in attributes) + { + Write(' '); + Write(kvp.Key); + Write('='); + Write('"'); + Write(kvp.Value); + Write('"'); + } + + Write("/*M*/>"); + } + + public void EndMarker(string kind) + { + if (_markerMode != MarkerMode.Xml) + { + return; + } + + Write("/*M*/"); + } + + private void AddCppAttributes(IEnumerable attrs, string prefix = null, string postfix = null) + { + var attributeList = string.Join("^", attrs); + if (string.IsNullOrWhiteSpace(attributeList)) + { + return; + } + + if (prefix is null) + { + WriteIndentation(); + } + else + { + WriteNewlineIfNeeded(); + Write(prefix); + } + + Write("[CppAttributeList(\""); + Write(attributeList); + Write("\")]"); + + if (postfix is null) + { + NeedsNewline = true; + } + else + { + Write(postfix); + } + } + + private void AddNativeInheritanceAttribute(string inheritedFromName, string prefix = null, string postfix = null, string attributePrefix = null) + { + if (prefix is null) + { + WriteIndentation(); + } + else + { + WriteNewlineIfNeeded(); + Write(prefix); + } + + Write('['); + + if (attributePrefix != null) + { + Write(attributePrefix); + } + + Write("NativeInheritance"); + Write('('); + + Write('"'); + Write(PInvokeGenerator.EscapeString(inheritedFromName)); + Write('"'); + Write(')'); + Write(']'); + + if (postfix is null) + { + NeedsNewline = true; + } + else + { + Write(postfix); + } + } + + private void AddNativeTypeNameAttribute(string nativeTypeName, string prefix = null, string postfix = null, string attributePrefix = null) + { + if (string.IsNullOrWhiteSpace(nativeTypeName)) + { + return; + } + + if (prefix is null) + { + WriteIndentation(); + } + else + { + WriteNewlineIfNeeded(); + Write(prefix); + } + + Write('['); + + if (attributePrefix != null) + { + Write(attributePrefix); + } + + Write("NativeTypeName(\""); + Write(PInvokeGenerator.EscapeString(nativeTypeName)); + Write("\")]"); + + if (postfix is null) + { + NeedsNewline = true; + } + else + { + Write(postfix); + } + } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/MarkerMode.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/MarkerMode.cs new file mode 100644 index 00000000..e6249153 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/MarkerMode.cs @@ -0,0 +1,9 @@ +namespace ClangSharp.CSharp +{ + internal enum MarkerMode + { + None, + Xml + // TODO Future prospect: Add an option to output the markers as C# comments? + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj b/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj index 2398c8cd..f61f748d 100644 --- a/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj +++ b/sources/ClangSharp.PInvokeGenerator/ClangSharp.PInvokeGenerator.csproj @@ -4,6 +4,7 @@ ClangSharp net5.0;netcoreapp3.1;netstandard2.0 + 9 diff --git a/sources/ClangSharp.PInvokeGenerator/Extensions/StringExtensions.cs b/sources/ClangSharp.PInvokeGenerator/Extensions/StringExtensions.cs new file mode 100644 index 00000000..540a26f1 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/Extensions/StringExtensions.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; +using ClangSharp.Abstractions; + +namespace ClangSharp +{ + internal static class StringExtensions + { + public static string Unquote(this string str) + => str.StartsWith("\"") && str.EndsWith("\"") && !str.EndsWith("\\\"") + ? str.Substring(1, str.Length - 2) + : str; + + public static string AsString(this AccessSpecifier value) => value switch + { + AccessSpecifier.Public => "public", + AccessSpecifier.Protected => "protected", + AccessSpecifier.ProtectedInternal => "protected internal", + AccessSpecifier.Internal => "internal", + AccessSpecifier.PrivateProtected => "private protected", + AccessSpecifier.Private => "private", + _ => "public" + }; + + public static string AsString(this CallingConvention value, bool isForFnPtr) => value switch + { + CallingConvention.Winapi => "Winapi", + CallingConvention.Cdecl => "Cdecl", + CallingConvention.StdCall => isForFnPtr ? "Stdcall" : "StdCall", + CallingConvention.ThisCall => isForFnPtr ? "Thiscall" : "ThisCall", + CallingConvention.FastCall => isForFnPtr ? "Fastcall" : "FastCall", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) + }; + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs new file mode 100644 index 00000000..ec6f5af3 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/FunctionOrDelegateFlags.cs @@ -0,0 +1,22 @@ +using System; + +namespace ClangSharp +{ + [Flags] + public enum FunctionOrDelegateFlags + { + IsVirtual = 1 << 0, + IsDllImport = 1 << 1, + HasFnPtrCodeGen = 1 << 2, + IsAggressivelyInlined = 1 << 3, + SetLastError = 1 << 4, + IsCxx = 1 << 5, + NeedsNewKeyword = 1 << 6, + IsUnsafe = 1 << 7, + IsCtxCxxRecord = 1 << 8, + IsCxxRecordCtxUnsafe = 1 << 9, + IsMemberFunction = 1 << 10, + IsStatic = 1 << 11, + IsNotStatic = 1 << 12 + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/OutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/OutputBuilder.cs deleted file mode 100644 index 0df4b608..00000000 --- a/sources/ClangSharp.PInvokeGenerator/OutputBuilder.cs +++ /dev/null @@ -1,158 +0,0 @@ -// 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.Collections.Generic; -using System.Text; - -namespace ClangSharp -{ - public sealed class OutputBuilder - { - public const string DefaultIndentationString = " "; - - private readonly string _name; - private readonly List _contents; - private readonly StringBuilder _currentLine; - private readonly SortedSet _usingDirectives; - private readonly SortedSet _staticUsingDirectives; - private readonly string _indentationString; - private readonly bool _isTestOutput; - - private int _indentationLevel; - - public OutputBuilder(string name, string indentationString = DefaultIndentationString, bool isTestOutput = false) - { - _name = name; - _contents = new List(); - _currentLine = new StringBuilder(); - _usingDirectives = new SortedSet(); - _staticUsingDirectives = new SortedSet(); - _indentationString = indentationString; - _isTestOutput = isTestOutput; - } - - public IEnumerable Contents => _contents; - - public string IndentationString => _indentationString; - - public bool IsTestOutput => _isTestOutput; - - public string Name => _name; - - public bool NeedsNewline { get; set; } - - public bool NeedsSemicolon { get; set; } - - public IEnumerable StaticUsingDirectives => _staticUsingDirectives; - - public IEnumerable UsingDirectives => _usingDirectives; - - public void AddUsingDirective(string namespaceName) - { - if (namespaceName.StartsWith("static ")) - { - _staticUsingDirectives.Add(namespaceName); - } - else - { - _usingDirectives.Add(namespaceName); - } - } - - public void DecreaseIndentation() - { - if (_indentationLevel == 0) - { - throw new InvalidOperationException(); - } - - _indentationLevel--; - } - - public void IncreaseIndentation() - { - _indentationLevel++; - } - - public void WriteBlockStart() - { - WriteIndentedLine('{'); - IncreaseIndentation(); - } - - public void WriteBlockEnd() - { - // We don't need a newline if immediately closing the scope - NeedsNewline = false; - - // We don't need a semicolon if immediately closing the scope - NeedsSemicolon = false; - - DecreaseIndentation(); - WriteIndentedLine('}'); - } - - public void Write(T value) - { - _currentLine.Append(value); - } - - public void WriteIndentation() - { - WriteNewlineIfNeeded(); - - for (var i = 0; i < _indentationLevel; i++) - { - _currentLine.Append(_indentationString); - } - } - - public void WriteIndented(T value) - { - WriteIndentation(); - Write(value); - } - - public void WriteIndentedLine(T value) - { - WriteIndentation(); - WriteLine(value); - } - - public void WriteLine(T value) - { - Write(value); - WriteNewline(); - } - - public void WriteNewline() - { - _contents.Add(_currentLine.ToString()); - _currentLine.Clear(); - NeedsNewline = false; - } - - public void WriteNewlineIfNeeded() - { - if (NeedsNewline) - { - WriteNewline(); - } - } - - public void WriteSemicolon() - { - Write(';'); - NeedsSemicolon = false; - NeedsNewline = true; - } - - public void WriteSemicolonIfNeeded() - { - if (NeedsSemicolon) - { - WriteSemicolon(); - } - } - } -} diff --git a/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs b/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs index 4dc1c50f..47cee0e6 100644 --- a/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs +++ b/sources/ClangSharp.PInvokeGenerator/OutputBuilderFactory.cs @@ -2,38 +2,62 @@ using System; using System.Collections.Generic; +using ClangSharp.Abstractions; +using ClangSharp.CSharp; +using ClangSharp.XML; namespace ClangSharp { - public sealed class OutputBuilderFactory + internal sealed class OutputBuilderFactory { - private readonly Dictionary _outputBuilders; + private readonly PInvokeGeneratorOutputMode _mode; + private readonly Dictionary _outputBuilders; - public OutputBuilderFactory() + public OutputBuilderFactory(PInvokeGeneratorOutputMode mode) { - _outputBuilders = new Dictionary(); + _mode = mode; + _outputBuilders = new Dictionary(); } - public IEnumerable OutputBuilders => _outputBuilders.Values; + public IEnumerable OutputBuilders => _outputBuilders.Values; public void Clear() { _outputBuilders.Clear(); } - public OutputBuilder Create(string name, bool isTestOutput = false) + public IOutputBuilder Create(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } - var outputBuilder = new OutputBuilder(name, isTestOutput: isTestOutput); + var outputBuilder = _mode switch + { + PInvokeGeneratorOutputMode.CSharp => (IOutputBuilder) new CSharpOutputBuilder(name), + PInvokeGeneratorOutputMode.Xml => new XmlOutputBuilder(name), + _ => throw new ArgumentOutOfRangeException() + }; + + _outputBuilders.Add(name, outputBuilder); + return outputBuilder; + } + + public CSharpOutputBuilder CreateTests(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + var outputBuilder = new CSharpOutputBuilder(name, isTestOutput: true); + _outputBuilders.Add(name, outputBuilder); return outputBuilder; } - public OutputBuilder GetOutputBuilder(string name) + public IOutputBuilder GetOutputBuilder(string name) { if (string.IsNullOrWhiteSpace(name)) { @@ -42,7 +66,22 @@ public OutputBuilder GetOutputBuilder(string name) return _outputBuilders[name]; } - public bool TryGetOutputBuilder(string name, out OutputBuilder outputBuilder) + public CSharpOutputBuilder GetTestOutputBuilder(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (_outputBuilders[name] is CSharpOutputBuilder csharpOutputBuilder && csharpOutputBuilder.IsTestOutput) + { + return csharpOutputBuilder; + } + + throw new ArgumentException("A test output builder was not found with the given name", nameof(name)); + } + + public bool TryGetOutputBuilder(string name, out IOutputBuilder outputBuilder) { if (string.IsNullOrWhiteSpace(name)) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index efe8e0a9..40309bb2 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -5,7 +5,9 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices; using System.Text; +using ClangSharp.Abstractions; using ClangSharp.Interop; namespace ClangSharp @@ -14,12 +16,17 @@ public partial class PInvokeGenerator { private void VisitClassTemplateDecl(ClassTemplateDecl classTemplateDecl) { - AddDiagnostic(DiagnosticLevel.Warning, $"Class templates are not supported: '{GetCursorQualifiedName(classTemplateDecl)}'. Generated bindings may be incomplete.", classTemplateDecl); + AddDiagnostic(DiagnosticLevel.Warning, + $"Class templates are not supported: '{GetCursorQualifiedName(classTemplateDecl)}'. Generated bindings may be incomplete.", + classTemplateDecl); } - private void VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl classTemplateSpecializationDecl) + private void VisitClassTemplateSpecializationDecl( + ClassTemplateSpecializationDecl classTemplateSpecializationDecl) { - AddDiagnostic(DiagnosticLevel.Warning, $"Class template specializations are not supported: '{GetCursorQualifiedName(classTemplateSpecializationDecl)}'. Generated bindings may be incomplete.", classTemplateSpecializationDecl); + AddDiagnostic(DiagnosticLevel.Warning, + $"Class template specializations are not supported: '{GetCursorQualifiedName(classTemplateSpecializationDecl)}'. Generated bindings may be incomplete.", + classTemplateSpecializationDecl); } private void VisitDecl(Decl decl) @@ -224,7 +231,8 @@ private void VisitDecl(Decl decl) default: { - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported declaration: '{decl.Kind}'. Generated bindings may be incomplete.", decl); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported declaration: '{decl.Kind}'. Generated bindings may be incomplete.", decl); break; } } @@ -232,7 +240,7 @@ private void VisitDecl(Decl decl) private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) { - var accessSpecifier = string.Empty; + var accessSpecifier = AccessSpecifier.None; var name = GetRemappedCursorName(enumConstantDecl); var escapedName = EscapeName(name); var isAnonymousEnum = false; @@ -244,52 +252,55 @@ private void VisitEnumConstantDecl(EnumConstantDecl enumConstantDecl) if (GetRemappedCursorName(enumDecl).StartsWith("__AnonymousEnum_")) { isAnonymousEnum = true; - accessSpecifier = GetAccessSpecifierName(enumDecl); + accessSpecifier = GetAccessSpecifier(enumDecl); } + typeName = GetRemappedTypeName(enumDecl, context: null, enumDecl.IntegerType, out var nativeTypeName); } else { - typeName = GetRemappedTypeName(enumConstantDecl, context: null, enumConstantDecl.Type, out var nativeTypeName); + typeName = GetRemappedTypeName(enumConstantDecl, context: null, enumConstantDecl.Type, + out var nativeTypeName); } - _outputBuilder.WriteIndentation(); - - if (isAnonymousEnum) + var desc = new ConstantDesc { - _outputBuilder.Write(accessSpecifier); - _outputBuilder.Write(" const "); - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - } + AccessSpecifier = accessSpecifier, + TypeName = typeName, + EscapedName = escapedName, + NativeTypeName = null, + Kind = isAnonymousEnum ? ConstantKind.PrimitiveConstant : ConstantKind.Enumerator + }; - _outputBuilder.Write(escapedName); + _outputBuilder.BeginConstant(in desc); if (enumConstantDecl.InitExpr != null) { - _outputBuilder.Write(" = "); + _outputBuilder.BeginConstantValue(); UncheckStmt(typeName, enumConstantDecl.InitExpr); + _outputBuilder.EndConstantValue(); } else if (isAnonymousEnum) { - _outputBuilder.Write(" = "); - + _outputBuilder.BeginConstantValue(); if (IsUnsigned(typeName)) { - _outputBuilder.Write(enumConstantDecl.UnsignedInitVal); + _outputBuilder.WriteConstantValue(enumConstantDecl.UnsignedInitVal); } else { - _outputBuilder.Write(enumConstantDecl.InitVal); + _outputBuilder.WriteConstantValue(enumConstantDecl.InitVal); } + + _outputBuilder.EndConstantValue(); } - _outputBuilder.WriteLine(isAnonymousEnum ? ';' : ','); + _outputBuilder.EndConstant(isAnonymousEnum); } private void VisitEnumDecl(EnumDecl enumDecl) { - var accessSpecifier = GetAccessSpecifierName(enumDecl); + var accessSpecifier = GetAccessSpecifier(enumDecl); var name = GetRemappedCursorName(enumDecl); var escapedName = EscapeName(name); var isAnonymousEnum = false; @@ -304,21 +315,10 @@ private void VisitEnumDecl(EnumDecl enumDecl) { if (!isAnonymousEnum) { - var typeName = GetRemappedTypeName(enumDecl, context: null, enumDecl.IntegerType, out var nativeTypeName); - AddNativeTypeNameAttribute(nativeTypeName); - - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(" enum "); - _outputBuilder.Write(escapedName); - - if (!typeName.Equals("int")) - { - _outputBuilder.Write(" : "); - _outputBuilder.Write(typeName); - } + var typeName = GetRemappedTypeName(enumDecl, context: null, enumDecl.IntegerType, + out var nativeTypeName); - _outputBuilder.NeedsNewline = true; - _outputBuilder.WriteBlockStart(); + _outputBuilder.BeginEnum(accessSpecifier, typeName, escapedName, nativeTypeName); } Visit(enumDecl.Enumerators); @@ -326,7 +326,7 @@ private void VisitEnumDecl(EnumDecl enumDecl) if (!isAnonymousEnum) { - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndEnum(); } } StopUsingOutputBuilder(); @@ -339,66 +339,50 @@ private void VisitFieldDecl(FieldDecl fieldDecl) return; } - var accessSpecifier = GetAccessSpecifierName(fieldDecl); + var accessSpecifier = GetAccessSpecifier(fieldDecl); var name = GetRemappedCursorName(fieldDecl); var escapedName = EscapeName(name); var type = fieldDecl.Type; var typeName = GetRemappedTypeName(fieldDecl, context: null, type, out var nativeTypeName); + int? offset = null; if (fieldDecl.Parent.IsUnion) { - _outputBuilder.WriteIndentedLine("[FieldOffset(0)]"); + offset = 0; } - AddNativeTypeNameAttribute(nativeTypeName); - - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); - if (NeedsNewKeyword(name)) + var desc = new FieldDesc { - _outputBuilder.Write("new "); - } + AccessSpecifier = accessSpecifier, + NativeTypeName = nativeTypeName, + EscapedName = escapedName, + Offset = offset, + NeedsNewKeyword = NeedsNewKeyword(name) + }; + + _outputBuilder.BeginField(in desc); if (type.CanonicalType is ConstantArrayType constantArrayType) { - if (IsSupportedFixedSizedBufferType(typeName)) - { - _outputBuilder.Write("fixed "); - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - _outputBuilder.Write(escapedName); - _outputBuilder.Write('['); - _outputBuilder.Write(Math.Max(constantArrayType.Size, 1)); - - var elementType = constantArrayType.ElementType; - - while (elementType.CanonicalType is ConstantArrayType subConstantArrayType) - { - _outputBuilder.Write(" * "); - _outputBuilder.Write(Math.Max(subConstantArrayType.Size, 1)); - - elementType = subConstantArrayType.ElementType; - } - - _outputBuilder.Write(']'); - } - else + var count = Math.Max(constantArrayType.Size, 1).ToString(); + var elementType = constantArrayType.ElementType; + while (elementType.CanonicalType is ConstantArrayType subConstantArrayType) { - _outputBuilder.Write(GetArtificialFixedSizedBufferName(fieldDecl)); - _outputBuilder.Write(' '); - _outputBuilder.Write(escapedName); + count += " * "; + count += Math.Max(subConstantArrayType.Size, 1).ToString(); + elementType = subConstantArrayType.ElementType; } + + _outputBuilder.WriteFixedCountField(typeName, escapedName, GetArtificialFixedSizedBufferName(fieldDecl), + count); } else { - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - _outputBuilder.Write(escapedName); + _outputBuilder.WriteRegularField(typeName, escapedName); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.EndField(); } private void VisitFunctionDecl(FunctionDecl functionDecl) @@ -408,94 +392,19 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) return; } - var accessSppecifier = GetAccessSpecifierName(functionDecl); + var accessSppecifier = GetAccessSpecifier(functionDecl); var name = GetRemappedCursorName(functionDecl); - if (!(functionDecl.DeclContext is CXXRecordDecl cxxRecordDecl)) - { - cxxRecordDecl = null; - StartUsingOutputBuilder(_config.MethodClassName); - } - - WithAttributes("*"); - WithAttributes(name); - - WithUsings("*"); - WithUsings(name); - - var type = functionDecl.Type; - var callConv = CXCallingConv.CXCallingConv_Invalid; - - if (type is AttributedType attributedType) - { - type = attributedType.ModifiedType; - callConv = attributedType.Handle.FunctionTypeCallingConv; - } - - if (type is FunctionType functionType) - { - if (callConv == CXCallingConv.CXCallingConv_Invalid) - { - callConv = functionType.CallConv; - } - } - var cxxMethodDecl = functionDecl as CXXMethodDecl; var body = functionDecl.Body; var isVirtual = (cxxMethodDecl != null) && cxxMethodDecl.IsVirtual; var escapedName = isVirtual ? PrefixAndStripName(name) : EscapeAndStripName(name); - if (isVirtual) - { - Debug.Assert(!_config.GeneratePreviewCodeFnptr); - - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - - var callingConventionName = GetCallingConventionName(functionDecl, callConv, name, isForFnptr: false); - - _outputBuilder.WriteIndented("[UnmanagedFunctionPointer"); - - if (callingConventionName != "Winapi") - { - _outputBuilder.Write("(CallingConvention."); - _outputBuilder.Write(callingConventionName); - _outputBuilder.Write(')'); - } - - _outputBuilder.WriteLine(']'); - } - else if (body is null) + if (!(functionDecl.DeclContext is CXXRecordDecl cxxRecordDecl)) { - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - - _outputBuilder.WriteIndented("[DllImport("); - - WithLibraryPath(name); - - _outputBuilder.Write(", "); - - var callingConventionName = GetCallingConventionName(functionDecl, callConv, name, isForFnptr: false); - - if (callingConventionName != "Winapi") - { - _outputBuilder.Write("CallingConvention = CallingConvention."); - _outputBuilder.Write(callingConventionName); - _outputBuilder.Write(", "); - } - - var entryPoint = (cxxMethodDecl is null) ? GetCursorName(functionDecl) : cxxMethodDecl.Handle.Mangling.CString; - - if (entryPoint != escapedName) - { - _outputBuilder.Write("EntryPoint = \""); - _outputBuilder.Write(entryPoint); - _outputBuilder.Write("\", "); - } - - _outputBuilder.Write("ExactSpelling = true"); - WithSetLastError(name); - _outputBuilder.WriteLine(")]"); + cxxRecordDecl = null; + StartUsingOutputBuilder(_config.MethodClassName); } var returnType = functionDecl.ReturnType; @@ -508,62 +417,68 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) nativeTypeName = string.IsNullOrWhiteSpace(nativeTypeName) ? "bool" : nativeTypeName; } - AddNativeTypeNameAttribute(nativeTypeName, attributePrefix: "return: "); - - _outputBuilder.WriteIndented(accessSppecifier); + var type = functionDecl.Type; + var callConv = CXCallingConv.CXCallingConv_Invalid; - if (isVirtual) + if (type is AttributedType attributedType) { - _outputBuilder.Write(" delegate"); + type = attributedType.ModifiedType; + callConv = attributedType.Handle.FunctionTypeCallingConv; } - else if ((body is null) || (cxxMethodDecl is null) || cxxMethodDecl.IsStatic) - { - _outputBuilder.Write(" static"); - if (body is null) + if (type is FunctionType functionType) + { + if (callConv == CXCallingConv.CXCallingConv_Invalid) { - _outputBuilder.Write(" extern"); + callConv = functionType.CallConv; } } - _outputBuilder.Write(' '); + var callingConventionName = GetCallingConvention(functionDecl, callConv, name); + var entryPoint = !isVirtual && body is null + ? (cxxMethodDecl is null) ? GetCursorName(functionDecl) : cxxMethodDecl.Handle.Mangling.CString + : null; + var isDllImport = body is null && !isVirtual; - if (!isVirtual) + var desc = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> { - if (NeedsNewKeyword(escapedName, functionDecl.Parameters)) - { - _outputBuilder.Write("new "); - } - - if (IsUnsafe(functionDecl)) - { - if (cxxRecordDecl is null) - { - _isMethodClassUnsafe = true; - } - else if (!IsUnsafe(cxxRecordDecl)) - { - _outputBuilder.Write("unsafe "); - } - } - } + AccessSpecifier = accessSppecifier, + NativeTypeName = nativeTypeName, + EscapedName = escapedName, + EntryPoint = entryPoint, + CallingConvention = callingConventionName, + LibraryPath = isDllImport ? GetLibraryPath(name).Unquote() : null, + IsVirtual = isVirtual, + IsDllImport = isDllImport, + HasFnPtrCodeGen = _config.GeneratePreviewCodeFnptr, + SetLastError = GetSetLastError(name), + IsCxx = cxxMethodDecl is not null, + IsStatic = isDllImport || (cxxMethodDecl?.IsStatic ?? true), + NeedsNewKeyword = NeedsNewKeyword(escapedName, functionDecl.Parameters), + IsUnsafe = IsUnsafe(functionDecl), + IsCtxCxxRecord = cxxRecordDecl is not null, + IsCxxRecordCtxUnsafe = cxxRecordDecl is not null && IsUnsafe(cxxRecordDecl), + WriteCustomAttrs = static x => + { + x.This.WithAttributes("*"); + x.This.WithAttributes(x.Name); + + x.This.WithUsings("*"); + x.This.WithUsings(x.Name); + }, + CustomAttrGeneratorData = (name, this) + }; + + _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); var needsReturnFixup = isVirtual && NeedsReturnFixup(cxxMethodDecl); if (!(functionDecl is CXXConstructorDecl)) { - _outputBuilder.Write(returnTypeName); - - if (needsReturnFixup) - { - _outputBuilder.Write('*'); - } - - _outputBuilder.Write(' '); + _outputBuilder.WriteReturnType(needsReturnFixup ? $"{returnTypeName}*" : returnTypeName); } - _outputBuilder.Write(escapedName); - _outputBuilder.Write('('); + _outputBuilder.BeginFunctionInnerPrototype(escapedName); if (isVirtual) { @@ -575,39 +490,56 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) } var cxxRecordDeclName = GetRemappedCursorName(thisCursor); - _outputBuilder.Write(EscapeName(cxxRecordDeclName)); - _outputBuilder.Write("* pThis"); + var cxxRecordEscapedName = EscapeName(cxxRecordDeclName); + ParameterDesc<(string Name, PInvokeGenerator This)> parameterDesc = new() + { + Name = "pThis", + Type = $"{cxxRecordEscapedName}*", + WriteCustomAttrs = static x => { }, + CustomAttrGeneratorData = default + }; + + _outputBuilder.BeginParameter(in parameterDesc); + _outputBuilder.EndParameter(); if (needsReturnFixup) { - _outputBuilder.Write(", "); - _outputBuilder.Write(returnTypeName); - _outputBuilder.Write("* _result"); + _outputBuilder.WriteParameterSeparator(); + parameterDesc = new() + { + Name = "_result", + Type = $"{returnTypeName}*", + WriteCustomAttrs = static x => { }, + CustomAttrGeneratorData = default + }; + _outputBuilder.BeginParameter(in parameterDesc); + _outputBuilder.EndParameter(); } if (functionDecl.Parameters.Any()) { - _outputBuilder.Write(", "); + _outputBuilder.WriteParameterSeparator(); } } Visit(functionDecl.Parameters); - _outputBuilder.Write(')'); + _outputBuilder.EndFunctionInnerPrototype(); if ((body is null) || isVirtual) { - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.EndFunctionOrDelegate(isVirtual, true); } else { - _outputBuilder.NeedsNewline = true; + int firstCtorInitializer = functionDecl.Parameters.Any() + ? (functionDecl.CursorChildren.IndexOf(functionDecl.Parameters.Last()) + 1) + : 0; + int lastCtorInitializer = (functionDecl.Body != null) + ? functionDecl.CursorChildren.IndexOf(functionDecl.Body) + : functionDecl.CursorChildren.Count; - int firstCtorInitializer = functionDecl.Parameters.Any() ? (functionDecl.CursorChildren.IndexOf(functionDecl.Parameters.Last()) + 1) : 0; - int lastCtorInitializer = (functionDecl.Body != null) ? functionDecl.CursorChildren.IndexOf(functionDecl.Body) : functionDecl.CursorChildren.Count; - - _outputBuilder.WriteBlockStart(); + _outputBuilder.BeginBody(); if (functionDecl is CXXConstructorDecl cxxConstructorDecl) { @@ -616,19 +548,20 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) if (body is CompoundStmt compoundStmt) { + _outputBuilder.BeginConstructorInitializers(); VisitStmts(compoundStmt.Body); + _outputBuilder.EndConstructorInitializers(); } else { - _outputBuilder.WriteIndentation(); + _outputBuilder.BeginInnerFunctionBody(); Visit(body); + _outputBuilder.EndInnerFunctionBody(); } - _outputBuilder.WriteSemicolonIfNeeded(); - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndBody(); + _outputBuilder.EndFunctionOrDelegate(isVirtual, false); } - _outputBuilder.NeedsNewline = true; Visit(functionDecl.Decls, excludedCursors: functionDecl.Parameters); @@ -637,7 +570,8 @@ private void VisitFunctionDecl(FunctionDecl functionDecl) StopUsingOutputBuilder(); } - void VisitCtorInitializers(CXXConstructorDecl cxxConstructorDecl, int firstCtorInitializer, int lastCtorInitializer) + void VisitCtorInitializers(CXXConstructorDecl cxxConstructorDecl, int firstCtorInitializer, + int lastCtorInitializer) { for (int i = firstCtorInitializer; i < lastCtorInitializer; i++) { @@ -657,36 +591,28 @@ void VisitCtorInitializers(CXXConstructorDecl cxxConstructorDecl, int firstCtorI var memberRefName = GetRemappedCursorName(memberRef.Referenced); var memberInitName = memberInit.Spelling; - if ((memberInit is CastExpr castExpr) && (castExpr.SubExprAsWritten is DeclRefExpr declRefExpr)) + if ((memberInit is CastExpr {SubExprAsWritten: DeclRefExpr declRefExpr})) { memberInitName = GetRemappedCursorName(declRefExpr.Decl); } - _outputBuilder.WriteIndentation(); - - if (memberRefName.Equals(memberInitName)) - { - _outputBuilder.Write("this"); - _outputBuilder.Write('.'); - } - Visit(memberRef); - _outputBuilder.Write(' '); - _outputBuilder.Write('='); - _outputBuilder.Write(' '); + _outputBuilder.BeginConstructorInitializer(memberRefName, memberInitName); - var memberRefTypeName = GetRemappedTypeName(memberRef, context: null, memberRef.Type, out var memberRefNativeTypeName); + var memberRefTypeName = GetRemappedTypeName(memberRef, context: null, memberRef.Type, + out var memberRefNativeTypeName); UncheckStmt(memberRefTypeName, memberInit); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.EndConstructorInitializer(); } } } private void VisitFunctionTemplateDecl(FunctionTemplateDecl functionTemplateDecl) { - AddDiagnostic(DiagnosticLevel.Warning, $"Function templates are not supported: '{GetCursorQualifiedName(functionTemplateDecl)}'. Generated bindings may be incomplete.", functionTemplateDecl); + AddDiagnostic(DiagnosticLevel.Warning, + $"Function templates are not supported: '{GetCursorQualifiedName(functionTemplateDecl)}'. Generated bindings may be incomplete.", + functionTemplateDecl); } private void VisitLinkageSpecDecl(LinkageSpecDecl linkageSpecDecl) @@ -726,7 +652,9 @@ private void VisitParmVarDecl(ParmVarDecl parmVarDecl) else { IsPrevContextDecl(out var previousContext); - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported parameter variable declaration parent: '{previousContext.CursorKindSpelling}'. Generated bindings may be incomplete.", previousContext); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported parameter variable declaration parent: '{previousContext.CursorKindSpelling}'. Generated bindings may be incomplete.", + previousContext); } void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) @@ -734,42 +662,52 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) var type = parmVarDecl.Type; var typeName = GetRemappedTypeName(parmVarDecl, context: null, type, out var nativeTypeName); - if ((((functionDecl is CXXMethodDecl cxxMethodDecl) && cxxMethodDecl.IsVirtual) || (functionDecl.Body is null)) && (typeName == "bool")) + if (((functionDecl is CXXMethodDecl {IsVirtual: true}) || (functionDecl.Body is null)) && + (typeName == "bool")) { // bool is not blittable, so we shouldn't use it for P/Invoke signatures typeName = "byte"; nativeTypeName = string.IsNullOrWhiteSpace(nativeTypeName) ? "bool" : nativeTypeName; } - AddNativeTypeNameAttribute(nativeTypeName, prefix: "", postfix: " "); - AddCppAttributes(parmVarDecl, prefix: "", postfix: " "); - - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - var name = GetRemappedCursorName(parmVarDecl); var escapedName = EscapeName(name); - _outputBuilder.Write(escapedName); - var parameters = functionDecl.Parameters; var index = parameters.IndexOf(parmVarDecl); var lastIndex = parameters.Count - 1; if (name.Equals("param")) { - _outputBuilder.Write(index); + escapedName += index; } + var desc = new ParameterDesc<(string Name, PInvokeGenerator This)> + { + Name = escapedName, + Type = typeName, + NativeTypeName = nativeTypeName, + CppAttributes = _config.GenerateNativeInheritanceAttribute + ? parmVarDecl.Attrs.Select(x => EscapeString(x.Spelling)) + : null, + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => { } + }; + + _outputBuilder.BeginParameter(in desc); + if (parmVarDecl.HasDefaultArg) { - _outputBuilder.Write(" = "); + _outputBuilder.BeginParameterDefault(); UncheckStmt(typeName, parmVarDecl.DefaultArg); + _outputBuilder.EndParameterDefault(); } + _outputBuilder.EndParameter(); + if (index != lastIndex) { - _outputBuilder.Write(", "); + _outputBuilder.WriteParameterSeparator(); } } @@ -777,14 +715,9 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl) { var type = parmVarDecl.Type; var typeName = GetRemappedTypeName(parmVarDecl, context: null, type, out var nativeTypeName); - AddNativeTypeNameAttribute(nativeTypeName, prefix: "", postfix: " "); - AddCppAttributes(parmVarDecl, prefix: "", postfix: " "); - - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); var name = GetRemappedCursorName(parmVarDecl); - _outputBuilder.Write(EscapeName(name)); + var escapedName = EscapeName(name); var parameters = typedefDecl.CursorChildren.OfType().ToList(); var index = parameters.IndexOf(parmVarDecl); @@ -792,18 +725,35 @@ void ForTypedefDecl(ParmVarDecl parmVarDecl, TypedefDecl typedefDecl) if (name.Equals("param")) { - _outputBuilder.Write(index); + escapedName += index; } + var desc = new ParameterDesc<(string Name, PInvokeGenerator This)> + { + Name = escapedName, + Type = typeName, + NativeTypeName = nativeTypeName, + CppAttributes = _config.GenerateNativeInheritanceAttribute + ? parmVarDecl.Attrs.Select(x => EscapeString(x.Spelling)) + : null, + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => { } + }; + + _outputBuilder.BeginParameter(in desc); + if (parmVarDecl.HasDefaultArg) { - _outputBuilder.Write(" = "); + _outputBuilder.BeginParameterDefault(); UncheckStmt(typeName, parmVarDecl.DefaultArg); + _outputBuilder.EndParameterDefault(); } + _outputBuilder.EndParameter(); + if (index != lastIndex) { - _outputBuilder.Write(", "); + _outputBuilder.WriteParameterSeparator(); } } } @@ -825,9 +775,12 @@ private void VisitRecordDecl(RecordDecl recordDecl) } var alignment = recordDecl.TypeForDecl.Handle.AlignOf; - var maxAlignm = recordDecl.Fields.Any() ? recordDecl.Fields.Max((fieldDecl) => fieldDecl.Type.Handle.AlignOf) : alignment; + var maxAlignm = recordDecl.Fields.Any() + ? recordDecl.Fields.Max((fieldDecl) => fieldDecl.Type.Handle.AlignOf) + : alignment; - if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && !(recordDecl.DeclContext is RecordDecl)) + if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && + !(recordDecl.DeclContext is RecordDecl)) { _testOutputBuilder.WriteIndented("/// Provides validation of the Validates that the of the Validates that the of the struct is correct."); @@ -919,6 +861,7 @@ private void VisitRecordDecl(RecordDecl recordDecl) } } + string nativeNameWithExtras = null, nativeInheritance = null; if ((cxxRecordDecl != null) && cxxRecordDecl.Bases.Any()) { var nativeTypeNameBuilder = new StringBuilder(); @@ -937,37 +880,46 @@ private void VisitRecordDecl(RecordDecl recordDecl) nativeTypeNameBuilder.Append(baseName); } - AddNativeTypeNameAttribute(nativeTypeNameBuilder.ToString()); - AddNativeInheritanceAttribute(GetCursorName(cxxRecordDecl.Bases.Last().Referenced)); + nativeNameWithExtras = nativeTypeNameBuilder.ToString(); + nativeInheritance = GetCursorName(cxxRecordDecl.Bases.Last().Referenced); } - _outputBuilder.WriteIndented(GetAccessSpecifierName(recordDecl)); - _outputBuilder.Write(' '); - - if (IsUnsafe(recordDecl)) + var desc = new StructDesc<(string Name, PInvokeGenerator This)> { - _outputBuilder.Write("unsafe "); - } - - _outputBuilder.Write("partial struct "); - _outputBuilder.Write(escapedName); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockStart(); + AccessSpecifier = GetAccessSpecifier(recordDecl), + EscapedName = escapedName, + IsUnsafe = IsUnsafe(recordDecl), + HasVtbl = hasVtbl, + Layout = layout, + Uuid = nullableUuid, + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => { }, + NativeType = nativeNameWithExtras, + NativeInheritance = _config.GenerateNativeInheritanceAttribute ? nativeInheritance : null + }; + _outputBuilder.BeginStruct(in desc); if (hasVtbl) { + var fieldDesc = new FieldDesc + { + AccessSpecifier = AccessSpecifier.Public, + NativeTypeName = null, + EscapedName = "lpVtbl", + Offset = null, + NeedsNewKeyword = false + }; + _outputBuilder.BeginField(in fieldDesc); if (_config.GenerateExplicitVtbls) { - _outputBuilder.WriteIndented("public Vtbl* lpVtbl"); + _outputBuilder.WriteRegularField("Vtbl*", "lpVtbl"); } else { - _outputBuilder.WriteIndented("public void** lpVtbl"); + _outputBuilder.WriteRegularField("void**", "lpVtbl"); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.NeedsNewline = true; + _outputBuilder.EndField(); } if (cxxRecordDecl != null) @@ -978,24 +930,30 @@ private void VisitRecordDecl(RecordDecl recordDecl) if (HasFields(baseCxxRecordDecl)) { - _outputBuilder.WriteIndented(GetAccessSpecifierName(baseCxxRecordDecl)); - _outputBuilder.Write(' '); - _outputBuilder.Write(GetRemappedCursorName(baseCxxRecordDecl)); - _outputBuilder.Write(' '); - + var parent = GetRemappedCursorName(baseCxxRecordDecl); var baseFieldName = GetAnonymousName(cxxBaseSpecifier, "Base"); - baseFieldName = GetRemappedName(baseFieldName, cxxBaseSpecifier, tryRemapOperatorName: true); + baseFieldName = GetRemappedName(baseFieldName, cxxBaseSpecifier, + tryRemapOperatorName: true); - _outputBuilder.Write(baseFieldName); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - - _outputBuilder.NeedsNewline = true; + var fieldDesc = new FieldDesc + { + AccessSpecifier = GetAccessSpecifier(baseCxxRecordDecl), + NativeTypeName = null, + EscapedName = baseFieldName, + Offset = null, + NeedsNewKeyword = false, + InheritedFrom = parent + }; + + _outputBuilder.BeginField(in fieldDesc); + _outputBuilder.WriteRegularField(parent, baseFieldName); + _outputBuilder.EndField(); } } } - if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && !(recordDecl.DeclContext is RecordDecl)) + if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && + !(recordDecl.DeclContext is RecordDecl)) { _testOutputBuilder.WriteIndented("/// Validates that the ()); + OutputDelegateSignatures(cxxRecordDecl, cxxRecordDecl, + hitsPerName: new Dictionary()); } } @@ -1138,7 +1100,8 @@ private void VisitRecordDecl(RecordDecl recordDecl) Visit(recordDecl.Decls, excludedCursors); - foreach (var constantArray in recordDecl.Fields.Where((field) => field.Type.CanonicalType is ConstantArrayType)) + foreach (var constantArray in recordDecl.Fields.Where((field) => + field.Type.CanonicalType is ConstantArrayType)) { VisitConstantArrayFieldDecl(recordDecl, constantArray); } @@ -1147,31 +1110,30 @@ private void VisitRecordDecl(RecordDecl recordDecl) { if (!_config.GenerateCompatibleCode) { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); + _outputBuilder.EmitCompatibleCodeSupport(); } if (!_config.GeneratePreviewCodeFnptr) { - _outputBuilder.AddUsingDirective("System"); - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); + _outputBuilder.EmitFnPtrSupport(); } int index = 0; - OutputVtblHelperMethods(cxxRecordDecl, cxxRecordDecl, ref index, hitsPerName: new Dictionary()); + OutputVtblHelperMethods(cxxRecordDecl, cxxRecordDecl, ref index, + hitsPerName: new Dictionary()); if (_config.GenerateExplicitVtbls) { - _outputBuilder.NeedsNewline = true; - _outputBuilder.WriteIndentedLine("public partial struct Vtbl"); - _outputBuilder.WriteBlockStart(); + _outputBuilder.BeginExplicitVtbl(); OutputVtblEntries(cxxRecordDecl, cxxRecordDecl, hitsPerName: new Dictionary()); - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndExplicitVtbl(); } } - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndStruct(); - if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && !(recordDecl.DeclContext is RecordDecl)) + if ((_testOutputBuilder != null) && !recordDecl.IsAnonymousStructOrUnion && + !(recordDecl.DeclContext is RecordDecl)) { _testOutputBuilder.WriteBlockEnd(); } @@ -1208,7 +1170,8 @@ bool HasFields(RecordDecl recordDecl) foreach (var decl in recordDecl.Decls) { - if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion && HasFields(nestedRecordDecl)) + if ((decl is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion && + HasFields(nestedRecordDecl)) { return true; } @@ -1230,7 +1193,8 @@ bool HasFields(RecordDecl recordDecl) return false; } - void OutputDelegateSignatures(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, Dictionary hitsPerName) + void OutputDelegateSignatures(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, + Dictionary hitsPerName) { foreach (var cxxBaseSpecifier in cxxRecordDecl.Bases) { @@ -1247,7 +1211,7 @@ void OutputDelegateSignatures(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxx if (!_config.GeneratePreviewCodeFnptr) { - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); var remappedName = FixupNameForMultipleHits(cxxMethodDecl, hitsPerName); Debug.Assert(CurrentContext == rootCxxRecordDecl); @@ -1267,18 +1231,20 @@ void OutputMethods(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl) foreach (var cxxMethodDecl in cxxRecordDecl.Methods) { - if (cxxMethodDecl.IsVirtual || (cxxMethodDecl is CXXConstructorDecl) || (cxxMethodDecl is CXXDestructorDecl)) + if (cxxMethodDecl.IsVirtual || (cxxMethodDecl is CXXConstructorDecl) || + (cxxMethodDecl is CXXDestructorDecl)) { continue; } Debug.Assert(CurrentContext == rootCxxRecordDecl); Visit(cxxMethodDecl); - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); } } - void OutputVtblEntries(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, Dictionary hitsPerName) + void OutputVtblEntries(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, + Dictionary hitsPerName) { foreach (var cxxBaseSpecifier in cxxRecordDecl.Bases) { @@ -1293,44 +1259,44 @@ void OutputVtblEntries(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordD foreach (var cxxMethodDecl in cxxMethodDecls) { OutputVtblEntry(rootCxxRecordDecl, cxxMethodDecl, hitsPerName); - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); } } } - void OutputVtblEntry(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl, Dictionary hitsPerName) + void OutputVtblEntry(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl, + Dictionary hitsPerName) { if (!cxxMethodDecl.IsVirtual || IsExcluded(cxxMethodDecl)) { return; } - var cxxMethodDeclTypeName = GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, cxxMethodDecl.Type, out var nativeTypeName); - AddNativeTypeNameAttribute(nativeTypeName); + var cxxMethodDeclTypeName = GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, cxxMethodDecl.Type, + out var nativeTypeName); - var accessSpecifier = GetAccessSpecifierName(cxxMethodDecl); + var accessSpecifier = GetAccessSpecifier(cxxMethodDecl); var remappedName = FixupNameForMultipleHits(cxxMethodDecl, hitsPerName); var name = GetRemappedCursorName(cxxMethodDecl); + var escapedName = EscapeAndStripName(name); RestoreNameForMultipleHits(cxxMethodDecl, hitsPerName, remappedName); - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); - - if (NeedsNewKeyword(remappedName)) + var desc = new FieldDesc { - _outputBuilder.Write("new "); - } - - _outputBuilder.Write(cxxMethodDeclTypeName); - _outputBuilder.Write(' '); - - _outputBuilder.Write(EscapeAndStripName(name)); + AccessSpecifier = accessSpecifier, + NativeTypeName = nativeTypeName, + EscapedName = escapedName, + Offset = null, + NeedsNewKeyword = NeedsNewKeyword(remappedName) + }; - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.BeginField(in desc); + _outputBuilder.WriteRegularField(cxxMethodDeclTypeName, escapedName); + _outputBuilder.EndField(); } - void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl, ref int vtblIndex, Dictionary hitsPerName) + void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethodDecl, ref int vtblIndex, + Dictionary hitsPerName) { if (!cxxMethodDecl.IsVirtual) { @@ -1343,58 +1309,59 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod { vtblIndex += 1; } + return; } var currentContext = _context.AddLast(cxxMethodDecl); - - if (_config.GenerateAggressiveInlining) - { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } - - var accessSpecifier = GetAccessSpecifierName(cxxMethodDecl); + var accessSpecifier = GetAccessSpecifier(cxxMethodDecl); var returnType = cxxMethodDecl.ReturnType; - var returnTypeName = GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, returnType, out var nativeTypeName); - AddNativeTypeNameAttribute(nativeTypeName, attributePrefix: "return: "); - - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); + var returnTypeName = + GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, returnType, out var nativeTypeName); var remappedName = FixupNameForMultipleHits(cxxMethodDecl, hitsPerName); var name = GetRemappedCursorName(cxxMethodDecl); RestoreNameForMultipleHits(cxxMethodDecl, hitsPerName, remappedName); - if (NeedsNewKeyword(remappedName, cxxMethodDecl.Parameters)) - { - _outputBuilder.Write("new"); - _outputBuilder.Write(' '); - } + var desc = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> + { + AccessSpecifier = accessSpecifier, + CustomAttrGeneratorData = (name, this), + IsAggressivelyInlined = _config.GenerateAggressiveInlining, + EscapedName = EscapeAndStripName(remappedName), + WriteCustomAttrs = static _ => {}, + IsMemberFunction = true, + NativeTypeName = nativeTypeName, + NeedsNewKeyword = NeedsNewKeyword(remappedName, cxxMethodDecl.Parameters), + HasFnPtrCodeGen = _config.GeneratePreviewCodeFnptr, + IsCtxCxxRecord = true, + IsCxxRecordCtxUnsafe = IsUnsafe(cxxRecordDecl), + IsUnsafe = true + }; - _outputBuilder.Write(returnTypeName); - _outputBuilder.Write(' '); - _outputBuilder.Write(EscapeAndStripName(remappedName)); - _outputBuilder.Write('('); + _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); + _outputBuilder.WriteReturnType(returnTypeName); + _outputBuilder.BeginFunctionInnerPrototype(desc.EscapedName); Visit(cxxMethodDecl.Parameters); - _outputBuilder.WriteLine(')'); - _outputBuilder.WriteBlockStart(); + _outputBuilder.EndFunctionInnerPrototype(); + _outputBuilder.BeginBody(); var needsReturnFixup = false; var cxxRecordDeclName = GetRemappedCursorName(cxxRecordDecl); var escapedCXXRecordDeclName = EscapeName(cxxRecordDeclName); - _outputBuilder.WriteIndentation(); + _outputBuilder.BeginInnerFunctionBody(); + var body = _outputBuilder.BeginCSharpCode(); if (_config.GenerateCompatibleCode) { - _outputBuilder.Write("fixed ("); - _outputBuilder.Write(escapedCXXRecordDeclName); - _outputBuilder.WriteLine("* pThis = &this)"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndentation(); + body.Write("fixed ("); + body.Write(escapedCXXRecordDeclName); + body.WriteLine("* pThis = &this)"); + body.WriteBlockStart(); + body.WriteIndentation(); } if (returnType.Kind != CXTypeKind.CXType_Void) @@ -1403,116 +1370,135 @@ void OutputVtblHelperMethod(CXXRecordDecl cxxRecordDecl, CXXMethodDecl cxxMethod if (needsReturnFixup) { - _outputBuilder.Write(returnTypeName); - _outputBuilder.Write(" result"); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteIndentation(); + body.BeginMarker("fixup", new KeyValuePair("type", "*result")); + body.Write(returnTypeName); + body.EndMarker("fixup"); + body.Write(" result"); + body.WriteSemicolon(); + body.WriteNewline(); + body.WriteIndentation(); } - _outputBuilder.Write("return "); + body.Write("return "); } if (needsReturnFixup) { - _outputBuilder.Write('*'); + body.Write('*'); } if (!_config.GeneratePreviewCodeFnptr) { - _outputBuilder.Write("Marshal.GetDelegateForFunctionPointer<"); - _outputBuilder.Write(PrefixAndStripName(name)); - _outputBuilder.Write(">("); + body.Write("Marshal.GetDelegateForFunctionPointer<"); + body.BeginMarker("delegate"); + body.Write(PrefixAndStripName(name)); + body.EndMarker("delegate"); + body.Write(">("); } if (_config.GenerateExplicitVtbls) { - _outputBuilder.Write("lpVtbl->"); - _outputBuilder.Write(EscapeAndStripName(name)); + body.Write("lpVtbl->"); + body.BeginMarker("vtbl", new KeyValuePair("explicit", true)); + body.Write(EscapeAndStripName(name)); + body.EndMarker("vtbl"); } else { - var cxxMethodDeclTypeName = GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, cxxMethodDecl.Type, out var _); + var cxxMethodDeclTypeName = + GetRemappedTypeName(cxxMethodDecl, cxxRecordDecl, cxxMethodDecl.Type, out var _); if (_config.GeneratePreviewCodeFnptr) { - _outputBuilder.Write('('); + body.Write('('); } - _outputBuilder.Write('('); - _outputBuilder.Write(cxxMethodDeclTypeName); - _outputBuilder.Write(")(lpVtbl["); - _outputBuilder.Write(vtblIndex); - _outputBuilder.Write("])"); + body.Write('('); + body.Write(cxxMethodDeclTypeName); + body.Write(")(lpVtbl["); + body.BeginMarker("vtbl", new KeyValuePair("explicit", false)); + body.Write(vtblIndex); + body.EndMarker("vtbl"); + body.Write("])"); if (_config.GeneratePreviewCodeFnptr) { - _outputBuilder.Write(')'); + body.Write(')'); } } if (!_config.GeneratePreviewCodeFnptr) { - _outputBuilder.Write(')'); + body.Write(')'); } - _outputBuilder.Write('('); + body.Write('('); + body.BeginMarker("param", new KeyValuePair("special", "thisPtr")); if (_config.GenerateCompatibleCode) { - _outputBuilder.Write("pThis"); + body.Write("pThis"); } else { - _outputBuilder.Write('('); - _outputBuilder.Write(escapedCXXRecordDeclName); - _outputBuilder.Write("*)Unsafe.AsPointer(ref this)"); + body.Write('('); + body.Write(escapedCXXRecordDeclName); + body.Write("*)Unsafe.AsPointer(ref this)"); } + body.EndMarker("param"); if (needsReturnFixup) { - _outputBuilder.Write(", &result"); + body.BeginMarker("param", new KeyValuePair("special", "retFixup")); + body.Write(", &result"); + body.EndMarker("param"); } var parmVarDecls = cxxMethodDecl.Parameters; for (int index = 0; index < parmVarDecls.Count; index++) { - _outputBuilder.Write(", "); + body.Write(", "); var parmVarDeclName = GetRemappedCursorName(parmVarDecls[index]); var escapedParmVarDeclName = EscapeName(parmVarDeclName); - _outputBuilder.Write(escapedParmVarDeclName); - if (parmVarDeclName.Equals("param")) { - _outputBuilder.Write(index); + escapedParmVarDeclName += index; } + + body.BeginMarker("param", new KeyValuePair("name", escapedParmVarDeclName)); + body.Write(escapedParmVarDeclName); + body.EndMarker("param"); } - _outputBuilder.Write(')'); + body.Write(')'); if (returnTypeName == "bool") { - _outputBuilder.Write(" != 0"); + body.Write(" != 0"); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + body.WriteSemicolon(); + body.WriteNewline(); if (_config.GenerateCompatibleCode) { - _outputBuilder.WriteBlockEnd(); + body.WriteBlockEnd(); } - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndCSharpCode(body); + _outputBuilder.EndInnerFunctionBody(); + _outputBuilder.EndBody(); + _outputBuilder.EndFunctionOrDelegate(false, false); vtblIndex += 1; Debug.Assert(_context.Last == currentContext); _context.RemoveLast(); } - void OutputVtblHelperMethods(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, ref int index, Dictionary hitsPerName) + void OutputVtblHelperMethods(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxRecordDecl, ref int index, + Dictionary hitsPerName) { foreach (var cxxBaseSpecifier in cxxRecordDecl.Bases) { @@ -1525,12 +1511,13 @@ void OutputVtblHelperMethods(CXXRecordDecl rootCxxRecordDecl, CXXRecordDecl cxxR foreach (var cxxMethodDecl in cxxMethodDecls) { - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); OutputVtblHelperMethod(rootCxxRecordDecl, cxxMethodDecl, ref index, hitsPerName); } } - void RestoreNameForMultipleHits(CXXMethodDecl cxxMethodDecl, Dictionary hitsPerName, string remappedName) + void RestoreNameForMultipleHits(CXXMethodDecl cxxMethodDecl, Dictionary hitsPerName, + string remappedName) { if (hitsPerName[remappedName] != 1) { @@ -1567,33 +1554,36 @@ void VisitAnonymousRecordDecl(RecordDecl recordDecl, RecordDecl nestedRecordDecl if (suffixLength != 0) { - nestedRecordDeclFieldName = nestedRecordDeclFieldName.Substring(1, nestedRecordDeclFieldName.Length - suffixLength); + nestedRecordDeclFieldName = + nestedRecordDeclFieldName.Substring(1, nestedRecordDeclFieldName.Length - suffixLength); } } - var nestedRecordDeclName = GetRemappedTypeName(nestedRecordDecl, context: null, nestedRecordDecl.TypeForDecl, out string nativeTypeName); + var nestedRecordDeclName = GetRemappedTypeName(nestedRecordDecl, context: null, + nestedRecordDecl.TypeForDecl, out string nativeTypeName); - if (recordDecl.IsUnion) + var desc = new FieldDesc { - _outputBuilder.WriteIndentedLine("[FieldOffset(0)]"); - } - AddNativeTypeNameAttribute(nativeTypeName); + AccessSpecifier = AccessSpecifier.Public, + NativeTypeName = nativeTypeName, + EscapedName = nestedRecordDeclFieldName, + Offset = recordDecl.IsUnion ? 0 : null, + NeedsNewKeyword = false + }; - _outputBuilder.WriteIndented("public "); - _outputBuilder.Write(nestedRecordDeclName); - _outputBuilder.Write(' '); - _outputBuilder.Write(nestedRecordDeclFieldName); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.NeedsNewline = true; + _outputBuilder.BeginField(in desc); + _outputBuilder.WriteRegularField(nestedRecordDeclName, nestedRecordDeclFieldName); + _outputBuilder.EndField(); if (!recordDecl.IsAnonymousStructOrUnion) { - VisitAnonymousRecordDeclFields(recordDecl, nestedRecordDecl, nestedRecordDeclName, nestedRecordDeclFieldName); + VisitAnonymousRecordDeclFields(recordDecl, nestedRecordDecl, nestedRecordDeclName, + nestedRecordDeclFieldName); } } - void VisitAnonymousRecordDeclFields(RecordDecl rootRecordDecl, RecordDecl anonymousRecordDecl, string contextType, string contextName) + void VisitAnonymousRecordDeclFields(RecordDecl rootRecordDecl, RecordDecl anonymousRecordDecl, + string contextType, string contextName) { if (_config.ExcludeAnonymousFieldHelpers) { @@ -1606,175 +1596,181 @@ void VisitAnonymousRecordDeclFields(RecordDecl rootRecordDecl, RecordDecl anonym { var type = fieldDecl.Type; - var accessSpecifier = GetAccessSpecifierName(anonymousRecordDecl); + var accessSpecifier = GetAccessSpecifier(anonymousRecordDecl); var typeName = GetRemappedTypeName(fieldDecl, context: null, type, out var fieldNativeTypeName); var name = GetRemappedCursorName(fieldDecl); var escapedName = EscapeName(name); - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); + var desc = new FieldDesc + { + AccessSpecifier = accessSpecifier, + NativeTypeName = null, + EscapedName = escapedName, + Offset = null, + NeedsNewKeyword = false + }; + + _outputBuilder.WriteDivider(true); + _outputBuilder.BeginField(in desc); var isFixedSizedBuffer = (type.CanonicalType is ConstantArrayType); var generateCompatibleCode = _config.GenerateCompatibleCode; + var typeString = string.Empty; if (!fieldDecl.IsBitField && (!isFixedSizedBuffer || generateCompatibleCode)) { - _outputBuilder.Write("ref "); + typeString = "ref "; } if (type.CanonicalType is RecordType recordType) { var recordDecl = recordType.Decl; - while ((recordDecl.DeclContext is RecordDecl parentRecordDecl) && (parentRecordDecl != rootRecordDecl)) + while ((recordDecl.DeclContext is RecordDecl parentRecordDecl) && + (parentRecordDecl != rootRecordDecl)) { var parentRecordDeclName = GetRemappedCursorName(parentRecordDecl); var escapedParentRecordDeclName = EscapeName(parentRecordDeclName); - _outputBuilder.Write(escapedParentRecordDeclName); - _outputBuilder.Write('.'); + typeString += escapedParentRecordDeclName + '.'; recordDecl = parentRecordDecl; } } - var isSupportedFixedSizedBufferType = isFixedSizedBuffer && IsSupportedFixedSizedBufferType(typeName); + var isSupportedFixedSizedBufferType = + isFixedSizedBuffer && IsSupportedFixedSizedBufferType(typeName); if (isFixedSizedBuffer) { if (!generateCompatibleCode) { - _outputBuilder.AddUsingDirective("System"); - _outputBuilder.Write("Span<"); + _outputBuilder.EmitSystemSupport(); + typeString += "Span<"; } - else if(!isSupportedFixedSizedBufferType) + else if (!isSupportedFixedSizedBufferType) { - _outputBuilder.Write(contextType); - _outputBuilder.Write('.'); + typeString += contextType + '.'; typeName = GetArtificialFixedSizedBufferName(fieldDecl); } } - _outputBuilder.Write(typeName); - + typeString += typeName; if (isFixedSizedBuffer && !generateCompatibleCode) { - _outputBuilder.Write('>'); + typeString += '>'; } - _outputBuilder.Write(' '); - _outputBuilder.Write(escapedName); + _outputBuilder.WriteRegularField(typeString, escapedName); - generateCompatibleCode |= ((type.CanonicalType is PointerType) || (type.CanonicalType is ReferenceType)) && ((typeName != "IntPtr") && (typeName != "UIntPtr")); - - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockStart(); - - if (_config.GenerateAggressiveInlining) - { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } + generateCompatibleCode |= + ((type.CanonicalType is PointerType) || (type.CanonicalType is ReferenceType)) && + ((typeName != "IntPtr") && (typeName != "UIntPtr")); - _outputBuilder.WriteIndentedLine("get"); - _outputBuilder.WriteBlockStart(); + _outputBuilder.BeginBody(); + _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining); + var code = _outputBuilder.BeginCSharpCode(); if (fieldDecl.IsBitField) { - _outputBuilder.WriteIndented("return "); - _outputBuilder.Write(contextName); - _outputBuilder.Write('.'); - _outputBuilder.Write(escapedName); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); - - _outputBuilder.WriteNewline(); - - if (_config.GenerateAggressiveInlining) - { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } - - _outputBuilder.WriteIndentedLine("set"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented(contextName); - _outputBuilder.Write('.'); - _outputBuilder.Write(escapedName); - _outputBuilder.Write(" = value"); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + code.WriteIndented("return "); + code.Write(contextName); + code.Write('.'); + code.Write(escapedName); + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); + + _outputBuilder.EndGetter(); + + _outputBuilder.BeginSetter(_config.GenerateAggressiveInlining); + + code = _outputBuilder.BeginCSharpCode(); + code.WriteIndented(contextName); + code.Write('.'); + code.Write(escapedName); + code.Write(" = value"); + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); + + _outputBuilder.EndSetter(); } else if (generateCompatibleCode) { - _outputBuilder.WriteIndented("fixed ("); - _outputBuilder.Write(contextType); - _outputBuilder.Write("* pField = &"); - _outputBuilder.Write(contextName); - _outputBuilder.WriteLine(')'); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented("return ref pField->"); - _outputBuilder.Write(escapedName); + code.WriteIndented("fixed ("); + code.Write(contextType); + code.Write("* pField = &"); + code.Write(contextName); + code.WriteLine(')'); + code.WriteBlockStart(); + code.WriteIndented("return ref pField->"); + code.Write(escapedName); if (isSupportedFixedSizedBufferType) { - _outputBuilder.Write("[0]"); + code.Write("[0]"); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); + code.WriteSemicolon(); + code.WriteNewline(); + code.WriteBlockEnd(); + _outputBuilder.EndCSharpCode(code); + + _outputBuilder.EndGetter(); } else { - _outputBuilder.WriteIndented("return "); + code.WriteIndented("return "); if (!isFixedSizedBuffer) { - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - _outputBuilder.Write("ref MemoryMarshal.GetReference("); + code.AddUsingDirective("System.Runtime.InteropServices"); + code.Write("ref MemoryMarshal.GetReference("); } if (!isFixedSizedBuffer || isSupportedFixedSizedBufferType) { - _outputBuilder.Write("MemoryMarshal.CreateSpan(ref "); + code.Write("MemoryMarshal.CreateSpan(ref "); } - _outputBuilder.Write(contextName); - _outputBuilder.Write('.'); - _outputBuilder.Write(escapedName); + code.Write(contextName); + code.Write('.'); + code.Write(escapedName); if (isFixedSizedBuffer) { if (isSupportedFixedSizedBufferType) { - _outputBuilder.Write("[0], "); - _outputBuilder.Write(((ConstantArrayType)type.CanonicalType).Size); + code.Write("[0], "); + code.Write(((ConstantArrayType)type.CanonicalType).Size); } else { - _outputBuilder.Write(".AsSpan("); + code.Write(".AsSpan("); } } else { - _outputBuilder.Write(", 1)"); + code.Write(", 1)"); } - _outputBuilder.Write(')'); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - } + code.Write(')'); + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); - _outputBuilder.WriteBlockEnd(); - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndGetter(); + } - _outputBuilder.NeedsNewline = true; + _outputBuilder.EndBody(); + _outputBuilder.EndField(false); + _outputBuilder.WriteDivider(); } else if ((declaration is RecordDecl nestedRecordDecl) && nestedRecordDecl.IsAnonymousStructOrUnion) { - var nestedRecordDeclName = GetRemappedTypeName(nestedRecordDecl, context: null, nestedRecordDecl.TypeForDecl, out string nativeTypeName); + var nestedRecordDeclName = GetRemappedTypeName(nestedRecordDecl, context: null, + nestedRecordDecl.TypeForDecl, out string nativeTypeName); var name = GetRemappedCursorName(nestedRecordDecl); if (name.StartsWith("_")) @@ -1795,19 +1791,20 @@ void VisitAnonymousRecordDeclFields(RecordDecl rootRecordDecl, RecordDecl anonym name = name.Substring(1, name.Length - suffixLength); } } + var escapedName = EscapeName(name); - VisitAnonymousRecordDeclFields(rootRecordDecl, nestedRecordDecl, $"{contextType}.{nestedRecordDeclName}", $"{contextName}.{escapedName}"); + VisitAnonymousRecordDeclFields(rootRecordDecl, nestedRecordDecl, + $"{contextType}.{nestedRecordDeclName}", $"{contextName}.{escapedName}"); } } } - void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, string contextName, ref int index, ref long previousSize, ref long remainingBits) + void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, string contextName, + ref int index, ref long previousSize, ref long remainingBits) { Debug.Assert(fieldDecl.IsBitField); - var outputBuilder = _outputBuilder; - var type = fieldDecl.Type; var typeName = GetRemappedTypeName(fieldDecl, context: null, type, out var nativeTypeName); @@ -1815,12 +1812,9 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, { nativeTypeName = typeName; } + nativeTypeName += $" : {fieldDecl.BitWidthValue}"; - if (fieldDecl.Parent.IsUnion) - { - _outputBuilder.WriteIndentedLine("[FieldOffset(0)]"); - } var currentSize = fieldDecl.Type.Handle.SizeOf; var bitfieldName = "_bitfield"; @@ -1828,7 +1822,8 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, Type typeBacking; string typeNameBacking; - if ((!_config.GenerateUnixTypes && (currentSize != previousSize)) || (fieldDecl.BitWidthValue > remainingBits)) + if ((!_config.GenerateUnixTypes && (currentSize != previousSize)) || + (fieldDecl.BitWidthValue > remainingBits)) { if (index >= 0) { @@ -1844,13 +1839,17 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, if (fieldDecl.Parent == recordDecl) { - _outputBuilder.WriteIndented("public "); - _outputBuilder.Write(typeNameBacking); - _outputBuilder.Write(' '); - _outputBuilder.Write(bitfieldName); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.NeedsNewline = true; + var fieldDesc = new FieldDesc + { + AccessSpecifier = AccessSpecifier.Public, + NativeTypeName = null, + EscapedName = bitfieldName, + Offset = fieldDecl.Parent.IsUnion ? 0 : null, + NeedsNewKeyword = false + }; + _outputBuilder.BeginField(in fieldDesc); + _outputBuilder.WriteRegularField(typeNameBacking, bitfieldName); + _outputBuilder.EndField(); } } else @@ -1871,8 +1870,6 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, typeNameBacking = GetRemappedTypeName(fieldDecl, context: null, typeBacking, out _); } - AddNativeTypeNameAttribute(nativeTypeName); - var bitfieldOffset = (currentSize * 8) - remainingBits; var bitwidthHexStringBacking = ((1 << fieldDecl.BitWidthValue) - 1).ToString("X"); @@ -1895,6 +1892,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, { goto default; } + goto case CXTypeKind.CXType_UInt; } @@ -1918,6 +1916,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, { goto default; } + goto case CXTypeKind.CXType_Int; } @@ -1929,7 +1928,9 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, default: { - AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported bitfield type: '{canonicalTypeBacking.TypeClass}'. Generated bindings may be incomplete.", fieldDecl); + AddDiagnostic(DiagnosticLevel.Warning, + $"Unsupported bitfield type: '{canonicalTypeBacking.TypeClass}'. Generated bindings may be incomplete.", + fieldDecl); break; } } @@ -1960,6 +1961,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, { goto default; } + goto case CXTypeKind.CXType_UInt; } @@ -1983,6 +1985,7 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, { goto default; } + goto case CXTypeKind.CXType_Int; } @@ -1994,173 +1997,197 @@ void VisitBitfieldDecl(FieldDecl fieldDecl, Type[] types, RecordDecl recordDecl, default: { - AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported bitfield type: '{canonicalType.TypeClass}'. Generated bindings may be incomplete.", fieldDecl); + AddDiagnostic(DiagnosticLevel.Warning, + $"Unsupported bitfield type: '{canonicalType.TypeClass}'. Generated bindings may be incomplete.", + fieldDecl); break; } } canonicalType = type.CanonicalType; - var accessSpecifier = GetAccessSpecifierName(fieldDecl); + var accessSpecifier = GetAccessSpecifier(fieldDecl); var name = GetRemappedCursorName(fieldDecl); var escapedName = EscapeName(name); - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - _outputBuilder.WriteLine(escapedName); - _outputBuilder.WriteBlockStart(); - - if (_config.GenerateAggressiveInlining) + var desc = new FieldDesc { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } + AccessSpecifier = accessSpecifier, + NativeTypeName = nativeTypeName, + EscapedName = escapedName, + Offset = null, + NeedsNewKeyword = false + }; - _outputBuilder.WriteIndentedLine("get"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented("return "); + _outputBuilder.WriteDivider(); + _outputBuilder.BeginField(in desc); + _outputBuilder.WriteRegularField(typeName, escapedName); + _outputBuilder.BeginBody(); + _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining); + var code = _outputBuilder.BeginCSharpCode(); + + code.WriteIndented("return "); if ((currentSize < 4) || (canonicalTypeBacking != canonicalType)) { - _outputBuilder.Write('('); - _outputBuilder.Write(typeName); - _outputBuilder.Write(")("); + code.Write('('); + code.BeginMarker("typeName"); + code.Write(typeName); + code.EndMarker("typeName"); + code.Write(")("); } if (bitfieldOffset != 0) { - _outputBuilder.Write('('); + code.Write('('); } if (!string.IsNullOrWhiteSpace(contextName)) { - _outputBuilder.Write(contextName); - _outputBuilder.Write('.'); + code.BeginMarker("contextName"); + code.Write(contextName); + code.EndMarker("contextName"); + code.Write('.'); } - _outputBuilder.Write(bitfieldName); + + code.BeginMarker("bitfieldName"); + code.Write(bitfieldName); + code.EndMarker("bitfieldName"); if (bitfieldOffset != 0) { - _outputBuilder.Write(" >> "); - _outputBuilder.Write(bitfieldOffset); - _outputBuilder.Write(')'); + code.Write(" >> "); + code.BeginMarker("bitfieldOffset"); + code.Write(bitfieldOffset); + code.EndMarker("bitfieldOffset"); + code.Write(')'); } - _outputBuilder.Write(" & 0x"); - _outputBuilder.Write(bitwidthHexStringBacking); + code.Write(" & 0x"); + code.BeginMarker("bitwidthHexStringBacking"); + code.Write(bitwidthHexStringBacking); + code.EndMarker("bitwidthHexStringBacking"); if ((currentSize < 4) || (canonicalTypeBacking != canonicalType)) { - _outputBuilder.Write(')'); + code.Write(')'); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); - - _outputBuilder.NeedsNewline = true; - - if (_config.GenerateAggressiveInlining) - { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); + _outputBuilder.EndGetter(); - _outputBuilder.WriteIndentedLine("set"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndentation(); + _outputBuilder.BeginSetter(_config.GenerateAggressiveInlining); + code = _outputBuilder.BeginCSharpCode(); + code.WriteIndentation(); if (!string.IsNullOrWhiteSpace(contextName)) { - _outputBuilder.Write(contextName); - _outputBuilder.Write('.'); + code.BeginMarker("contextName"); + code.Write(contextName); + code.EndMarker("contextName"); + code.Write('.'); } - _outputBuilder.Write(bitfieldName); - _outputBuilder.Write(" = "); + code.BeginMarker("bitfieldName"); + code.Write(bitfieldName); + code.EndMarker("bitfieldName"); + + code.Write(" = "); if (currentSize < 4) { - _outputBuilder.Write('('); - _outputBuilder.Write(typeNameBacking); - _outputBuilder.Write(")("); + code.Write('('); + code.BeginMarker("typeNameBacking"); + code.Write(typeNameBacking); + code.EndMarker("typeNameBacking"); + code.Write(")("); } - _outputBuilder.Write('('); + code.Write('('); if (!string.IsNullOrWhiteSpace(contextName)) { - _outputBuilder.Write(contextName); - _outputBuilder.Write('.'); + code.Write(contextName); + code.Write('.'); } - _outputBuilder.Write(bitfieldName); - _outputBuilder.Write(" & ~"); + code.Write(bitfieldName); + + code.Write(" & ~"); if (bitfieldOffset != 0) { - _outputBuilder.Write('('); + code.Write('('); } - _outputBuilder.Write("0x"); - _outputBuilder.Write(bitwidthHexStringBacking); + code.Write("0x"); + code.BeginMarker("bitwidthHexStringBacking"); + code.Write(bitwidthHexStringBacking); + code.EndMarker("bitwidthHexStringBacking"); if (bitfieldOffset != 0) { - _outputBuilder.Write(" << "); - _outputBuilder.Write(bitfieldOffset); - _outputBuilder.Write(')'); + code.Write(" << "); + code.BeginMarker("bitfieldOffset"); + code.Write(bitfieldOffset); + code.EndMarker("bitfieldOffset"); + code.Write(')'); } - _outputBuilder.Write(") | "); + code.Write(") | "); if ((canonicalTypeBacking != canonicalType) && !(canonicalType is EnumType)) { - _outputBuilder.Write('('); - _outputBuilder.Write(typeNameBacking); - _outputBuilder.Write(')'); + code.Write('('); + code.Write(typeNameBacking); + code.Write(')'); } - _outputBuilder.Write('('); + code.Write('('); if (bitfieldOffset != 0) { - _outputBuilder.Write('('); + code.Write('('); } if (canonicalType is EnumType) { - _outputBuilder.Write('('); - _outputBuilder.Write(typeNameBacking); - _outputBuilder.Write(")(value)"); + code.Write('('); + code.Write(typeNameBacking); + code.Write(")(value)"); } else { - _outputBuilder.Write("value"); + code.Write("value"); } - _outputBuilder.Write(" & 0x"); - _outputBuilder.Write(bitwidthHexString); + code.Write(" & 0x"); + code.BeginMarker("bitwidthHexString"); + code.Write(bitwidthHexString); + code.EndMarker("bitwidthHexString"); if (bitfieldOffset != 0) { - _outputBuilder.Write(") << "); - _outputBuilder.Write(bitfieldOffset); + code.Write(") << "); + code.Write(bitfieldOffset); } - _outputBuilder.Write(')'); + code.Write(')'); if (currentSize < 4) { - _outputBuilder.Write(')'); + code.Write(')'); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); - _outputBuilder.WriteBlockEnd(); + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); + _outputBuilder.EndSetter(); + _outputBuilder.EndBody(); + _outputBuilder.EndField(false); + _outputBuilder.WriteDivider(); remainingBits -= fieldDecl.BitWidthValue; previousSize = Math.Max(previousSize, currentSize); @@ -2179,43 +2206,41 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) return; } - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); var alignment = recordDecl.TypeForDecl.Handle.AlignOf; var maxAlignm = recordDecl.Fields.Max((fieldDecl) => fieldDecl.Type.Handle.AlignOf); + StructLayoutAttribute layout = null; if (alignment < maxAlignm) { - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - _outputBuilder.WriteIndented("[StructLayout(LayoutKind.Sequential"); - _outputBuilder.Write(", Pack = "); - _outputBuilder.Write(alignment); - _outputBuilder.WriteLine(")]"); + layout = new StructLayoutAttribute(LayoutKind.Sequential); + layout.Pack = (int) alignment; } - var accessSpecifier = GetAccessSpecifierName(constantArray); + var accessSpecifier = GetAccessSpecifier(constantArray); var canonicalElementType = type.ElementType.CanonicalType; - var isUnsafeElementType = ((canonicalElementType is PointerType) || (canonicalElementType is ReferenceType)) && ((typeName != "IntPtr") && (typeName != "UIntPtr")); - - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); - - if (isUnsafeElementType) - { - _outputBuilder.Write("unsafe "); - } + var isUnsafeElementType = + ((canonicalElementType is PointerType) || (canonicalElementType is ReferenceType)) && + ((typeName != "IntPtr") && (typeName != "UIntPtr")); var name = GetArtificialFixedSizedBufferName(constantArray); var escapedName = EscapeName(name); - _outputBuilder.Write("partial struct "); - _outputBuilder.WriteLine(escapedName); - _outputBuilder.WriteBlockStart(); + var desc = new StructDesc<(string Name, PInvokeGenerator This)> + { + AccessSpecifier = accessSpecifier, + CustomAttrGeneratorData = (name, this), + EscapedName = escapedName, + IsUnsafe = isUnsafeElementType, + Layout = layout, + WriteCustomAttrs = static _ => {} + }; + + _outputBuilder.BeginStruct(in desc); var totalSize = Math.Max(type.Size, 1); - var sizePerDimension = new List<(long index, long size)>() { - (0, type.Size) - }; + var sizePerDimension = new List<(long index, long size)>() {(0, type.Size)}; var elementType = type.ElementType; @@ -2228,19 +2253,16 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) for (long i = 0; i < totalSize; i++) { - _outputBuilder.WriteIndented("public "); - _outputBuilder.Write(typeName); - _outputBuilder.Write(" e"); - var dimension = sizePerDimension[0]; - _outputBuilder.Write(dimension.index++); + var firstDimension = dimension.index++; + var fieldName = "e" + firstDimension; sizePerDimension[0] = dimension; + var separateStride = false; for (int d = 1; d < sizePerDimension.Count; d++) { dimension = sizePerDimension[d]; - _outputBuilder.Write('_'); - _outputBuilder.Write(dimension.index); + fieldName += "_" + dimension.index; sizePerDimension[d] = dimension; var previousDimension = sizePerDimension[d - 1]; @@ -2250,128 +2272,156 @@ void VisitConstantArrayFieldDecl(RecordDecl recordDecl, FieldDecl constantArray) previousDimension.index = 0; dimension.index++; sizePerDimension[d - 1] = previousDimension; - _outputBuilder.NeedsNewline = true; + separateStride = true; } sizePerDimension[d] = dimension; } - if (_outputBuilder.NeedsNewline) + var fieldDesc = new FieldDesc { - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.NeedsNewline = true; - } - else + AccessSpecifier = accessSpecifier, + NativeTypeName = null, + EscapedName = fieldName, + Offset = null, + NeedsNewKeyword = false + }; + + _outputBuilder.BeginField(in fieldDesc); + _outputBuilder.WriteRegularField(typeName, fieldName); + _outputBuilder.EndField(); + if (!separateStride) { - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.SuppressDivider(); } } - _outputBuilder.NeedsNewline = true; - _outputBuilder.WriteIndented("public "); - var generateCompatibleCode = _config.GenerateCompatibleCode; - if (generateCompatibleCode && !isUnsafeElementType) - { - _outputBuilder.Write("unsafe "); - } - else if (!isUnsafeElementType) - { - _outputBuilder.AddUsingDirective("System"); - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - } - - _outputBuilder.Write("ref "); - _outputBuilder.Write(typeName); - _outputBuilder.Write(' '); - if (generateCompatibleCode || isUnsafeElementType) { - _outputBuilder.WriteLine("this[int index]"); - _outputBuilder.WriteBlockStart(); - - if (_config.GenerateAggressiveInlining) - { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } - - _outputBuilder.WriteIndentedLine("get"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented("fixed ("); - _outputBuilder.Write(typeName); - _outputBuilder.WriteLine("* pThis = &e0)"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented("return ref pThis[index]"); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); - _outputBuilder.WriteBlockEnd(); - _outputBuilder.WriteBlockEnd(); + _outputBuilder.BeginIndexer(AccessSpecifier.Public, generateCompatibleCode && !isUnsafeElementType); + _outputBuilder.WriteIndexer($"ref {typeName}"); + _outputBuilder.BeginIndexerParameters(); + var param = new ParameterDesc<(string Name, PInvokeGenerator This)> + { + Name = "index", + Type = "int", + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => {} + }; + _outputBuilder.BeginParameter(in param); + _outputBuilder.EndParameter(); + _outputBuilder.EndIndexerParameters(); + _outputBuilder.BeginBody(); + + _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining); + var code = _outputBuilder.BeginCSharpCode(); + + code.WriteIndented("fixed ("); + code.Write(typeName); + code.WriteLine("* pThis = &e0)"); + code.WriteBlockStart(); + code.WriteIndented("return ref pThis[index]"); + code.WriteSemicolon(); + code.WriteNewline(); + code.WriteBlockEnd(); + _outputBuilder.EndCSharpCode(code); + + _outputBuilder.EndGetter(); + _outputBuilder.EndBody(); + _outputBuilder.EndIndexer(); } else { - _outputBuilder.WriteLine("this[int index]"); - _outputBuilder.WriteBlockStart(); - - if (_config.GenerateAggressiveInlining) + _outputBuilder.BeginIndexer(AccessSpecifier.Public, false); + _outputBuilder.WriteIndexer($"ref {typeName}"); + _outputBuilder.BeginIndexerParameters(); + var param = new ParameterDesc<(string Name, PInvokeGenerator This)> { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } + Name = "index", + Type = "int", + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => {} + }; + _outputBuilder.BeginParameter(in param); + _outputBuilder.EndParameter(); + _outputBuilder.EndIndexerParameters(); + _outputBuilder.BeginBody(); - _outputBuilder.WriteIndentedLine("get"); - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndented("return ref AsSpan("); + _outputBuilder.BeginGetter(_config.GenerateAggressiveInlining); + var code = _outputBuilder.BeginCSharpCode(); + code.AddUsingDirective("System"); + code.AddUsingDirective("System.Runtime.InteropServices"); + + code.WriteIndented("return ref AsSpan("); if (type.Size == 1) { - _outputBuilder.Write("int.MaxValue"); + code.Write("int.MaxValue"); } - _outputBuilder.Write(")[index]"); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockEnd(); - _outputBuilder.WriteBlockEnd(); + code.Write(")[index]"); + code.WriteSemicolon(); + code.WriteNewline(); + _outputBuilder.EndCSharpCode(code); - _outputBuilder.NeedsNewline = true; + _outputBuilder.EndGetter(); + _outputBuilder.EndBody(); + _outputBuilder.EndIndexer(); - if (_config.GenerateAggressiveInlining) + var function = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.WriteIndentedLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]"); - } + AccessSpecifier = AccessSpecifier.Public, + EscapedName = "AsSpan", + IsAggressivelyInlined = _config.GenerateAggressiveInlining, + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => {}, + IsStatic = false, + IsMemberFunction = true + }; - _outputBuilder.WriteIndented("public Span<"); - _outputBuilder.Write(typeName); - _outputBuilder.Write("> AsSpan("); + _outputBuilder.BeginFunctionOrDelegate(in function, ref _isMethodClassUnsafe); + _outputBuilder.WriteReturnType($"Span<{typeName}>"); + _outputBuilder.BeginFunctionInnerPrototype("AsSpan"); if (type.Size == 1) { - _outputBuilder.Write("int length"); + param = new ParameterDesc<(string Name, PInvokeGenerator This)> + { + Name = "length", + Type = "int", + CustomAttrGeneratorData = (name, this), + WriteCustomAttrs = static _ => {} + }; + + _outputBuilder.BeginParameter(in param); + _outputBuilder.EndParameter(); } - _outputBuilder.Write(") => MemoryMarshal.CreateSpan(ref e0, "); + _outputBuilder.EndFunctionInnerPrototype(); + _outputBuilder.BeginBody(true); + code = _outputBuilder.BeginCSharpCode(); + + code.Write("MemoryMarshal.CreateSpan(ref e0, "); if (type.Size == 1) { - _outputBuilder.Write("length"); + code.Write("length"); } else { - _outputBuilder.Write(totalSize); + code.Write(totalSize); } - _outputBuilder.Write(')'); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + code.Write(')'); + code.WriteSemicolon(); + _outputBuilder.EndBody(true); + _outputBuilder.EndCSharpCode(code); + _outputBuilder.EndFunctionOrDelegate(false, false); } - _outputBuilder.WriteBlockEnd(); + _outputBuilder.EndStruct(); } } @@ -2397,46 +2447,37 @@ void ForFunctionProtoType(TypedefDecl typedefDecl, FunctionProtoType functionPro var name = GetRemappedCursorName(typedefDecl); var escapedName = EscapeName(name); - StartUsingOutputBuilder(name); - { - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - - var callingConventionName = GetCallingConventionName(typedefDecl, (parentType is AttributedType) ? parentType.Handle.FunctionTypeCallingConv : functionProtoType.CallConv, name, isForFnptr: false); - - _outputBuilder.WriteIndented("[UnmanagedFunctionPointer"); - - if (callingConventionName != "Winapi") - { - _outputBuilder.Write("(CallingConvention."); - _outputBuilder.Write(callingConventionName); - _outputBuilder.Write(')'); - } - - _outputBuilder.WriteLine(']'); + var callingConventionName = GetCallingConvention(typedefDecl, + (parentType is AttributedType) + ? parentType.Handle.FunctionTypeCallingConv + : functionProtoType.CallConv, name); var returnType = functionProtoType.ReturnType; - var returnTypeName = GetRemappedTypeName(typedefDecl, context: null, returnType, out var nativeTypeName); - AddNativeTypeNameAttribute(nativeTypeName, attributePrefix: "return: "); - - _outputBuilder.WriteIndented(GetAccessSpecifierName(typedefDecl)); - _outputBuilder.Write(' '); + var returnTypeName = + GetRemappedTypeName(typedefDecl, context: null, returnType, out var nativeTypeName); - if (IsUnsafe(typedefDecl, functionProtoType)) + StartUsingOutputBuilder(name); + { + var desc = new FunctionOrDelegateDesc<(string Name, PInvokeGenerator This)> { - _outputBuilder.Write("unsafe "); - } + AccessSpecifier = GetAccessSpecifier(typedefDecl), + CallingConvention = callingConventionName, + CustomAttrGeneratorData = (name, this), + EscapedName = escapedName, + IsVirtual = true, // such that it outputs as a delegate + IsUnsafe = IsUnsafe(typedefDecl, functionProtoType), + NativeTypeName = nativeTypeName, + WriteCustomAttrs = static _ => {} + }; - _outputBuilder.Write("delegate "); - _outputBuilder.Write(returnTypeName); - _outputBuilder.Write(' '); - _outputBuilder.Write(escapedName); - _outputBuilder.Write('('); + _outputBuilder.BeginFunctionOrDelegate(in desc, ref _isMethodClassUnsafe); + _outputBuilder.WriteReturnType(returnTypeName); + _outputBuilder.BeginFunctionInnerPrototype(escapedName); Visit(typedefDecl.CursorChildren.OfType()); - _outputBuilder.Write(')'); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.EndFunctionInnerPrototype(); + _outputBuilder.EndFunctionOrDelegate(true, true); } StopUsingOutputBuilder(); } @@ -2463,9 +2504,12 @@ void ForPointeeType(TypedefDecl typedefDecl, Type parentType, Type pointeeType) { ForPointeeType(typedefDecl, typedefType, typedefType.Decl.UnderlyingType); } - else if (!(pointeeType is ConstantArrayType) && !(pointeeType is BuiltinType) && !(pointeeType is TagType)) + else if (!(pointeeType is ConstantArrayType) && !(pointeeType is BuiltinType) && + !(pointeeType is TagType)) { - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported pointee type: '{pointeeType.TypeClass}'. Generating bindings may be incomplete.", typedefDecl); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported pointee type: '{pointeeType.TypeClass}'. Generating bindings may be incomplete.", + typedefDecl); } } @@ -2505,7 +2549,8 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType) if (_config.LogPotentialTypedefRemappings) { var typedefName = typedefDecl.UnderlyingDecl.Name; - var possibleNamesToRemap = new string[] { "_" + typedefName, "_tag" + typedefName, "tag" + typedefName }; + var possibleNamesToRemap = + new string[] {"_" + typedefName, "_tag" + typedefName, "tag" + typedefName}; var underlyingName = underlyingTagType.AsString; foreach (var possibleNameToRemap in possibleNamesToRemap) @@ -2514,7 +2559,8 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType) { if (possibleNameToRemap == underlyingName) { - AddDiagnostic(DiagnosticLevel.Info, $"Potential remap: {possibleNameToRemap}={typedefName}"); + AddDiagnostic(DiagnosticLevel.Info, + $"Potential remap: {possibleNameToRemap}={typedefName}"); } } } @@ -2526,8 +2572,11 @@ void ForUnderlyingType(TypedefDecl typedefDecl, Type underlyingType) } else { - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported underlying type: '{underlyingType.TypeClass}'. Generating bindings may be incomplete.", typedefDecl); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported underlying type: '{underlyingType.TypeClass}'. Generating bindings may be incomplete.", + typedefDecl); } + return; } @@ -2554,7 +2603,8 @@ private void VisitVarDecl(VarDecl varDecl) { ForDeclStmt(varDecl, declStmt); } - else if (IsPrevContextDecl(out _) || IsPrevContextDecl(out _) || IsPrevContextDecl(out _)) + else if (IsPrevContextDecl(out _) || IsPrevContextDecl(out _) || + IsPrevContextDecl(out _)) { if (!varDecl.HasInit) { @@ -2573,7 +2623,7 @@ private void VisitVarDecl(VarDecl varDecl) isMacroDefinitionRecord = true; } - var accessSpecifier = GetAccessSpecifierName(varDecl); + var accessSpecifier = GetAccessSpecifier(varDecl); var name = GetRemappedName(nativeName, varDecl, tryRemapOperatorName: false); var escapedName = EscapeName(name); @@ -2595,7 +2645,9 @@ private void VisitVarDecl(VarDecl varDecl) StartUsingOutputBuilder(_config.MethodClassName); openedOutputBuilder = true; - if (IsUnsafe(varDecl, type) && (!varDecl.HasInit || !IsStmtAsWritten(varDecl.Init, out _, removeParens: true))) + if (IsUnsafe(varDecl, type) && (!varDecl.HasInit || + !IsStmtAsWritten(varDecl.Init, out _, + removeParens: true))) { _isMethodClassUnsafe = true; } @@ -2628,14 +2680,10 @@ private void VisitVarDecl(VarDecl varDecl) nativeTypeName = nativeTypeNameBuilder.ToString(); } - AddNativeTypeNameAttribute(nativeTypeName); - - _outputBuilder.WriteIndented(accessSpecifier); - _outputBuilder.Write(' '); - var isProperty = false; var isStringLiteral = false; + var kind = ConstantKind.None; if (IsStmtAsWritten(varDecl.Init, out var stringLiteral, removeParens: true)) { switch (stringLiteral.Kind) @@ -2643,8 +2691,8 @@ private void VisitVarDecl(VarDecl varDecl) case CX_CharacterKind.CX_CLK_Ascii: case CX_CharacterKind.CX_CLK_UTF8: { - _outputBuilder.AddUsingDirective("System"); - _outputBuilder.Write("static "); + _outputBuilder.EmitSystemSupport(); + kind |= ConstantKind.NonPrimitiveConstant; typeName = "ReadOnlySpan"; isProperty = true; @@ -2664,7 +2712,7 @@ private void VisitVarDecl(VarDecl varDecl) case CX_CharacterKind.CX_CLK_UTF16: { - _outputBuilder.Write("const "); + kind |= ConstantKind.PrimitiveConstant; typeName = "string"; isStringLiteral = true; @@ -2673,56 +2721,67 @@ private void VisitVarDecl(VarDecl varDecl) default: { - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported string literal kind: '{stringLiteral.Kind}'. Generated bindings may be incomplete.", stringLiteral); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported string literal kind: '{stringLiteral.Kind}'. Generated bindings may be incomplete.", + stringLiteral); break; } } } else if ((type.IsLocalConstQualified || isMacroDefinitionRecord) && CanBeConstant(type, varDecl.Init)) { - _outputBuilder.Write("const "); + kind |= ConstantKind.PrimitiveConstant; } else if ((varDecl.StorageClass == CX_StorageClass.CX_SC_Static) || openedOutputBuilder) { - _outputBuilder.Write("static "); + kind |= ConstantKind.NonPrimitiveConstant; if (type.IsLocalConstQualified || isMacroDefinitionRecord) { - _outputBuilder.Write("readonly "); + kind |= ConstantKind.ReadOnly; } } - _outputBuilder.Write(typeName); - if (!isStringLiteral && type is ArrayType) { - _outputBuilder.Write("[]"); + typeName += "[]"; } - _outputBuilder.Write(' '); + var desc = new ConstantDesc + { + AccessSpecifier = accessSpecifier, + TypeName = typeName, + EscapedName = escapedName, + NativeTypeName = nativeTypeName, + Kind = kind + }; - _outputBuilder.Write(escapedName); + _outputBuilder.BeginConstant(in desc); if (varDecl.HasInit) { - _outputBuilder.Write(" ="); + _outputBuilder.BeginConstantValue(isProperty); + + var dereference = (type.CanonicalType is PointerType pointerType) && + (pointerType.PointeeType.CanonicalType is FunctionType) && + isMacroDefinitionRecord; - if (isProperty) + if (dereference) { - _outputBuilder.Write('>'); + _outputBuilder.BeginDereference(); } - _outputBuilder.Write(' '); + UncheckStmt(typeName, varDecl.Init); - if ((type.CanonicalType is PointerType pointerType) && (pointerType.PointeeType.CanonicalType is FunctionType) && isMacroDefinitionRecord) + if (dereference) { - _outputBuilder.Write('&'); + _outputBuilder.EndDereference(); } - UncheckStmt(typeName, varDecl.Init); + + _outputBuilder.EndConstantValue(); } - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + _outputBuilder.EndConstant(true); if (openedOutputBuilder) { @@ -2730,17 +2789,20 @@ private void VisitVarDecl(VarDecl varDecl) } else { - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); } } else { IsPrevContextDecl(out var previousContext); - AddDiagnostic(DiagnosticLevel.Error, $"Unsupported variable declaration parent: '{previousContext.CursorKindSpelling}'. Generated bindings may be incomplete.", previousContext); + AddDiagnostic(DiagnosticLevel.Error, + $"Unsupported variable declaration parent: '{previousContext.CursorKindSpelling}'. Generated bindings may be incomplete.", + previousContext); } void ForDeclStmt(VarDecl varDecl, DeclStmt declStmt) { + var outputBuilder = StartCSharpCode(); var name = GetRemappedCursorName(varDecl); var escapedName = EscapeName(name); @@ -2749,27 +2811,30 @@ void ForDeclStmt(VarDecl varDecl, DeclStmt declStmt) var type = varDecl.Type; var typeName = GetRemappedTypeName(varDecl, context: null, type, out var nativeTypeName); - _outputBuilder.Write(typeName); + outputBuilder.Write(typeName); if (type is ArrayType) { - _outputBuilder.Write("[]"); + outputBuilder.Write("[]"); } - _outputBuilder.Write(' '); + outputBuilder.Write(' '); } - _outputBuilder.Write(escapedName); + outputBuilder.Write(escapedName); if (varDecl.HasInit) { - _outputBuilder.Write(' '); - _outputBuilder.Write('='); - _outputBuilder.Write(' '); + outputBuilder.Write(' '); + outputBuilder.Write('='); + outputBuilder.Write(' '); - var varDeclTypeName = GetRemappedTypeName(varDecl, context: null, varDecl.Type, out var varDeclNativeTypeName); + var varDeclTypeName = GetRemappedTypeName(varDecl, context: null, varDecl.Type, + out var varDeclNativeTypeName); UncheckStmt(varDeclTypeName, varDecl.Init); } + + StopCSharpCode(); } bool CanBeConstant(Type type, Expr initExpr) @@ -2906,6 +2971,7 @@ bool IsConstant(Expr initExpr) return true; } } + return false; } @@ -3046,7 +3112,8 @@ bool IsConstant(Expr initExpr) long alignment32 = -1; long alignment64 = -1; - GetTypeSize(unaryExprOrTypeTraitExpr, argumentType, ref alignment32, ref alignment64, out size32, out size64); + GetTypeSize(unaryExprOrTypeTraitExpr, argumentType, ref alignment32, ref alignment64, out size32, + out size64); switch (unaryExprOrTypeTraitExpr.Kind) { @@ -3078,7 +3145,9 @@ bool IsConstant(Expr initExpr) default: { - AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported statement class: '{initExpr.StmtClassName}'. Generated bindings may not be constant.", initExpr); + AddDiagnostic(DiagnosticLevel.Warning, + $"Unsupported statement class: '{initExpr.StmtClassName}'. Generated bindings may not be constant.", + initExpr); return false; } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitRef.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitRef.cs index 2b72e322..f2fb3701 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitRef.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitRef.cs @@ -7,7 +7,8 @@ public partial class PInvokeGenerator private void VisitRef(Ref @ref) { var name = GetRemappedCursorName(@ref.Referenced); - _outputBuilder.Write(name); + StartCSharpCode().Write(name); + StopCSharpCode(); } } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index 4e425d6d..f7f788b9 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -12,49 +12,58 @@ public partial class PInvokeGenerator { private void VisitArraySubscriptExpr(ArraySubscriptExpr arraySubscriptExpr) { + var outputBuilder = StartCSharpCode(); Visit(arraySubscriptExpr.Base); - _outputBuilder.Write('['); + outputBuilder.Write('['); Visit(arraySubscriptExpr.Idx); - _outputBuilder.Write(']'); + outputBuilder.Write(']'); + StopCSharpCode(); } private void VisitBinaryOperator(BinaryOperator binaryOperator) { + var outputBuilder = StartCSharpCode(); Visit(binaryOperator.LHS); - _outputBuilder.Write(' '); - _outputBuilder.Write(binaryOperator.OpcodeStr); - _outputBuilder.Write(' '); + outputBuilder.Write(' '); + outputBuilder.Write(binaryOperator.OpcodeStr); + outputBuilder.Write(' '); Visit(binaryOperator.RHS); + StopCSharpCode(); } private void VisitBreakStmt(BreakStmt breakStmt) { - _outputBuilder.Write("break"); + StartCSharpCode().Write("break"); + StopCSharpCode(); } private void VisitBody(Stmt stmt) { + var outputBuilder = StartCSharpCode(); if (stmt is CompoundStmt) { Visit(stmt); } else { - _outputBuilder.WriteBlockStart(); - _outputBuilder.WriteIndentation(); - _outputBuilder.NeedsSemicolon = true; - _outputBuilder.NeedsNewline = true; + outputBuilder.WriteBlockStart(); + outputBuilder.WriteIndentation(); + outputBuilder.NeedsSemicolon = true; + outputBuilder.NeedsNewline = true; Visit(stmt); - _outputBuilder.WriteSemicolonIfNeeded(); - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.WriteBlockEnd(); + outputBuilder.WriteSemicolonIfNeeded(); + outputBuilder.WriteNewlineIfNeeded(); + outputBuilder.WriteBlockEnd(); } + + StopCSharpCode(); } private void VisitCallExpr(CallExpr callExpr) { + var outputBuilder = StartCSharpCode(); var calleeDecl = callExpr.CalleeDecl; if (calleeDecl is FunctionDecl functionDecl) @@ -63,16 +72,16 @@ private void VisitCallExpr(CallExpr callExpr) { case "memcpy": { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.Write("Unsafe.CopyBlockUnaligned"); + outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); + outputBuilder.Write("Unsafe.CopyBlockUnaligned"); VisitArgs(callExpr); break; } case "memset": { - _outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); - _outputBuilder.Write("Unsafe.InitBlockUnaligned"); + outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); + outputBuilder.Write("Unsafe.InitBlockUnaligned"); VisitArgs(callExpr); break; } @@ -97,7 +106,7 @@ private void VisitCallExpr(CallExpr callExpr) void VisitArgs(CallExpr callExpr) { - _outputBuilder.Write('('); + outputBuilder.Write('('); var args = callExpr.Args; @@ -107,34 +116,40 @@ void VisitArgs(CallExpr callExpr) for (int i = 1; i < args.Count; i++) { - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(args[i]); } } - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } + + StopCSharpCode(); } private void VisitCaseStmt(CaseStmt caseStmt) { - _outputBuilder.Write("case "); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("case "); Visit(caseStmt.LHS); - _outputBuilder.WriteLine(':'); + outputBuilder.WriteLine(':'); if (caseStmt.SubStmt is SwitchCase) { - _outputBuilder.WriteIndentation(); + outputBuilder.WriteIndentation(); Visit(caseStmt.SubStmt); } else { VisitBody(caseStmt.SubStmt); } + + StopCSharpCode(); } private void VisitCharacterLiteral(CharacterLiteral characterLiteral) { + var outputBuilder = StartCSharpCode(); switch (characterLiteral.Kind) { case CX_CharacterKind.CX_CLK_Ascii: @@ -142,13 +157,13 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) { if (characterLiteral.Value > ushort.MaxValue) { - _outputBuilder.Write("0x"); - _outputBuilder.Write(characterLiteral.Value.ToString("X8")); + outputBuilder.Write("0x"); + outputBuilder.Write(characterLiteral.Value.ToString("X8")); } else if (characterLiteral.Value > byte.MaxValue) { - _outputBuilder.Write("0x"); - _outputBuilder.Write(characterLiteral.Value.ToString("X4")); + outputBuilder.Write("0x"); + outputBuilder.Write(characterLiteral.Value.ToString("X4")); } else { @@ -156,16 +171,16 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) if (!isPreviousExplicitCast) { - _outputBuilder.Write("(byte)("); + outputBuilder.Write("(byte)("); } - _outputBuilder.Write('\''); - _outputBuilder.Write(EscapeCharacter((char)characterLiteral.Value)); - _outputBuilder.Write('\''); + outputBuilder.Write('\''); + outputBuilder.Write(EscapeCharacter((char)characterLiteral.Value)); + outputBuilder.Write('\''); if (!isPreviousExplicitCast) { - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } } break; @@ -185,22 +200,22 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) { if (characterLiteral.Value > ushort.MaxValue) { - _outputBuilder.Write("0x"); - _outputBuilder.Write(characterLiteral.Value.ToString("X8")); + outputBuilder.Write("0x"); + outputBuilder.Write(characterLiteral.Value.ToString("X8")); } else { - _outputBuilder.Write('\''); - _outputBuilder.Write(EscapeCharacter((char)characterLiteral.Value)); - _outputBuilder.Write('\''); + outputBuilder.Write('\''); + outputBuilder.Write(EscapeCharacter((char)characterLiteral.Value)); + outputBuilder.Write('\''); } break; } case CX_CharacterKind.CX_CLK_UTF32: { - _outputBuilder.Write("0x"); - _outputBuilder.Write(characterLiteral.Value.ToString("X8")); + outputBuilder.Write("0x"); + outputBuilder.Write(characterLiteral.Value.ToString("X8")); break; } @@ -210,36 +225,46 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) break; } } + + StopCSharpCode(); } private void VisitCompoundStmt(CompoundStmt compoundStmt) { - _outputBuilder.WriteBlockStart(); + var outputBuilder = StartCSharpCode(); + outputBuilder.WriteBlockStart(); VisitStmts(compoundStmt.Body); - _outputBuilder.WriteSemicolonIfNeeded(); - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.WriteBlockEnd(); + outputBuilder.WriteSemicolonIfNeeded(); + outputBuilder.WriteNewlineIfNeeded(); + outputBuilder.WriteBlockEnd(); + StopCSharpCode(); } private void VisitConditionalOperator(ConditionalOperator conditionalOperator) { + var outputBuilder = StartCSharpCode(); Visit(conditionalOperator.Cond); - _outputBuilder.Write(" ? "); + outputBuilder.Write(" ? "); Visit(conditionalOperator.TrueExpr); - _outputBuilder.Write(" : "); + outputBuilder.Write(" : "); Visit(conditionalOperator.FalseExpr); + StopCSharpCode(); } private void VisitContinueStmt(ContinueStmt continueStmt) { - _outputBuilder.Write("continue"); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("continue"); + StopCSharpCode(); } private void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr cxxBoolLiteralExpr) { - _outputBuilder.Write(cxxBoolLiteralExpr.ValueString); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write(cxxBoolLiteralExpr.ValueString); + StopCSharpCode(); } private void VisitCXXConstCastExpr(CXXConstCastExpr cxxConstCastExpr) @@ -252,16 +277,17 @@ private void VisitCXXConstCastExpr(CXXConstCastExpr cxxConstCastExpr) private void VisitCXXConstructExpr(CXXConstructExpr cxxConstructExpr) { + var outputBuilder = StartCSharpCode(); var isCopyOrMoveConstructor = cxxConstructExpr.Constructor is { IsCopyConstructor: true } or { IsMoveConstructor: true }; if (!isCopyOrMoveConstructor) { - _outputBuilder.Write("new "); + outputBuilder.Write("new "); var constructorName = GetRemappedCursorName(cxxConstructExpr.Constructor); - _outputBuilder.Write(constructorName); - _outputBuilder.Write('('); + outputBuilder.Write(constructorName); + outputBuilder.Write('('); } var args = cxxConstructExpr.Args; @@ -272,15 +298,17 @@ private void VisitCXXConstructExpr(CXXConstructExpr cxxConstructExpr) for (int i = 1; i < args.Count; i++) { - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(args[i]); } } if (!isCopyOrMoveConstructor) { - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } + + StopCSharpCode(); } private void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr cxxFunctionalCastExpr) @@ -297,11 +325,14 @@ private void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr cxxFunctionalCastE private void VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr cxxNullPtrLiteralExpr) { - _outputBuilder.Write("null"); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("null"); + StopCSharpCode(); } private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr) { + var outputBuilder = StartCSharpCode(); var calleeDecl = cxxOperatorCallExpr.CalleeDecl; if (calleeDecl is FunctionDecl functionDecl) @@ -309,7 +340,7 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr) if (functionDecl.DeclContext is CXXRecordDecl) { Visit(cxxOperatorCallExpr.Args[0]); - _outputBuilder.Write('.'); + outputBuilder.Write('.'); } var functionDeclName = GetCursorName(functionDecl); @@ -327,17 +358,19 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr) case "operator^=": { Visit(args[0]); - _outputBuilder.Write(' '); - _outputBuilder.Write(functionDeclName.Substring(8)); - _outputBuilder.Write(' '); + outputBuilder.Write(' '); + outputBuilder.Write(functionDeclName.Substring(8)); + outputBuilder.Write(' '); Visit(args[1]); + StopCSharpCode(); return; } case "operator~": { - _outputBuilder.Write(functionDeclName.Substring(8)); + outputBuilder.Write(functionDeclName.Substring(8)); Visit(args[0]); + StopCSharpCode(); return; } @@ -349,9 +382,9 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr) } var name = GetRemappedCursorName(functionDecl); - _outputBuilder.Write(name); + outputBuilder.Write(name); - _outputBuilder.Write('('); + outputBuilder.Write('('); if (args.Count != 0) { @@ -360,59 +393,69 @@ private void VisitCXXOperatorCallExpr(CXXOperatorCallExpr cxxOperatorCallExpr) for (int i = firstIndex + 1; i < args.Count; i++) { - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(args[i]); } } - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } else { AddDiagnostic(DiagnosticLevel.Error, $"Unsupported callee declaration: '{calleeDecl.Kind}'. Generated bindings may be incomplete.", calleeDecl); } + + StopCSharpCode(); } private void VisitCXXThisExpr(CXXThisExpr cxxThisExpr) { - _outputBuilder.Write("this"); + StartCSharpCode().Write("this"); + StopCSharpCode(); } private void VisitCXXUuidofExpr(CXXUuidofExpr cxxUuidofExpr) { - _outputBuilder.Write("typeof("); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("typeof("); var type = cxxUuidofExpr.IsTypeOperand ? cxxUuidofExpr.TypeOperand : cxxUuidofExpr.ExprOperand.Type; var typeName = GetRemappedTypeName(cxxUuidofExpr, context: null, type, out _); - _outputBuilder.Write(typeName); + outputBuilder.Write(typeName); - _outputBuilder.Write(").GUID"); + outputBuilder.Write(").GUID"); + + StopCSharpCode(); } private void VisitDeclRefExpr(DeclRefExpr declRefExpr) { + var outputBuilder = StartCSharpCode(); if ((declRefExpr.Decl is EnumConstantDecl enumConstantDecl) && (declRefExpr.DeclContext != enumConstantDecl.DeclContext) && (enumConstantDecl.DeclContext is NamedDecl namedDecl)) { var enumName = GetRemappedCursorName(namedDecl); if (!_config.DontUseUsingStaticsForEnums) { - _outputBuilder.AddUsingDirective($"static {_config.Namespace}.{enumName}"); + outputBuilder.AddUsingDirective($"static {_config.Namespace}.{enumName}"); } else { - _outputBuilder.Write(enumName); - _outputBuilder.Write("."); + outputBuilder.Write(enumName); + outputBuilder.Write("."); } } var name = GetRemappedCursorName(declRefExpr.Decl); var escapedName = (declRefExpr.Decl is FunctionDecl) ? EscapeAndStripName(name) : EscapeName(name); - _outputBuilder.Write(escapedName); + outputBuilder.Write(escapedName); + + StopCSharpCode(); } private void VisitDeclStmt(DeclStmt declStmt) { + var outputBuilder = StartCSharpCode(); if (declStmt.IsSingleDecl) { Visit(declStmt.SingleDecl); @@ -423,86 +466,101 @@ private void VisitDeclStmt(DeclStmt declStmt) foreach (var decl in declStmt.Decls.Skip(1)) { - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(decl); } } + + StopCSharpCode(); } private void VisitDefaultStmt(DefaultStmt defaultStmt) { - _outputBuilder.WriteLine("default:"); + var outputBuilder = StartCSharpCode(); + outputBuilder.WriteLine("default:"); if (defaultStmt.SubStmt is SwitchCase) { - _outputBuilder.WriteIndentation(); + outputBuilder.WriteIndentation(); Visit(defaultStmt.SubStmt); } else { VisitBody(defaultStmt.SubStmt); } + + StopCSharpCode(); } private void VisitDoStmt(DoStmt doStmt) { - _outputBuilder.WriteLine("do"); + var outputBuilder = StartCSharpCode(); + outputBuilder.WriteLine("do"); VisitBody(doStmt.Body); - - _outputBuilder.WriteIndented("while ("); + + outputBuilder.WriteIndented("while ("); Visit(doStmt.Cond); - _outputBuilder.Write(')'); - _outputBuilder.WriteSemicolon(); - _outputBuilder.WriteNewline(); + outputBuilder.Write(')'); + outputBuilder.WriteSemicolon(); + outputBuilder.WriteNewline(); + + outputBuilder.NeedsNewline = true; - _outputBuilder.NeedsNewline = true; + StopCSharpCode(); } private void VisitExplicitCastExpr(ExplicitCastExpr explicitCastExpr) { + var outputBuilder = StartCSharpCode(); if (IsPrevContextDecl(out var _) && explicitCastExpr.Type is EnumType enumType) { - _outputBuilder.Write('('); + outputBuilder.Write('('); var enumUnderlyingTypeName = GetRemappedTypeName(explicitCastExpr, context: null, enumType.Decl.IntegerType, out _); - _outputBuilder.Write(enumUnderlyingTypeName); - _outputBuilder.Write(')'); + outputBuilder.Write(enumUnderlyingTypeName); + outputBuilder.Write(')'); } var type = explicitCastExpr.Type; var typeName = GetRemappedTypeName(explicitCastExpr, context: null, type, out var nativeTypeName); - _outputBuilder.Write('('); - _outputBuilder.Write(typeName); - _outputBuilder.Write(')'); + outputBuilder.Write('('); + outputBuilder.Write(typeName); + outputBuilder.Write(')'); ParenthesizeStmt(explicitCastExpr.SubExprAsWritten); + + StopCSharpCode(); } private void VisitFloatingLiteral(FloatingLiteral floatingLiteral) { + var outputBuilder = StartCSharpCode(); if (floatingLiteral.ValueString.EndsWith(".f")) { - _outputBuilder.Write(floatingLiteral.ValueString.Substring(0, floatingLiteral.ValueString.Length - 1)); - _outputBuilder.Write("0f"); + outputBuilder.Write(floatingLiteral.ValueString.Substring(0, floatingLiteral.ValueString.Length - 1)); + outputBuilder.Write("0f"); } else { - _outputBuilder.Write(floatingLiteral.ValueString); + outputBuilder.Write(floatingLiteral.ValueString); if (floatingLiteral.ValueString.EndsWith(".")) { - _outputBuilder.Write('0'); + outputBuilder.Write('0'); } } + + StopCSharpCode(); } private void VisitForStmt(ForStmt forStmt) { - _outputBuilder.Write("for ("); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("for ("); if (forStmt.ConditionVariableDeclStmt != null) { @@ -512,67 +570,77 @@ private void VisitForStmt(ForStmt forStmt) { Visit(forStmt.Init); } - _outputBuilder.WriteSemicolon(); + + outputBuilder.WriteSemicolon(); if (forStmt.Cond != null) { - _outputBuilder.Write(' '); + outputBuilder.Write(' '); Visit(forStmt.Cond); } - _outputBuilder.WriteSemicolon(); + + outputBuilder.WriteSemicolon(); if (forStmt.Inc != null) { - _outputBuilder.Write(' '); + outputBuilder.Write(' '); Visit(forStmt.Inc); } - _outputBuilder.WriteLine(')'); + + outputBuilder.WriteLine(')'); VisitBody(forStmt.Body); + StopCSharpCode(); } private void VisitGotoStmt(GotoStmt gotoStmt) { - _outputBuilder.Write("goto "); - _outputBuilder.Write(gotoStmt.Label.Name); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("goto "); + outputBuilder.Write(gotoStmt.Label.Name); + StopCSharpCode(); } private void VisitIfStmt(IfStmt ifStmt) { - _outputBuilder.Write("if ("); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write("if ("); Visit(ifStmt.Cond); - _outputBuilder.WriteLine(')'); + outputBuilder.WriteLine(')'); VisitBody(ifStmt.Then); if (ifStmt.Else != null) { - _outputBuilder.WriteIndented("else"); + outputBuilder.WriteIndented("else"); if (ifStmt.Else is IfStmt) { - _outputBuilder.Write(' '); + outputBuilder.Write(' '); Visit(ifStmt.Else); } else { - _outputBuilder.WriteNewline(); + outputBuilder.WriteNewline(); VisitBody(ifStmt.Else); } } + + StopCSharpCode(); } private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) { + var outputBuilder = StartCSharpCode(); var subExpr = implicitCastExpr.SubExprAsWritten; switch (implicitCastExpr.CastKind) { case CX_CastKind.CX_CK_NullToPointer: { - _outputBuilder.Write("null"); + outputBuilder.Write("null"); break; } @@ -585,7 +653,7 @@ private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) else { ParenthesizeStmt(subExpr); - _outputBuilder.Write(" != null"); + outputBuilder.Write(" != null"); } break; } @@ -611,7 +679,7 @@ private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) else { ParenthesizeStmt(subExpr); - _outputBuilder.Write(" != 0"); + outputBuilder.Write(" != 0"); } break; } @@ -622,15 +690,15 @@ private void VisitImplicitCastExpr(ImplicitCastExpr implicitCastExpr) if (needsCast) { - _outputBuilder.Write("(byte)("); + outputBuilder.Write("(byte)("); } ParenthesizeStmt(subExpr); - _outputBuilder.Write(" ? 1 : 0"); + outputBuilder.Write(" ? 1 : 0"); if (needsCast) { - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } break; @@ -667,33 +735,37 @@ void ForEnumConstantDecl(ImplicitCastExpr implicitCastExpr, EnumConstantDecl enu var type = implicitCastExpr.Type; var typeName = GetRemappedTypeName(implicitCastExpr, context: null, type, out var nativeTypeName); - _outputBuilder.Write('('); - _outputBuilder.Write(typeName); - _outputBuilder.Write(')'); + outputBuilder.Write('('); + outputBuilder.Write(typeName); + outputBuilder.Write(')'); ParenthesizeStmt(subExpr); } } + + StopCSharpCode(); } private void VisitImplicitValueInitExpr(ImplicitValueInitExpr implicitValueInitExpr) { - _outputBuilder.Write("default"); + StartCSharpCode().Write("default"); + StopCSharpCode(); } private void VisitInitListExpr(InitListExpr initListExpr) { + var outputBuilder = StartCSharpCode(); ForType(initListExpr, initListExpr.Type); void ForArrayType(InitListExpr initListExpr, ArrayType arrayType) { - _outputBuilder.Write("new "); + outputBuilder.Write("new "); var type = initListExpr.Type; var typeName = GetRemappedTypeName(initListExpr, context: null, type, out var nativeTypeName); - _outputBuilder.Write(typeName); - _outputBuilder.Write('['); + outputBuilder.Write(typeName); + outputBuilder.Write('['); long size = -1; @@ -708,66 +780,66 @@ void ForArrayType(InitListExpr initListExpr, ArrayType arrayType) if (size != -1) { - _outputBuilder.Write(size); + outputBuilder.Write(size); } - _outputBuilder.WriteLine(']'); - _outputBuilder.WriteBlockStart(); + outputBuilder.WriteLine(']'); + outputBuilder.WriteBlockStart(); for (int i = 0; i < initListExpr.Inits.Count; i++) { - _outputBuilder.WriteIndentation(); + outputBuilder.WriteIndentation(); Visit(initListExpr.Inits[i]); - _outputBuilder.WriteLine(','); + outputBuilder.WriteLine(','); } for (int i = initListExpr.Inits.Count; i < size; i++) { - _outputBuilder.WriteIndentedLine("default,"); + outputBuilder.WriteIndentedLine("default,"); } - _outputBuilder.DecreaseIndentation(); - _outputBuilder.WriteIndented('}'); - _outputBuilder.NeedsSemicolon = true; + outputBuilder.DecreaseIndentation(); + outputBuilder.WriteIndented('}'); + outputBuilder.NeedsSemicolon = true; } void ForRecordType(InitListExpr initListExpr, RecordType recordType) { - _outputBuilder.Write("new "); + outputBuilder.Write("new "); var type = initListExpr.Type; var typeName = GetRemappedTypeName(initListExpr, context: null, type, out var nativeTypeName); - _outputBuilder.Write(typeName); + outputBuilder.Write(typeName); if (typeName == "Guid") { - _outputBuilder.Write('('); + outputBuilder.Write('('); Visit(initListExpr.Inits[0]); - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(initListExpr.Inits[1]); - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(initListExpr.Inits[2]); initListExpr = (InitListExpr)initListExpr.Inits[3]; for (int i = 0; i < initListExpr.Inits.Count; i++) { - _outputBuilder.Write(", "); + outputBuilder.Write(", "); Visit(initListExpr.Inits[i]); } - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } else { - _outputBuilder.WriteNewline(); - _outputBuilder.WriteBlockStart(); + outputBuilder.WriteNewline(); + outputBuilder.WriteBlockStart(); var decl = recordType.Decl; @@ -782,15 +854,15 @@ void ForRecordType(InitListExpr initListExpr, RecordType recordType) var fieldName = GetRemappedCursorName(decl.Fields[i]); - _outputBuilder.WriteIndented(fieldName); - _outputBuilder.Write(" = "); + outputBuilder.WriteIndented(fieldName); + outputBuilder.Write(" = "); Visit(init); - _outputBuilder.WriteLine(','); + outputBuilder.WriteLine(','); } - _outputBuilder.DecreaseIndentation(); - _outputBuilder.WriteIndented('}'); - _outputBuilder.NeedsSemicolon = true; + outputBuilder.DecreaseIndentation(); + outputBuilder.WriteIndented('}'); + outputBuilder.NeedsSemicolon = true; } } @@ -817,6 +889,8 @@ void ForType(InitListExpr initListExpr, Type type) AddDiagnostic(DiagnosticLevel.Error, $"Unsupported init list expression type: '{type.KindSpelling}'. Generated bindings may be incomplete.", initListExpr); } } + + StopCSharpCode(); } private void VisitIntegerLiteral(IntegerLiteral integerLiteral) @@ -865,20 +939,24 @@ private void VisitIntegerLiteral(IntegerLiteral integerLiteral) valueString = valueString.Substring(0, valueString.Length - 1) + "U"; } - _outputBuilder.Write(valueString); + StartCSharpCode().Write(valueString); + StopCSharpCode(); } private void VisitLabelStmt(LabelStmt labelStmt) { - _outputBuilder.Write(labelStmt.Decl.Name); - _outputBuilder.WriteLine(':'); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write(labelStmt.Decl.Name); + outputBuilder.WriteLine(':'); - _outputBuilder.WriteIndentation(); + outputBuilder.WriteIndentation(); Visit(labelStmt.SubStmt); + StopCSharpCode(); } private void VisitMemberExpr(MemberExpr memberExpr) { + var outputBuilder = StartCSharpCode(); if (!memberExpr.IsImplicitAccess) { Visit(memberExpr.Base); @@ -900,32 +978,37 @@ private void VisitMemberExpr(MemberExpr memberExpr) if ((type != null) && ((type is PointerType) || (type is ReferenceType))) { - _outputBuilder.Write("->"); + outputBuilder.Write("->"); } else { - _outputBuilder.Write('.'); + outputBuilder.Write('.'); } } - _outputBuilder.Write(GetRemappedCursorName(memberExpr.MemberDecl)); + + outputBuilder.Write(GetRemappedCursorName(memberExpr.MemberDecl)); + StopCSharpCode(); } private void VisitParenExpr(ParenExpr parenExpr) { - _outputBuilder.Write('('); + var outputBuilder = StartCSharpCode(); + outputBuilder.Write('('); Visit(parenExpr.SubExpr); - _outputBuilder.Write(')'); + outputBuilder.Write(')'); + StopCSharpCode(); } private void VisitReturnStmt(ReturnStmt returnStmt) { + var outputBuilder = StartCSharpCode(); if (IsPrevContextDecl(out var functionDecl) && (functionDecl.ReturnType.CanonicalType.Kind != CXTypeKind.CXType_Void)) { - _outputBuilder.Write("return"); + outputBuilder.Write("return"); if (returnStmt.RetValue != null) { - _outputBuilder.Write(' '); + outputBuilder.Write(' '); Visit(returnStmt.RetValue); } } @@ -935,8 +1018,10 @@ private void VisitReturnStmt(ReturnStmt returnStmt) } else { - _outputBuilder.Write("return"); + outputBuilder.Write("return"); } + + StopCSharpCode(); } private void VisitStmt(Stmt stmt) @@ -1376,6 +1461,7 @@ private void VisitStmt(Stmt stmt) private void VisitStmts(IReadOnlyList stmts) { + var outputBuilder = StartCSharpCode(); var lastIndex = stmts.Count - 1; var previousStmt = null as Stmt; @@ -1385,17 +1471,17 @@ private void VisitStmts(IReadOnlyList stmts) if ((previousStmt is DeclStmt) && !(stmt is DeclStmt)) { - _outputBuilder.NeedsNewline = true; + outputBuilder.NeedsNewline = true; } - _outputBuilder.WriteIndentation(); - _outputBuilder.NeedsSemicolon = true; - _outputBuilder.NeedsNewline = true; + outputBuilder.WriteIndentation(); + outputBuilder.NeedsSemicolon = true; + outputBuilder.NeedsNewline = true; Visit(stmts[i]); - _outputBuilder.WriteSemicolonIfNeeded(); - _outputBuilder.WriteNewline(); + outputBuilder.WriteSemicolonIfNeeded(); + outputBuilder.WriteNewline(); previousStmt = stmt; } @@ -1406,39 +1492,42 @@ private void VisitStmts(IReadOnlyList stmts) if ((previousStmt is DeclStmt) && !(stmt is DeclStmt)) { - _outputBuilder.NeedsNewline = true; + outputBuilder.NeedsNewline = true; } - _outputBuilder.WriteIndentation(); - _outputBuilder.NeedsSemicolon = true; - _outputBuilder.NeedsNewline = true; + outputBuilder.WriteIndentation(); + outputBuilder.NeedsSemicolon = true; + outputBuilder.NeedsNewline = true; Visit(stmt); - _outputBuilder.WriteSemicolonIfNeeded(); - _outputBuilder.WriteNewlineIfNeeded(); + outputBuilder.WriteSemicolonIfNeeded(); + outputBuilder.WriteNewlineIfNeeded(); } + + StopCSharpCode(); } private void VisitStringLiteral(StringLiteral stringLiteral) { + var outputBuilder = StartCSharpCode(); switch (stringLiteral.Kind) { case CX_CharacterKind.CX_CLK_Ascii: case CX_CharacterKind.CX_CLK_UTF8: { - _outputBuilder.Write("new byte[] { "); + outputBuilder.Write("new byte[] { "); var bytes = Encoding.UTF8.GetBytes(stringLiteral.String); foreach (var b in bytes) { - _outputBuilder.Write("0x"); - _outputBuilder.Write(b.ToString("X2")); - _outputBuilder.Write(", "); + outputBuilder.Write("0x"); + outputBuilder.Write(b.ToString("X2")); + outputBuilder.Write(", "); } - _outputBuilder.Write("0x00 }"); + outputBuilder.Write("0x00 }"); break; } @@ -1454,9 +1543,9 @@ private void VisitStringLiteral(StringLiteral stringLiteral) case CX_CharacterKind.CX_CLK_UTF16: { - _outputBuilder.Write('"'); - _outputBuilder.Write(EscapeString(stringLiteral.String)); - _outputBuilder.Write('"'); + outputBuilder.Write('"'); + outputBuilder.Write(EscapeString(stringLiteral.String)); + outputBuilder.Write('"'); break; } @@ -1466,21 +1555,28 @@ private void VisitStringLiteral(StringLiteral stringLiteral) break; } } + + StopCSharpCode(); } private void VisitSwitchStmt(SwitchStmt switchStmt) { - _outputBuilder.Write("switch ("); + var outputBuilder = StartCSharpCode(); + + outputBuilder.Write("switch ("); Visit(switchStmt.Cond); - _outputBuilder.WriteLine(')'); + outputBuilder.WriteLine(')'); VisitBody(switchStmt.Body); + + StopCSharpCode(); } private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrTypeTraitExpr) { + var outputBuilder = StartCSharpCode(); var argumentType = unaryExprOrTypeTraitExpr.TypeOfArgument; long size32; @@ -1497,7 +1593,7 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT { if ((size32 == size64) && IsPrevContextDecl(out _)) { - _outputBuilder.Write(size32); + outputBuilder.Write(size32); } else { @@ -1545,16 +1641,16 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT if (needsCast) { - _outputBuilder.Write("(uint)("); + outputBuilder.Write("(uint)("); } - _outputBuilder.Write("sizeof("); - _outputBuilder.Write(typeName); - _outputBuilder.Write(')'); + outputBuilder.Write("sizeof("); + outputBuilder.Write(typeName); + outputBuilder.Write(')'); if (needsCast) { - _outputBuilder.Write(')'); + outputBuilder.Write(')'); } } break; @@ -1565,14 +1661,14 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT { if (alignment32 == alignment64) { - _outputBuilder.Write(alignment32); + outputBuilder.Write(alignment32); } else { - _outputBuilder.Write("Environment.Is64BitProcess ? "); - _outputBuilder.Write(alignment64); - _outputBuilder.Write(" : "); - _outputBuilder.Write(alignment32); + outputBuilder.Write("Environment.Is64BitProcess ? "); + outputBuilder.Write(alignment64); + outputBuilder.Write(" : "); + outputBuilder.Write(alignment32); } break; @@ -1584,17 +1680,20 @@ private void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr unaryExprOrT break; } } + + StopCSharpCode(); } private void VisitUnaryOperator(UnaryOperator unaryOperator) { + var outputBuilder = StartCSharpCode(); switch (unaryOperator.Opcode) { case CX_UnaryOperatorKind.CX_UO_PostInc: case CX_UnaryOperatorKind.CX_UO_PostDec: { Visit(unaryOperator.SubExpr); - _outputBuilder.Write(unaryOperator.OpcodeStr); + outputBuilder.Write(unaryOperator.OpcodeStr); break; } @@ -1605,7 +1704,7 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator) case CX_UnaryOperatorKind.CX_UO_Minus: case CX_UnaryOperatorKind.CX_UO_Not: { - _outputBuilder.Write(unaryOperator.OpcodeStr); + outputBuilder.Write(unaryOperator.OpcodeStr); Visit(unaryOperator.SubExpr); break; } @@ -1618,16 +1717,16 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator) if (canonicalType.IsIntegerType && (canonicalType.Kind != CXTypeKind.CXType_Bool)) { Visit(subExpr); - _outputBuilder.Write(" == 0"); + outputBuilder.Write(" == 0"); } else if ((canonicalType is PointerType) || (canonicalType is ReferenceType)) { Visit(subExpr); - _outputBuilder.Write(" == null"); + outputBuilder.Write(" == null"); } else { - _outputBuilder.Write(unaryOperator.OpcodeStr); + outputBuilder.Write(unaryOperator.OpcodeStr); Visit(unaryOperator.SubExpr); } break; @@ -1641,7 +1740,7 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator) } else { - _outputBuilder.Write(unaryOperator.OpcodeStr); + outputBuilder.Write(unaryOperator.OpcodeStr); Visit(unaryOperator.SubExpr); } break; @@ -1653,17 +1752,23 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator) break; } } + + StopCSharpCode(); } private void VisitWhileStmt(WhileStmt whileStmt) { - _outputBuilder.Write("while ("); + var outputBuilder = StartCSharpCode(); + + outputBuilder.Write("while ("); Visit(whileStmt.Cond); - _outputBuilder.WriteLine(')'); + outputBuilder.WriteLine(')'); VisitBody(whileStmt.Body); + + StopCSharpCode(); } } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 8dc69f1f..3a382e27 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -7,7 +7,10 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; +using ClangSharp.Abstractions; +using ClangSharp.CSharp; using ClangSharp.Interop; +using ClangSharp.XML; namespace ClangSharp { @@ -31,8 +34,10 @@ public sealed partial class PInvokeGenerator : IDisposable private string[] _clangCommandLineArgs; private CXTranslationUnit_Flags _translationFlags; - private OutputBuilder _outputBuilder; - private OutputBuilder _testOutputBuilder; + private IOutputBuilder _outputBuilder; + private CSharpOutputBuilder _testOutputBuilder; + private CSharpOutputBuilder _stmtOutputBuilder; + private int _stmtOutputBuilderUsers; private int _outputBuilderUsers; private bool _disposed; private bool _isMethodClassUnsafe; @@ -45,7 +50,7 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func { var directoryPath = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryPath); @@ -78,7 +83,7 @@ public PInvokeGenerator(PInvokeGeneratorConfiguration config, Func"); + sw.WriteLine(""); + sw.WriteLine(" "); + foreach (var ln in _config.HeaderText.Split('\n')) + { + sw.Write(" "); + sw.WriteLine(ln); + } - sw.WriteLine(); + sw.WriteLine(" "); + } } } } @@ -158,7 +174,7 @@ public void Close() if (_config.GenerateMultipleFiles) { - outputPath = Path.Combine(outputPath, $"{outputBuilder.Name}.cs"); + outputPath = Path.Combine(outputPath, $"{outputBuilder.Name}{outputBuilder.Extension}"); stream = _outputStreamFactory(outputPath); emitNamespaceDeclaration = true; } @@ -295,229 +311,189 @@ private void AddDiagnostic(DiagnosticLevel level, string message, Cursor cursor) _diagnostics.Add(diagnostic); } - private void AddCppAttributes(ParmVarDecl parmVarDecl, string prefix = null, string postfix = null) + private void CloseOutputBuilder(Stream stream, IOutputBuilder outputBuilder, bool isMethodClass, bool leaveStreamOpen, bool emitNamespaceDeclaration) { - if (!_config.GenerateCppAttributes) - { - return; - } - - if (parmVarDecl.Attrs.Count == 0) - { - return; - } - - if (prefix is null) - { - _outputBuilder.WriteIndentation(); - } - else + if (stream is null) { - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.Write(prefix); + throw new ArgumentNullException(nameof(stream)); } - _outputBuilder.Write($"[CppAttributeList(\""); - - _outputBuilder.Write(EscapeString(parmVarDecl.Attrs[0].Spelling)); - for (int i = 1; i < parmVarDecl.Attrs.Count; i++) + if (outputBuilder is null) { - // Separator char between attributes - _outputBuilder.Write('^'); - - _outputBuilder.Write(EscapeString(parmVarDecl.Attrs[i].Spelling)); + throw new ArgumentNullException(nameof(outputBuilder)); } - _outputBuilder.Write($"\")]"); + using var sw = new StreamWriter(stream, defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); + sw.NewLine = "\n"; - if (postfix is null) - { - _outputBuilder.NeedsNewline = true; - } - else + if (_config.GenerateMultipleFiles) { - _outputBuilder.Write(postfix); - } - } + if (outputBuilder is CSharpOutputBuilder csharpOutputBuilder) + { + if (_config.HeaderText != string.Empty) + { + sw.WriteLine(_config.HeaderText); + } - private void AddNativeInheritanceAttribute(string inheritedFromName, string prefix = null, string postfix = null, string attributePrefix = null) - { - if (!_config.GenerateNativeInheritanceAttribute) - { - return; - } + var usingDirectives = csharpOutputBuilder.UsingDirectives.Concat(csharpOutputBuilder.StaticUsingDirectives); - if (prefix is null) - { - _outputBuilder.WriteIndentation(); - } - else - { - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.Write(prefix); - } + if (usingDirectives.Any()) + { + foreach (var usingDirective in usingDirectives) + { + sw.Write("using "); + sw.Write(usingDirective); + sw.WriteLine(';'); + } - _outputBuilder.Write('['); + sw.WriteLine(); + } + } + else if (outputBuilder is XmlOutputBuilder xmlOutputBuilder) + { + sw.WriteLine(""); + sw.WriteLine(""); + sw.WriteLine(" "); + foreach (var ln in _config.HeaderText.Split('\n')) + { + sw.Write(" "); + sw.WriteLine(ln); + } - if (attributePrefix != null) - { - _outputBuilder.Write(attributePrefix); + sw.WriteLine(" "); + } } - _outputBuilder.Write("NativeInheritance"); - _outputBuilder.Write('('); - - _outputBuilder.Write('"'); - _outputBuilder.Write(EscapeString(inheritedFromName)); - _outputBuilder.Write('"'); - _outputBuilder.Write(')'); - _outputBuilder.Write(']'); - - if (postfix is null) + if (outputBuilder is CSharpOutputBuilder csOutputBuilder) { - _outputBuilder.NeedsNewline = true; + ForCSharp(csOutputBuilder); } - else - { - _outputBuilder.Write(postfix); - } - } - - private void AddNativeTypeNameAttribute(string nativeTypeName, string prefix = null, string postfix = null, string attributePrefix = null) - { - if (string.IsNullOrWhiteSpace(nativeTypeName)) + else if (outputBuilder is XmlOutputBuilder xmlOutputBuilder) { - return; + ForXml(xmlOutputBuilder); } - if (prefix is null) - { - _outputBuilder.WriteIndentation(); - } - else + void ForCSharp(CSharpOutputBuilder csharpOutputBuilder) { - _outputBuilder.WriteNewlineIfNeeded(); - _outputBuilder.Write(prefix); - } - - _outputBuilder.Write('['); + var indentationString = csharpOutputBuilder.IndentationString; - if (attributePrefix != null) - { - _outputBuilder.Write(attributePrefix); - } + if (emitNamespaceDeclaration) + { + sw.Write("namespace "); + sw.Write(Config.Namespace); - _outputBuilder.Write("NativeTypeName(\""); - _outputBuilder.Write(EscapeString(nativeTypeName)); - _outputBuilder.Write("\")]"); + if (csharpOutputBuilder.IsTestOutput) + { + sw.Write(".UnitTests"); + } - if (postfix is null) - { - _outputBuilder.NeedsNewline = true; - } - else - { - _outputBuilder.Write(postfix); - } - } + sw.WriteLine(); + sw.WriteLine('{'); + } + else + { + sw.WriteLine(); + } - private void CloseOutputBuilder(Stream stream, OutputBuilder outputBuilder, bool isMethodClass, bool leaveStreamOpen, bool emitNamespaceDeclaration) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } + if (isMethodClass) + { + sw.Write(indentationString); + sw.Write("public static "); - if (outputBuilder is null) - { - throw new ArgumentNullException(nameof(outputBuilder)); - } + if (_isMethodClassUnsafe) + { + sw.Write("unsafe "); + } - using var sw = new StreamWriter(stream, defaultStreamWriterEncoding, DefaultStreamWriterBufferSize, leaveStreamOpen); - sw.NewLine = "\n"; + sw.Write("partial class "); + sw.WriteLine(Config.MethodClassName); + sw.Write(indentationString); + sw.WriteLine('{'); - if (_config.GenerateMultipleFiles) - { - if (_config.HeaderText != string.Empty) - { - sw.WriteLine(_config.HeaderText); + indentationString += csharpOutputBuilder.IndentationString; } - var usingDirectives = outputBuilder.UsingDirectives.Concat(outputBuilder.StaticUsingDirectives); - - if (usingDirectives.Any()) + foreach (var line in csharpOutputBuilder.Contents) { - foreach(var usingDirective in usingDirectives) + if (string.IsNullOrWhiteSpace(line)) { - sw.Write("using "); - sw.Write(usingDirective); - sw.WriteLine(';'); + sw.WriteLine(); + } + else + { + sw.Write(indentationString); + sw.WriteLine(line); } - - sw.WriteLine(); } - } - - var indentationString = outputBuilder.IndentationString; - - if (emitNamespaceDeclaration) - { - sw.Write("namespace "); - sw.Write(Config.Namespace); - if (outputBuilder.IsTestOutput) + if (isMethodClass) { - sw.Write(".UnitTests"); + sw.Write(csharpOutputBuilder.IndentationString); + sw.WriteLine('}'); } - sw.WriteLine(); - sw.WriteLine('{'); - } - else - { - sw.WriteLine(); + if (_config.GenerateMultipleFiles) + { + sw.WriteLine('}'); + } } - if (isMethodClass) + void ForXml(XmlOutputBuilder xmlOutputBuilder) { - sw.Write(indentationString); - sw.Write("public static "); - - if (_isMethodClassUnsafe) + const string indent = " "; + var indentationString = indent; + if (emitNamespaceDeclaration) { - sw.Write("unsafe "); + sw.Write(indentationString); + sw.Write(""); + indentationString += indent; } - sw.Write("partial class "); - sw.WriteLine(Config.MethodClassName); - sw.Write(indentationString); - sw.WriteLine('{'); + if (isMethodClass) + { + sw.Write(indentationString); + sw.Write("'); + indentationString += indent; + } - foreach (var line in outputBuilder.Contents) - { - if (string.IsNullOrWhiteSpace(line)) + foreach (var line in xmlOutputBuilder.Contents) { - sw.WriteLine(); + if (string.IsNullOrWhiteSpace(line)) + { + sw.WriteLine(); + } + else + { + sw.Write(indentationString); + sw.WriteLine(line); + } } - else + + if (isMethodClass) { + indentationString = indentationString.Substring(0, indentationString.Length - indent.Length); sw.Write(indentationString); - sw.WriteLine(line); + sw.WriteLine(""); } - } - if (isMethodClass) - { - sw.Write(outputBuilder.IndentationString); - sw.WriteLine('}'); - } + if (_config.GenerateMultipleFiles) + { + indentationString = indentationString.Substring(0, indentationString.Length - indent.Length); + sw.Write(indentationString); + sw.WriteLine(""); + } - if (_config.GenerateMultipleFiles) - { - sw.WriteLine('}'); + sw.WriteLine(""); } } @@ -641,48 +617,48 @@ private string EscapeAndStripName(string name) return EscapeName(name); } - private string EscapeCharacter(char value) => EscapeString(value.ToString()); + internal static string EscapeCharacter(char value) => EscapeString(value.ToString()); - private string EscapeString(string value) => value.Replace("\\", "\\\\") - .Replace("\r", "\\r") - .Replace("\n", "\\n") - .Replace("\t", "\\t") - .Replace("\"", "\\\""); + internal static string EscapeString(string value) => value.Replace("\\", "\\\\") + .Replace("\r", "\\r") + .Replace("\n", "\\n") + .Replace("\t", "\\t") + .Replace("\"", "\\\""); - private string GetAccessSpecifierName(NamedDecl namedDecl) + private AccessSpecifier GetAccessSpecifier(NamedDecl namedDecl) { - string name; + AccessSpecifier name; switch (namedDecl.Access) { case CX_CXXAccessSpecifier.CX_CXXInvalidAccessSpecifier: { // Top level declarations will have an invalid access specifier - name = "public"; + name = AccessSpecifier.Public; break; } case CX_CXXAccessSpecifier.CX_CXXPublic: { - name = "public"; + name = AccessSpecifier.Public; break; } case CX_CXXAccessSpecifier.CX_CXXProtected: { - name = "protected"; + name = AccessSpecifier.Protected; break; } case CX_CXXAccessSpecifier.CX_CXXPrivate: { - name = "private"; + name = AccessSpecifier.Private; break; } default: { - name = "internal"; + name = AccessSpecifier.Internal; AddDiagnostic(DiagnosticLevel.Warning, $"Unknown access specifier: '{namedDecl.Access}'. Falling back to '{name}'.", namedDecl); break; } @@ -759,43 +735,48 @@ private Type[] GetBitfieldCount(RecordDecl recordDecl) return types.ToArray(); } - private string GetCallingConventionName(Cursor cursor, CXCallingConv callingConvention, string remappedName, bool isForFnptr) + private CallingConvention GetCallingConvention(Cursor cursor, CXCallingConv callingConvention, string remappedName) { if (_config.WithCallConvs.TryGetValue(remappedName, out string callConv) || _config.WithCallConvs.TryGetValue("*", out callConv)) { - return callConv; + if (Enum.TryParse(callConv, true, out CallingConvention callConvEnum)) + { + return callConvEnum; + } + + AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported manually-specified calling convention: '{callConv}'. Determining convention from cursor.", cursor); } switch (callingConvention) { case CXCallingConv.CXCallingConv_C: { - return "Cdecl"; + return CallingConvention.Cdecl; } case CXCallingConv.CXCallingConv_X86StdCall: { - return isForFnptr ? "Stdcall" : "StdCall"; + return CallingConvention.StdCall; } case CXCallingConv.CXCallingConv_X86FastCall: { - return isForFnptr ? "Fastcall" : "FastCall"; + return CallingConvention.FastCall; } case CXCallingConv.CXCallingConv_X86ThisCall: { - return isForFnptr ? "Thiscall" : "ThisCall"; + return CallingConvention.ThisCall; } case CXCallingConv.CXCallingConv_Win64: { - return "Winapi"; + return CallingConvention.Winapi; } default: { - var name = "Winapi"; + const CallingConvention name = CallingConvention.Winapi; AddDiagnostic(DiagnosticLevel.Warning, $"Unsupported calling convention: '{callingConvention}'. Falling back to '{name}'.", cursor); return name; } @@ -1131,7 +1112,7 @@ string AddUsingDirectiveIfNeeded(string remappedName) { if (remappedName.Equals("Guid") || remappedName.Equals("IntPtr") || remappedName.Equals("UIntPtr")) { - _outputBuilder?.AddUsingDirective("System"); + _outputBuilder?.EmitSystemSupport(); } return remappedName; @@ -1417,8 +1398,8 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string { // Pointers are not yet supported as generic arguments; remap to IntPtr typeName = "IntPtr"; - _outputBuilder.AddUsingDirective("System"); - } + _outputBuilder.EmitSystemSupport(); + } nameBuilder.Append(typeName); @@ -1441,7 +1422,7 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string } else { - // The default name should be correct + // The default name should be correct } if (name.Contains("::")) @@ -1496,7 +1477,7 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi if (_config.GeneratePreviewCodeFnptr && (functionType is FunctionProtoType functionProtoType)) { var remappedName = GetRemappedName(name, cursor, tryRemapOperatorName: false); - var callConv = GetCallingConventionName(cursor, functionType.CallConv, remappedName, isForFnptr: true); + var callConv = GetCallingConvention(cursor, functionType.CallConv, remappedName); var needsReturnFixup = false; var returnTypeName = GetRemappedTypeName(cursor, context: null, functionType.ReturnType, out _); @@ -1517,10 +1498,10 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi { nameBuilder.Append(" unmanaged"); - if (callConv != "Winapi") + if (callConv != CallingConvention.Winapi) { nameBuilder.Append('['); - nameBuilder.Append(callConv); + nameBuilder.Append(callConv.AsString(true)); nameBuilder.Append(']'); } } @@ -2544,7 +2525,7 @@ private bool IsStmtAsWritten(Cursor cursor, out T value, bool removeParens = } } - private bool IsSupportedFixedSizedBufferType(string typeName) + internal static bool IsSupportedFixedSizedBufferType(string typeName) { switch (typeName) { @@ -3200,9 +3181,20 @@ private void ParenthesizeStmt(Stmt stmt) } else { - _outputBuilder.Write('('); - Visit(stmt); - _outputBuilder.Write(')'); + if (_stmtOutputBuilderUsers > 0) + { + _stmtOutputBuilder.Write('('); + _stmtOutputBuilder.BeginMarker("value"); + Visit(stmt); + _stmtOutputBuilder.EndMarker("value"); + _stmtOutputBuilder.Write(')'); + } + else + { + _outputBuilder.BeginInnerValue(); + Visit(stmt); + _outputBuilder.EndInnerValue(); + } } } @@ -3223,7 +3215,7 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false Debug.Assert(_outputBuilderUsers >= 1); _outputBuilderUsers++; - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); if (includeTestOutput && !string.IsNullOrWhiteSpace(_config.TestOutputLocation)) { @@ -3246,7 +3238,7 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false if (includeTestOutput && !string.IsNullOrWhiteSpace(_config.TestOutputLocation)) { - _testOutputBuilder = _outputBuilderFactory.Create($"{name}Tests", isTestOutput: true); + _testOutputBuilder = _outputBuilderFactory.CreateTests($"{name}Tests"); _testOutputBuilder.AddUsingDirective("System.Runtime.InteropServices"); @@ -3262,11 +3254,11 @@ private void StartUsingOutputBuilder(string name, bool includeTestOutput = false } else { - _outputBuilder.NeedsNewline = true; + _outputBuilder.WriteDivider(); if (includeTestOutput && !string.IsNullOrWhiteSpace(_config.TestOutputLocation)) { - _testOutputBuilder = _outputBuilderFactory.GetOutputBuilder($"{name}Tests"); + _testOutputBuilder = _outputBuilderFactory.GetTestOutputBuilder($"{name}Tests"); Debug.Assert(_testOutputBuilder.IsTestOutput); _testOutputBuilder.NeedsNewline = true; } @@ -3461,7 +3453,7 @@ private void UncheckStmt(string targetTypeName, Stmt stmt) { if (IsUnchecked(targetTypeName, stmt)) { - _outputBuilder.Write("unchecked"); + _outputBuilder.BeginUnchecked(); var needsCast = IsStmtAsWritten(stmt, out _, removeParens: true) && (stmt.DeclContext is EnumDecl); @@ -3501,17 +3493,20 @@ private void UncheckStmt(string targetTypeName, Stmt stmt) if (needsCast) { - _outputBuilder.Write("(("); - _outputBuilder.Write(targetTypeName); - _outputBuilder.Write(')'); + _outputBuilder.BeginInnerValue(); + _outputBuilder.BeginInnerCast(); + _outputBuilder.WriteCastType(targetTypeName); + _outputBuilder.EndInnerCast(); } ParenthesizeStmt(stmt); if (needsCast) { - _outputBuilder.Write(')'); + _outputBuilder.EndInnerValue(); } + + _outputBuilder.EndUnchecked(); } else { @@ -3522,6 +3517,7 @@ private void UncheckStmt(string targetTypeName, Stmt stmt) private void Visit(Cursor cursor) { var currentContext = _context.AddLast(cursor); + var currentStmtUsers = _stmtOutputBuilder is not null ? (int?)_stmtOutputBuilderUsers : null; if (cursor is Attr attr) { @@ -3544,6 +3540,7 @@ private void Visit(Cursor cursor) AddDiagnostic(DiagnosticLevel.Error, $"Unsupported cursor: '{cursor.CursorKindSpelling}'. Generated bindings may be incomplete.", cursor); } + Debug.Assert(currentStmtUsers == (_stmtOutputBuilder is not null ? (int?)_stmtOutputBuilderUsers : null)); Debug.Assert(_context.Last == currentContext); _context.RemoveLast(); } @@ -3567,53 +3564,25 @@ private void WithAttributes(string remappedName) { foreach (var attribute in attributes) { - if (attribute.Equals("Flags") || attribute.Equals("Obsolete")) - { - _outputBuilder.AddUsingDirective("System"); - } - else if (attribute.Equals("EditorBrowsable") || attribute.StartsWith("EditorBrowsable(")) - { - _outputBuilder.AddUsingDirective("System.ComponentModel"); - } - else if (attribute.StartsWith("Guid(")) - { - _outputBuilder.AddUsingDirective("System.Runtime.InteropServices"); - } - - _outputBuilder.WriteIndented('['); - _outputBuilder.Write(attribute); - _outputBuilder.WriteLine(']'); + _outputBuilder.WriteCustomAttribute(attribute); } } } - private void WithLibraryPath(string remappedName) + private string GetLibraryPath(string remappedName) { if (!_config.WithLibraryPaths.TryGetValue(remappedName, out string libraryPath) && !_config.WithLibraryPaths.TryGetValue("*", out libraryPath)) { - _outputBuilder.Write(_config.LibraryPath); + return _config.LibraryPath; } else { - _outputBuilder.Write('"'); - _outputBuilder.Write(libraryPath); - _outputBuilder.Write('"'); + return libraryPath; } } - private void WithSetLastError(string remappedName) - { - if (_config.WithSetLastErrors.Contains("*") || _config.WithSetLastErrors.Contains(remappedName)) - { - _outputBuilder.Write(','); - _outputBuilder.Write(' '); - _outputBuilder.Write("SetLastError"); - _outputBuilder.Write(' '); - _outputBuilder.Write('='); - _outputBuilder.Write(' '); - _outputBuilder.Write("true"); - } - } + private bool GetSetLastError(string remappedName) => _config.WithSetLastErrors.Contains("*") || + _config.WithSetLastErrors.Contains(remappedName); private void WithTestAttribute() { @@ -3707,9 +3676,34 @@ private void WithUsings(string remappedName) { foreach (var @using in usings) { - _outputBuilder.AddUsingDirective(@using); + _outputBuilder.EmitUsingDirective(@using); } } } + + private CSharpOutputBuilder StartCSharpCode() + { + if (_stmtOutputBuilder is null) + { + _stmtOutputBuilder = _outputBuilder.BeginCSharpCode(); + _stmtOutputBuilderUsers = 1; + } + else + { + _stmtOutputBuilderUsers++; + } + + return _stmtOutputBuilder; + } + + private void StopCSharpCode() + { + _stmtOutputBuilderUsers--; + if (_stmtOutputBuilderUsers <= 0) + { + _outputBuilder.EndCSharpCode(_stmtOutputBuilder); + _stmtOutputBuilder = null; + } + } } } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 7e32e73f..5b00a6e9 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -19,7 +19,7 @@ public sealed class PInvokeGeneratorConfiguration private readonly Dictionary> _withUsings; private readonly PInvokeGeneratorConfigurationOptions _options; - public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) + public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, string outputLocation, string testOutputLocation, PInvokeGeneratorOutputMode outputMode = PInvokeGeneratorOutputMode.CSharp, PInvokeGeneratorConfigurationOptions options = PInvokeGeneratorConfigurationOptions.None, string[] excludedNames = null, string headerFile = null, string methodClassName = null, string methodPrefixToStrip = null, IReadOnlyDictionary remappedNames = null, string[] traversalNames = null, IReadOnlyDictionary> withAttributes = null, IReadOnlyDictionary withCallConvs = null, IReadOnlyDictionary withLibraryPaths = null, string[] withSetLastErrors = null, IReadOnlyDictionary withTypes = null, IReadOnlyDictionary> withUsings = null) { if (excludedNames is null) { @@ -46,6 +46,21 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s throw new ArgumentNullException(nameof(namespaceName)); } + if (outputMode != PInvokeGeneratorOutputMode.CSharp && outputMode != PInvokeGeneratorOutputMode.Xml) + { + throw new ArgumentOutOfRangeException(nameof(options)); + } + + if (outputMode == PInvokeGeneratorOutputMode.Xml && + (options & PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles) == 0 && + ((options & PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit) != 0 || + (options & PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit) != 0)) + { + // we can't mix XML and C#! we're in XML mode, not generating multiple files, and generating tests; fail + throw new ArgumentException("Can't generate tests in XML mode without multiple files.", + nameof(options)); + } + if (options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode) && options.HasFlag(PInvokeGeneratorConfigurationOptions.GeneratePreviewCode)) { throw new ArgumentOutOfRangeException(nameof(options)); @@ -80,6 +95,7 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s MethodClassName = methodClassName; MethodPrefixToStrip = methodPrefixToStrip; Namespace = namespaceName; + OutputMode = outputMode; OutputLocation = Path.GetFullPath(outputLocation); TestOutputLocation = !string.IsNullOrWhiteSpace(testOutputLocation) ? Path.GetFullPath(testOutputLocation) : string.Empty; @@ -167,6 +183,8 @@ public PInvokeGeneratorConfiguration(string libraryPath, string namespaceName, s public string Namespace { get; } + public PInvokeGeneratorOutputMode OutputMode { get; } + public string OutputLocation { get; } public IReadOnlyDictionary RemappedNames => _remappedNames; diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorOutputMode.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorOutputMode.cs new file mode 100644 index 00000000..2f112ad7 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorOutputMode.cs @@ -0,0 +1,8 @@ +namespace ClangSharp +{ + public enum PInvokeGeneratorOutputMode + { + CSharp, + Xml + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.Visit.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.Visit.cs new file mode 100644 index 00000000..b48cc40a --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.Visit.cs @@ -0,0 +1,31 @@ +namespace ClangSharp.XML +{ + internal partial class XmlOutputBuilder + { + public void WriteCustomAttribute(string attribute) + => _sb.AppendLine($"{attribute}"); + public void WriteIid(string iidName, string iidValue) + { + _sb.Append(""); + } + + public void WriteDivider(bool force = false) + { + // nop, used only by C# + } + + public void SuppressDivider() + { + // nop, used only by C# + } + + public void EmitUsingDirective(string directive) + { + // nop, used only by C# + } + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs new file mode 100644 index 00000000..bba4813b --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.VisitDecl.cs @@ -0,0 +1,372 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using ClangSharp.Abstractions; +using ClangSharp.CSharp; + +namespace ClangSharp.XML +{ + internal partial class XmlOutputBuilder + { + private StringBuilder _sb = new(); + public void BeginInnerValue() => _sb.Append(""); + public void EndInnerValue() => _sb.Append(""); + + public void BeginInnerCast() => _sb.Append(""); + public void WriteCastType(string targetTypeName) => _sb.Append(targetTypeName); + public void EndInnerCast() => _sb.Append(""); + + public void BeginUnchecked() => _sb.Append(""); + public void EndUnchecked() => _sb.Append(""); + + public void BeginConstant(in ConstantDesc desc) + { + _sb.Append((desc.Kind & ConstantKind.Enumerator) == 0 + ? $"" + : $""); + _sb.Append($""); + _sb.Append(desc.TypeName.Replace("<", "<").Replace(">", ">")); + _sb.Append(""); + } + + public void BeginConstantValue(bool isGetOnlyProperty = false) => _sb.Append(""); + public void WriteConstantValue(long value) => _sb.Append(value); + public void WriteConstantValue(ulong value) => _sb.Append(value); + public void EndConstantValue() => _sb.Append(""); + public void EndConstant(bool isConstant) => _sb.Append(isConstant ? "" : ""); + + public void BeginEnum(AccessSpecifier accessSpecifier, string typeName, string escapedName, string nativeTypeName) + => _sb.Append($"{typeName}"); + + public void EndEnum() => _sb.Append(""); + + public void BeginField(in FieldDesc desc) + { + _sb.Append($"'); + _sb.Append($" _sb.Append($" count=\"{count}\" fixed=\"{fixedName}\">" + + $"{typeName.Replace("<", "<").Replace(">", ">")}"); + + public void WriteRegularField(string typeName, string escapedName) + => _sb.Append($">{typeName.Replace("<", "<").Replace(">", ">")}"); + public void EndField(bool isBodyless = true) => _sb.Append(""); + public void BeginFunctionOrDelegate( + in FunctionOrDelegateDesc desc, ref bool isMethodClassUnsafe) + { + if (desc.IsVirtual) + { + Debug.Assert(!desc.HasFnPtrCodeGen); + _sb.Append($"'); + + desc.WriteCustomAttrs(desc.CustomAttrGeneratorData); + _sb.Append("'); + } + + public void WriteReturnType(string typeString) + { + _sb.Append(typeString.Replace("<", "<").Replace(">", ">")); + _sb.Append(""); + } + + public void BeginFunctionInnerPrototype(string escapedName) + { + // nop, only used in C# + } + + public void BeginParameter(in ParameterDesc info) + { + _sb.Append($""); + info.WriteCustomAttrs(info.CustomAttrGeneratorData); + _sb.Append(""); + _sb.Append(info.Type.Replace("<", "<").Replace(">", ">")); + _sb.Append(""); + } + + public void BeginParameterDefault() + { + _sb.Append(""); + } + + public void EndParameterDefault() + { + _sb.Append(""); + } + + public void EndParameter() + { + _sb.Append(""); + } + + public void WriteParameterSeparator() + { + // nop, used only in C# + } + + public void EndFunctionInnerPrototype() + { + // nop, used only in C# + } + + public void BeginBody(bool isExpressionBody = false) + { + // nop, used only by C# + } + + public void BeginConstructorInitializers() + { + // nop, method only exists for consistency and/or future use + } + + public void BeginConstructorInitializer(string memberRefName, string memberInitName) + { + // "hint" is the name we're initializing using, but should only be used as a "hint" rather than a definitive + // value, which is contained within the init block. + _sb.Append($""); + } + + public void EndConstructorInitializer() => _sb.Append(""); + + public void EndConstructorInitializers() + { + // nop, method only exists for consistency and/or future use + } + + public void BeginInnerFunctionBody() + { + _sb.Append(""); + } + + public void EndInnerFunctionBody() + { + _sb.Append(""); + } + + public void EndBody(bool isExpressionBody = false) + { + // nop, used only by C# + } + + public void EndFunctionOrDelegate(bool isVirtual, bool _) + => _sb.Append(isVirtual ? "" : ""); + + public void BeginStruct(in StructDesc info) + { + _sb.Append("'); + info.WriteCustomAttrs(info.CustomAttrGeneratorData); + } + + public void BeginExplicitVtbl() => _sb.Append(""); + public void EndExplicitVtbl() => _sb.Append(""); + + public void EndStruct() => _sb.Append(""); + + public void EmitCompatibleCodeSupport() + { + // nop, used only by C# + } + + public void EmitFnPtrSupport() + { + // nop, used only by C# + } + + public void EmitSystemSupport() + { + // nop, used only by C# + } + + public CSharpOutputBuilder BeginCSharpCode() + { + _sb.Append(""); + return new CSharpOutputBuilder("__Internal", markerMode: MarkerMode.Xml); + } + + public void EndCSharpCode(CSharpOutputBuilder output) + { + output.WritePendingLine(); + foreach (var s in output.Contents) + { + _sb.AppendLine(s.Replace("&", "&") + .Replace("<", "<") + .Replace(">", ">") + .Replace("/*M*/<", "<") + .Replace("/*M*/>", ">")); + } + + _sb.Append(""); + } + + public void BeginGetter(bool aggressivelyInlined) + { + _sb.Append("'); + } + + public void EndGetter() + { + _sb.Append(""); + } + + public void BeginSetter(bool aggressivelyInlined) + { + _sb.Append("'); + } + + public void EndSetter() + { + _sb.Append(""); + } + + public void BeginIndexer(AccessSpecifier accessSpecifier, bool isUnsafe) + { + _sb.Append("" : "\">"); + } + + public void WriteIndexer(string typeName) + { + _sb.Append(""); + _sb.Append(typeName.Replace("<", "<").Replace(">", ">")); + _sb.Append(""); + } + + public void BeginIndexerParameters() + { + // nop, used only by C# + } + + public void EndIndexerParameters() + { + // nop, used only by C# + } + + public void EndIndexer() => _sb.Append(""); + + public void BeginDereference() => _sb.Append(""); + + public void EndDereference() => _sb.Append(""); + } +} diff --git a/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.cs b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.cs new file mode 100644 index 00000000..cb948376 --- /dev/null +++ b/sources/ClangSharp.PInvokeGenerator/XML/XmlOutputBuilder.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using ClangSharp.Abstractions; + +namespace ClangSharp.XML +{ + internal partial class XmlOutputBuilder : IOutputBuilder + { + public XmlOutputBuilder(string name) + { + Name = name; + } + + public string Name { get; } + public string Extension { get; } = ".xml"; + public bool IsTestOutput { get; } = false; + + public IEnumerable Contents + { + get + { + StringWriter sw = new(); + XmlWriter writer = XmlWriter.Create(sw, new() + { + Indent = true, + IndentChars = " ", + ConformanceLevel = ConformanceLevel.Fragment + }); + + foreach (var node in XElement.Parse("" + _sb + "").Nodes()) + { + node.WriteTo(writer); + } + + writer.Flush(); + return sw.ToString().Split('\n'); + } + } + } +} diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index 5a4eda1e..da4b5a93 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -48,6 +48,7 @@ public static async Task Main(params string[] args) AddWithSetLastErrorOption(s_rootCommand); AddWithTypeOption(s_rootCommand); AddWithUsingOption(s_rootCommand); + AddOutputModeOption(s_rootCommand); return await s_rootCommand.InvokeAsync(args); } @@ -78,6 +79,7 @@ public static int Run(InvocationContext context) var withSetLastErrors = context.ParseResult.ValueForOption("--with-setlasterror"); var withTypeNameValuePairs = context.ParseResult.ValueForOption("--with-type"); var withUsingNameValuePairs = context.ParseResult.ValueForOption("--with-using"); + var outputMode = context.ParseResult.ValueForOption("--output-mode"); var errorList = new List(); @@ -342,7 +344,7 @@ public static int Run(InvocationContext context) translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited - var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withLibraryPath, withSetLastErrors, withTypes, withUsings); + var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, outputMode, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withLibraryPath, withSetLastErrors, withTypes, withUsings); if (config.GenerateMacroBindings) { @@ -848,5 +850,20 @@ private static void AddWithUsingOption(RootCommand rootCommand) rootCommand.AddOption(option); } + + private static void AddOutputModeOption(RootCommand rootCommand) + { + var option = new Option(new string[] { "--output-mode", "-om" }, "The mode describing how the information collected from the headers are presented in the resultant bindings.") + { + Argument = new Argument("") + { + ArgumentType = typeof(PInvokeGeneratorOutputMode), + Arity = ArgumentArity.ExactlyOne + } + }; + option.Argument.SetDefaultValue(PInvokeGeneratorOutputMode.CSharp); + + rootCommand.AddOption(option); + } } } diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs index 36dd5f5c..141592d3 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/PInvokeGeneratorTest.cs @@ -38,7 +38,7 @@ protected async Task ValidateGeneratedBindingsAsync(string inputContents, string using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents); var unsavedFiles = new CXUnsavedFile[] { unsavedFile }; - var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), testOutputLocation: null, configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTypes, withUsings); + var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), testOutputLocation: null, PInvokeGeneratorOutputMode.CSharp, configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTypes, withUsings); using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream)) {