├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── include ├── AST │ ├── CppDeclarationTree.h │ └── CppTypeDeclaration.h ├── COFFImportLibrary.h ├── CodeGeneration.h ├── GeneratedHeaderFile.h ├── HeaderGenerator.h ├── HeaderGeneratorConfig.h ├── TypeDependencyCollector.h └── Utils │ ├── DiaUtils.h │ ├── MemoryGrowableBuffer.h │ ├── StringUtils.h │ ├── TextWriter.h │ └── TopoSort.h ├── resources ├── config-libraries.json ├── config-stdlib.json └── config-windows.json └── src ├── AST ├── CppDeclarationTree.cpp └── CppTypeDeclaration.cpp ├── COFFImportLibrary.cpp ├── CodeGeneration.cpp ├── GeneratedHeaderFile.cpp ├── HeaderGenerator.cpp ├── HeaderGeneratorConfig.cpp ├── TypeDependencyCollector.cpp ├── Utils ├── DiaUtils.cpp ├── MemoryGrowableBuffer.cpp ├── StringUtils.cpp └── TextWriter.cpp └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | out/ 35 | dependencies/ 36 | resources_local/ 37 | 38 | cmake-build-debug/ 39 | cmake-build-release/ 40 | .idea/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | project(PDBHeaderGenerator) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | file(GLOB_RECURSE SOURCE_FILES src/*.cpp ) 7 | string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} SYSTEM_PROCESSOR_LOWERCASE) 8 | 9 | add_executable(PDBHeaderGenerator ${SOURCE_FILES} ) 10 | target_include_directories(PDBHeaderGenerator PUBLIC include) 11 | 12 | # DIA SDK dependency 13 | target_include_directories(PDBHeaderGenerator PRIVATE dependencies/DIA_SDK/include) 14 | target_link_libraries(PDBHeaderGenerator PRIVATE ${CMAKE_CURRENT_LIST_DIR}/dependencies/DIA_SDK/lib/${SYSTEM_PROCESSOR_LOWERCASE}/diaguids.lib) 15 | 16 | # nlohmann-json dependency 17 | add_subdirectory(dependencies/nlohmann-json) 18 | target_link_libraries(PDBHeaderGenerator PRIVATE nlohmann_json::nlohmann_json) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Archengius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/AST/CppDeclarationTree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "CppTypeDeclaration.h" 10 | 11 | class FormattedTextWriter; 12 | 13 | struct DeclarationPrintRules 14 | { 15 | // Base rules for type formatting 16 | TypeFormattingRules BaseTypeFormatRules; 17 | 18 | DeclarationPrintRules AppendScope( const std::wstring& InScopeElement ) const 19 | { 20 | DeclarationPrintRules NewRules; 21 | NewRules.BaseTypeFormatRules = BaseTypeFormatRules.AppendScope( InScopeElement ); 22 | return NewRules; 23 | } 24 | }; 25 | 26 | class ITopLevelDeclaration 27 | { 28 | public: 29 | // Namespace in which declaration resides. Declarations in the same namespace can share the namespace block 30 | std::wstring Namespace; 31 | // Top level declarations that this declaration depends on. Used to sort the dependencies. 32 | std::unordered_set Dependencies; 33 | std::wstring Comment; 34 | 35 | virtual ~ITopLevelDeclaration() = default; 36 | virtual void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const = 0; 37 | virtual bool IsInlineDeclaration() const { return true; } 38 | virtual const wchar_t* GetDeclarationTypeName() = 0; 39 | }; 40 | 41 | class CppFile final 42 | { 43 | public: 44 | std::wstring FileName; 45 | bool bIsHeaderFile{false}; 46 | std::vector SystemIncludes; 47 | std::vector LocalIncludes; 48 | std::vector> Declarations; 49 | DeclarationPrintRules FormatRules; 50 | 51 | explicit CppFile( DataModel::EDataModel InDataModel, uint32_t InTypeFormatFlags ); 52 | void Print(FormattedTextWriter& TextWriter) const; 53 | void WriteToFile(const std::filesystem::path& DirectoryPath) const; 54 | }; 55 | 56 | class TypedefDeclaration final : public ITopLevelDeclaration 57 | { 58 | public: 59 | std::wstring TypeName; 60 | std::wstring TypedefName; 61 | 62 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 63 | const wchar_t* GetDeclarationTypeName() override { return L"typedef"; } 64 | }; 65 | 66 | class PredeclarationStatement final : public ITopLevelDeclaration 67 | { 68 | std::shared_ptr PredeclarationType; 69 | public: 70 | explicit PredeclarationStatement( const std::shared_ptr& InTypeStatement ); 71 | 72 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 73 | const wchar_t* GetDeclarationTypeName() override { return L"predeclaration"; } 74 | }; 75 | 76 | enum class CppAccessModifier : uint8_t 77 | { 78 | Private, 79 | Protected, 80 | Public 81 | }; 82 | 83 | const wchar_t* CppAccessModifierToString( CppAccessModifier AccessModifier ); 84 | CppAccessModifier DefaultAccessModifierForCppUDTKind( CppUDTKind Kind ); 85 | 86 | struct UDTDeclarationBaseClass 87 | { 88 | std::shared_ptr BaseClass; 89 | CppAccessModifier AccessModifier{}; 90 | bool bIsVirtual{false}; 91 | 92 | void Print(FormattedTextWriter& TextWriter, CppUDTKind ClassKind, const TypeFormattingRules& Rules) const; 93 | }; 94 | 95 | class IUDTDeclarationMember 96 | { 97 | public: 98 | std::wstring MemberName; 99 | CppAccessModifier AccessModifier{}; 100 | std::unordered_set Dependencies; 101 | int32_t Priority{0}; 102 | std::wstring Comment; 103 | 104 | virtual ~IUDTDeclarationMember() = default; 105 | virtual void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const = 0; 106 | }; 107 | 108 | struct UDTDeclarationData; 109 | struct EnumDeclarationData; 110 | 111 | class UDTDataMemberDeclaration final : public IUDTDeclarationMember 112 | { 113 | public: 114 | std::shared_ptr MemberType; 115 | int32_t BitfieldSize{-1}; 116 | bool bIsStatic{false}; 117 | bool bIsConst{false}; 118 | bool bIsThreadLocal{false}; 119 | std::wstring ConstantValue; 120 | bool bIsTemplateSpecialization{false}; 121 | std::vector TemplateArguments; 122 | bool bIsConstexpr{false}; 123 | bool bWantsDefaultInitializer{false}; 124 | 125 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 126 | }; 127 | 128 | class UDTFunctionDeclaration final : public IUDTDeclarationMember 129 | { 130 | public: 131 | std::shared_ptr ReturnType; 132 | std::vector>> ParameterNamesAndTypes; 133 | bool bIsVirtual{false}; 134 | bool bIsConst{false}; 135 | bool bIsPureVirtual{false}; 136 | bool bIsOverride{false}; 137 | bool bIsStatic{false}; 138 | bool bNoReturnType{false}; 139 | bool bIsExplicit{false}; 140 | bool bIsTemplateSpecialization{false}; 141 | std::vector TemplateArguments; 142 | bool bIsVariadicArguments{false}; 143 | std::wstring InlineImplementation; 144 | 145 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 146 | }; 147 | 148 | struct UDTDeclarationData 149 | { 150 | CppUDTKind Kind{}; 151 | std::wstring ClassName; 152 | bool bIsFinal{false}; 153 | int32_t AlignmentModifier{0}; 154 | std::vector BaseClasses; 155 | std::vector> Members; 156 | bool bIsTemplateSpecialization{false}; 157 | std::vector TemplateArguments; 158 | bool bIsDllImport{false}; 159 | 160 | void PrintUDT(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, bool bIsAnonymousType = false) const; 161 | }; 162 | 163 | struct EnumDeclarationData 164 | { 165 | std::wstring EnumName; 166 | std::shared_ptr UnderlyingType; 167 | bool bIsScoped{false}; 168 | // If the enumeration is signed, values will be interpreted as signed int64 169 | std::vector> Values; 170 | 171 | void PrintEnum(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, bool bIsAnonymousEnum = false) const; 172 | }; 173 | 174 | class UDTNestedTypeDeclaration final : public IUDTDeclarationMember 175 | { 176 | std::shared_ptr Data; 177 | public: 178 | explicit UDTNestedTypeDeclaration( const std::shared_ptr& InDeclData ); 179 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 180 | }; 181 | 182 | class UDTNestedEnumDeclaration final : public IUDTDeclarationMember 183 | { 184 | std::shared_ptr Data; 185 | public: 186 | explicit UDTNestedEnumDeclaration( const std::shared_ptr& InDeclData ); 187 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 188 | }; 189 | 190 | class UDTDeclaration final : public ITopLevelDeclaration 191 | { 192 | std::shared_ptr Data; 193 | public: 194 | explicit UDTDeclaration( const std::shared_ptr& InDeclData ); 195 | 196 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 197 | bool IsInlineDeclaration() const override { return false; } 198 | const wchar_t* GetDeclarationTypeName() override { return L"type"; } 199 | }; 200 | 201 | class EnumDeclaration final : public ITopLevelDeclaration 202 | { 203 | std::shared_ptr Data; 204 | public: 205 | explicit EnumDeclaration( const std::shared_ptr& InDeclData ); 206 | 207 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 208 | bool IsInlineDeclaration() const override { return false; } 209 | const wchar_t* GetDeclarationTypeName() override { return L"enum"; } 210 | }; 211 | 212 | class GlobalDataDeclaration final : public ITopLevelDeclaration 213 | { 214 | public: 215 | std::wstring DataName; 216 | std::shared_ptr DataType; 217 | bool bIsExternCLinkage{false}; 218 | bool bIsThreadLocal{false}; 219 | std::wstring ConstantValue; 220 | bool bIsTemplateSpecialization{false}; 221 | std::vector TemplateArguments; 222 | bool bIsConstexpr{false}; 223 | bool bIsDllImport{false}; 224 | 225 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 226 | const wchar_t* GetDeclarationTypeName() override { return L"data"; } 227 | }; 228 | 229 | class GlobalFunctionDeclaration final : public ITopLevelDeclaration 230 | { 231 | public: 232 | std::wstring FunctionName; 233 | std::shared_ptr ReturnType; 234 | std::vector>> ParameterNamesAndTypes; 235 | bool bIsExternCLinkage{false}; 236 | bool bIsTemplateSpecialization{false}; 237 | std::vector TemplateArguments; 238 | bool bIsVariadicArguments{false}; 239 | bool bIsDllImport{false}; 240 | 241 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 242 | const wchar_t* GetDeclarationTypeName() override { return L"function"; } 243 | }; 244 | 245 | class AnonymousUDTTypeDeclaration final : public ITypeDeclaration 246 | { 247 | public: 248 | std::shared_ptr Data; 249 | bool bIsConst{false}; 250 | 251 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::AnonymousUDT; } 252 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 253 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 254 | bool IsInlineDeclaration() const override { return false; } 255 | std::shared_ptr Clone() const override; 256 | size_t GetDeclarationHash() const override; 257 | }; 258 | 259 | class AnonymousEnumTypeDeclaration final : public ITypeDeclaration 260 | { 261 | public: 262 | std::shared_ptr Data; 263 | bool bIsConst{false}; 264 | 265 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::AnonymousEnum; } 266 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 267 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 268 | bool IsInlineDeclaration() const override { return false; } 269 | std::shared_ptr Clone() const override; 270 | size_t GetDeclarationHash() const override; 271 | }; 272 | 273 | enum class ETemplateDeclarationArgumentType 274 | { 275 | Typename, 276 | TypeValue, 277 | }; 278 | 279 | struct TemplateDeclarationArgument 280 | { 281 | ETemplateDeclarationArgumentType Type{}; 282 | std::shared_ptr TypeValueKind; 283 | 284 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const; 285 | }; 286 | 287 | class TemplateTypeDeclaration final : public ITopLevelDeclaration 288 | { 289 | public: 290 | std::vector Arguments; 291 | CppUDTKind UDTKind{}; 292 | std::wstring ClassName; 293 | 294 | void Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const override; 295 | const wchar_t* GetDeclarationTypeName() override { return L"template"; } 296 | }; 297 | -------------------------------------------------------------------------------- /include/AST/CppTypeDeclaration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class FormattedTextWriter; 12 | 13 | namespace DataModel 14 | { 15 | enum EDataModel : uint8_t 16 | { 17 | // int is 32 bit, long is 32 bit, pointer is 64 bit. Used by x64 Windows 18 | LLP64 19 | }; 20 | struct DataModelDefinition 21 | { 22 | int32_t CharSizeBytes{}; 23 | int32_t ShortIntSizeBytes{}; 24 | int32_t IntSizeBytes{}; 25 | int32_t LongIntSizeBytes{}; 26 | int32_t LongLongIntSizeBytes{}; 27 | int32_t PointerSizeBytes{}; 28 | }; 29 | 30 | // Returns the data model definition for the given model enum 31 | DataModelDefinition GetDataModelDefinition( EDataModel InDataModel ); 32 | } 33 | 34 | class TypeFormattingRules 35 | { 36 | uint32_t Flags{0}; 37 | uint32_t AdditionalFlagsNonInheritable{0}; 38 | uint32_t FlagsMaskNonInheritable{0}; 39 | DataModel::EDataModel DataModel{DataModel::LLP64}; 40 | std::wstring CurrentScope; 41 | public: 42 | enum ETypeFormattingFlags : uint32_t 43 | { 44 | None = 0x00000000, 45 | // Int has a fixed length 46 | FixedLengthInt = 0x00000001, 47 | // Print fixed length integer types (MS specific types and all ints if FixedLengthInt is used) using cstdint defines, such as int64_t 48 | Cstdint = 0x00000002, 49 | // Emit CSU specifiers when emitting type 50 | EmitCSUSpecifier = 0x00000004, 51 | // Enum enum specifier when emitting type 52 | EmitEnumSpecifier = 0x00000008, 53 | // Do not emit outer scope and type 54 | SkipOuterScope = 0x00000010, 55 | // Emit function calling conventions 56 | EmitCallingConvention = 0x00000020, 57 | }; 58 | 59 | TypeFormattingRules() = default; 60 | TypeFormattingRules( const DataModel::EDataModel InDataModel, const uint32_t InFlags ) : Flags( InFlags ), DataModel( InDataModel ) {} 61 | 62 | DataModel::EDataModel GetDataModel() const { return DataModel; } 63 | 64 | bool HasAnyFlags(const uint32_t InFlags ) const 65 | { 66 | return (( Flags & ~FlagsMaskNonInheritable | AdditionalFlagsNonInheritable ) & InFlags) != 0; 67 | } 68 | bool HasAllFlags(const uint32_t InFlags ) const 69 | { 70 | return (( Flags & ~FlagsMaskNonInheritable | AdditionalFlagsNonInheritable ) & InFlags) == InFlags; 71 | } 72 | 73 | TypeFormattingRules AppendScope( const std::wstring& InScope ) const 74 | { 75 | TypeFormattingRules NewRules = *this; 76 | if ( !NewRules.CurrentScope.empty() && !InScope.empty() ) 77 | { 78 | NewRules.CurrentScope.append(L"::"); 79 | } 80 | NewRules.CurrentScope.append( InScope ); 81 | return NewRules; 82 | } 83 | std::wstring GetRelativeScope( const std::wstring& InScope ) const 84 | { 85 | int32_t Offset1 = 0; 86 | int32_t Offset2 = 0; 87 | while ( true ) 88 | { 89 | const int32_t NewOffset1 = CurrentScope.find( L"::", Offset1 ); 90 | const int32_t NewOffset2 = InScope.find( L"::", Offset2 ); 91 | 92 | const std::wstring_view Segment1( &CurrentScope[Offset1], ( NewOffset1 == std::wstring::npos ? CurrentScope.size() : NewOffset1 ) - Offset1 ); 93 | const std::wstring_view Segment2( &InScope[Offset2], ( NewOffset2 == std::wstring::npos ? InScope.size() : NewOffset2 ) - Offset2 ); 94 | 95 | // Paths were the same before but are now different 96 | if ( Segment1 != Segment2 ) 97 | { 98 | return InScope.substr(Offset1); 99 | } 100 | // There is no segments left in the target scope. The relative scope is empty. 101 | if ( NewOffset2 == std::wstring::npos ) 102 | { 103 | return std::wstring(); 104 | } 105 | // There is no segments left in the current scope, take the remainder of the path 106 | if ( NewOffset1 == std::wstring::npos ) 107 | { 108 | return InScope.substr( NewOffset2 + 2 ); 109 | } 110 | // Path segments are still matching, update the offsets 111 | Offset1 = NewOffset1 + 2; 112 | Offset2 = NewOffset2 + 2; 113 | } 114 | } 115 | 116 | // Filters out non-inheritable flags 117 | TypeFormattingRules InheritableFlags() const 118 | { 119 | return TypeFormattingRules{ DataModel, Flags }; 120 | } 121 | 122 | // Excludes (inheritable) flags 123 | TypeFormattingRules ExcludeFlags( const uint32_t InFlags ) const 124 | { 125 | TypeFormattingRules NewRules = *this; 126 | NewRules.Flags &= ~InFlags; 127 | return NewRules; 128 | } 129 | // Includes (inheritable) flags 130 | TypeFormattingRules IncludeFlags( const uint32_t InFlags ) const 131 | { 132 | TypeFormattingRules NewRules = *this; 133 | NewRules.Flags |= InFlags; 134 | return NewRules; 135 | } 136 | // Explicitly excludes specific flags, even if they are set as inheritable. Only works for the current type, not inherited by nested type declarations. 137 | TypeFormattingRules MaskFlagsNonInheritable( const uint32_t InFlags ) const 138 | { 139 | TypeFormattingRules NewRules = *this; 140 | NewRules.FlagsMaskNonInheritable |= InFlags; 141 | NewRules.AdditionalFlagsNonInheritable &= ~NewRules.FlagsMaskNonInheritable; 142 | return NewRules; 143 | } 144 | // Sets specific flags for the current type without modifying inherited flags. Only works for the current type, not inherited by nested type declarations. 145 | TypeFormattingRules AppendFlagsNonInheritable( const uint32_t InFlags ) const 146 | { 147 | TypeFormattingRules NewRules = *this; 148 | NewRules.AdditionalFlagsNonInheritable |= InFlags; 149 | NewRules.FlagsMaskNonInheritable &= ~NewRules.AdditionalFlagsNonInheritable; 150 | return NewRules; 151 | } 152 | }; 153 | 154 | enum class CppUDTKind : uint8_t 155 | { 156 | Class, 157 | Struct, 158 | Union 159 | }; 160 | 161 | const wchar_t* CppUDTKindToString( CppUDTKind Kind ); 162 | 163 | enum class ETypeDeclarationId : uint32_t 164 | { 165 | Generic, 166 | PointerType, 167 | FundamentalType, 168 | VoidType, 169 | FunctionType, 170 | UDT, 171 | Enum, 172 | AnonymousUDT, 173 | AnonymousEnum, 174 | ArrayType, 175 | InternalType, 176 | WildcardType, 177 | }; 178 | 179 | class ITypeDeclaration; 180 | struct TypeMemberReference; 181 | 182 | class TypeDeclarationMatchContext 183 | { 184 | std::unordered_map> WildcardMatches; 185 | std::unordered_map IntegerWildcardMatches; 186 | std::unordered_map TypeMemberReferenceWildcardMatches; 187 | public: 188 | bool MatchWildcard( int32_t InWildcardIndex, bool bIsWildcardConst, const std::shared_ptr& InMatchAgainst ); 189 | bool MatchIntegerWildcard( int32_t InWildcardIndex, int64_t InIntegerConstant ); 190 | bool MatchTypeMemberReferenceWildcard( int32_t InWildcardIndex, const TypeMemberReference& InTypeMemberReference ); 191 | 192 | std::shared_ptr SubstituteWildcard( int32_t InWildcardIndex, bool bIsWildcardConst ) const; 193 | int64_t SubstituteIntegerWildcard( int32_t InWildcardIndex ) const; 194 | TypeMemberReference SubstituteTypeMemberReferenceWildcard( int32_t InWildcardIndex ) const; 195 | }; 196 | 197 | struct TypeMemberReference 198 | { 199 | std::shared_ptr OwnerType; 200 | std::wstring MemberName; 201 | 202 | friend bool operator==(const TypeMemberReference& A, const TypeMemberReference& B); 203 | bool Match(const TypeMemberReference& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const; 204 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const; 205 | TypeMemberReference Substitute(const TypeDeclarationMatchContext& MatchContext) const; 206 | TypeMemberReference Clone() const; 207 | size_t GetTypeMemberReferenceHash() const; 208 | }; 209 | 210 | class ITypeDeclaration : public std::enable_shared_from_this 211 | { 212 | public: 213 | // Note: some types cannot be CV-qualified, in that case bIsConst value is ignored 214 | bool bIsConst{false}; 215 | 216 | virtual ~ITypeDeclaration() = default; 217 | 218 | virtual ETypeDeclarationId GetId() const = 0; 219 | virtual void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const = 0; 220 | virtual bool IsInlineDeclaration() const { return true; } 221 | virtual bool Identical( const std::shared_ptr& InOtherDeclaration ) const = 0; 222 | virtual bool Match( const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext ) const; 223 | virtual std::shared_ptr Clone() const = 0; 224 | virtual std::shared_ptr Substitute( const TypeDeclarationMatchContext& MatchContext ); 225 | virtual size_t GetDeclarationHash() const = 0; 226 | 227 | // Prints the type in addition to the variable name. Default implementation just appends the variable name to the type 228 | virtual void PrintVariableType(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const std::wstring& VariableName) const; 229 | 230 | // Parses type declaration from text 231 | static std::shared_ptr ParseTypeDeclaration( const std::wstring& InText, bool bAllowWildcardTypes = false ); 232 | 233 | static bool StaticIdentical( const std::shared_ptr& A, const std::shared_ptr& B ); 234 | static bool StaticMatch( const std::shared_ptr& Match, const std::shared_ptr& MatchAgainst, TypeDeclarationMatchContext& MatchContext ); 235 | static std::shared_ptr StaticSubstitute( const std::shared_ptr& Original, const TypeDeclarationMatchContext& MatchContext ); 236 | static size_t StaticGetDeclarationHash( const std::shared_ptr& Decl ); 237 | }; 238 | 239 | class PointerTypeDeclaration final : public ITypeDeclaration 240 | { 241 | public: 242 | std::shared_ptr PointeeType; 243 | bool bIsReference{false}; 244 | std::shared_ptr OwnerType; 245 | 246 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::PointerType; } 247 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 248 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 249 | bool Match(const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const override; 250 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 251 | std::shared_ptr Clone() const override; 252 | size_t GetDeclarationHash() const override; 253 | }; 254 | 255 | enum class EBasicType : int32_t 256 | { 257 | Char, // length: 1 byte, can be signed or unsigned 258 | Int, // length: implementation defined & can be influenced by type modifiers. Possible values: 2, 4, 8 bytes. can be signed or unsigned 259 | Bool, // length: implementation defined, usually 1. 260 | WideChar, // length: implementation defined, 2 on windows, 4 on linux 261 | Char8, // length: implementation defined, at least 1 262 | Char16, // length: implementation defined, at least 2 263 | Char32, // length: implementation defined, at least 4 264 | Nullptr, // length: implementation defined, same as pointer to void 265 | Float, // length: implementation defined, usually 4 bytes 266 | Double, // length: implementatino defined, usually 8 bytes 267 | 268 | // Microsoft extension: Fixed length types __int64, __int32, __int16, __int8 269 | FixedLengthInt8, // length: 1 byte 270 | FixedLengthInt16, // length: 2 bytes 271 | FixedLengthInt32, // length: 4 bytes 272 | FixedLengthInt64, // length: 8 bytes 273 | }; 274 | 275 | EBasicType GetFixedLengthIntType(int32_t InNumBytes); 276 | 277 | class FundamentalTypeDeclaration final : public ITypeDeclaration 278 | { 279 | public: 280 | EBasicType BasicType{EBasicType::Char}; 281 | bool bIsUnsigned{false}; 282 | bool bIsShort{false}; 283 | bool bIsLong{false}; 284 | bool bIsLongLong{false}; 285 | 286 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::FundamentalType; } 287 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 288 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 289 | std::shared_ptr Clone() const override; 290 | size_t GetDeclarationHash() const override; 291 | }; 292 | 293 | class VoidTypeDeclaration final : public ITypeDeclaration 294 | { 295 | public: 296 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::VoidType; } 297 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 298 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 299 | std::shared_ptr Clone() const override; 300 | size_t GetDeclarationHash() const override; 301 | }; 302 | 303 | enum class ECallingConvention : uint8_t 304 | { 305 | CDecl, 306 | StdCall, 307 | FastCall 308 | }; 309 | 310 | class FunctionTypeDeclaration final : public ITypeDeclaration 311 | { 312 | public: 313 | std::shared_ptr ReturnType; 314 | std::shared_ptr OwnerType; 315 | std::vector>> Arguments; 316 | bool bIsFunctionPointer{false}; 317 | bool bIsConstMemberFunction{false}; 318 | bool bIsVariadicArguments{false}; 319 | std::optional CallingConvention; 320 | 321 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::FunctionType; } 322 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 323 | void PrintVariableType(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const std::wstring& VariableName) const override; 324 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 325 | bool Match(const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const override; 326 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 327 | std::shared_ptr Clone() const override; 328 | size_t GetDeclarationHash() const override; 329 | private: 330 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const std::optional& VariableName) const; 331 | }; 332 | 333 | enum class ETemplateArgumentType : uint8_t 334 | { 335 | None, 336 | IntegerConst, 337 | TypeDeclaration, 338 | TypeMemberReference, 339 | IntegerWildcard, 340 | TypeMemberReferenceWildcard, 341 | }; 342 | 343 | struct TypeTemplateArgument 344 | { 345 | ETemplateArgumentType Type{ETemplateArgumentType::None}; 346 | int64_t IntegerConstant{0}; 347 | std::shared_ptr TypeConstant; 348 | TypeMemberReference TypeMemberReference; 349 | int32_t WildcardIndex{0}; 350 | 351 | friend bool operator==(const TypeTemplateArgument& A, const TypeTemplateArgument& B); 352 | bool Match(const TypeTemplateArgument& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const; 353 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const; 354 | TypeTemplateArgument Substitute(const TypeDeclarationMatchContext& MatchContext) const; 355 | TypeTemplateArgument Clone() const; 356 | size_t GetTemplateArgumentHash() const; 357 | 358 | static bool ParseTemplateArguments(const std::wstring& InText, std::vector& OutArguments, bool bAllowWildcardTypes = false); 359 | }; 360 | 361 | struct TypeTemplateArgumentContainer 362 | { 363 | std::vector Arguments; 364 | 365 | friend bool operator==(const TypeTemplateArgumentContainer& A, const TypeTemplateArgumentContainer& B); 366 | bool Match(const TypeTemplateArgumentContainer& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const; 367 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const; 368 | TypeTemplateArgumentContainer Substitute(const TypeDeclarationMatchContext& MatchContext) const; 369 | TypeTemplateArgumentContainer Clone() const; 370 | size_t GetContainerHash() const; 371 | 372 | static bool ParseTemplateArguments( const std::wstring& InText, TypeTemplateArgumentContainer& ArgumentContainer, bool bAllowWildcardTypes = false ); 373 | }; 374 | 375 | // std::hash specialization for template arguments 376 | template<> struct std::hash 377 | { 378 | std::size_t operator()(const TypeTemplateArgumentContainer& Container) const noexcept 379 | { 380 | return Container.GetContainerHash(); 381 | } 382 | }; 383 | 384 | enum class EInternalIdentifier : uint8_t 385 | { 386 | AnonymousTag, 387 | UnnamedTag, 388 | UnnamedType, 389 | UnnamedEnum, 390 | Lambda 391 | }; 392 | 393 | class InternalTypeDeclaration final : public ITypeDeclaration 394 | { 395 | public: 396 | std::shared_ptr OuterType; 397 | EInternalIdentifier Identifier{}; 398 | std::wstring InternalTypeName; 399 | int64_t LambdaIndex{}; 400 | 401 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::InternalType; } 402 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 403 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 404 | std::shared_ptr Clone() const override; 405 | size_t GetDeclarationHash() const override; 406 | }; 407 | 408 | class WildcardTypeDeclaration final : public ITypeDeclaration 409 | { 410 | public: 411 | int32_t WildcardIndex{}; 412 | 413 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::WildcardType; } 414 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 415 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 416 | bool Match(const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const override; 417 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 418 | std::shared_ptr Clone() const override; 419 | size_t GetDeclarationHash() const override; 420 | }; 421 | 422 | class UDTTypeDeclaration final : public ITypeDeclaration 423 | { 424 | public: 425 | // Format is: [OuterType::][OuterScope::] 426 | std::shared_ptr OuterType; 427 | std::wstring OuterScope; 428 | std::wstring ClassName; 429 | TypeTemplateArgumentContainer TemplateArguments; 430 | bool bIsGlobalNamespace{false}; 431 | // Data for pre-declaration 432 | std::optional UDTKind; 433 | 434 | UDTTypeDeclaration() = default; 435 | explicit UDTTypeDeclaration( const std::wstring& InClassName ) : ClassName(InClassName) {} 436 | 437 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::UDT; } 438 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 439 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 440 | bool Match(const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const override; 441 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 442 | std::shared_ptr Clone() const override; 443 | size_t GetDeclarationHash() const override; 444 | }; 445 | 446 | class EnumTypeDeclaration final : public ITypeDeclaration 447 | { 448 | public: 449 | // Format is: [OuterType::][OuterScope::] 450 | std::shared_ptr OuterType; 451 | std::wstring OuterScope; 452 | std::wstring EnumName; 453 | bool bIsGlobalNamespace{false}; 454 | // Data for pre-declaration 455 | std::optional bIsEnumClass; 456 | std::shared_ptr UnderlyingType; 457 | 458 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::Enum; } 459 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 460 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 461 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 462 | std::shared_ptr Clone() const override; 463 | size_t GetDeclarationHash() const override; 464 | }; 465 | 466 | class ArrayTypeDeclaration final : public ITypeDeclaration 467 | { 468 | public: 469 | std::shared_ptr ElementType; 470 | std::optional ArrayDimension; 471 | // Note: arrays cannot be const, only their elements can be 472 | 473 | ETypeDeclarationId GetId() const override { return ETypeDeclarationId::ArrayType; } 474 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const override; 475 | void PrintVariableType(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const std::wstring& VariableName) const override; 476 | bool Identical(const std::shared_ptr& InOtherDeclaration) const override; 477 | bool Match(const std::shared_ptr& InMatchAgainst, TypeDeclarationMatchContext& MatchContext) const override; 478 | std::shared_ptr Substitute(const TypeDeclarationMatchContext& MatchContext) override; 479 | std::shared_ptr Clone() const override; 480 | size_t GetDeclarationHash() const override; 481 | private: 482 | void Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const std::optional& VariableName) const; 483 | }; 484 | 485 | enum class ETypeTextToken : uint8_t 486 | { 487 | EndOfLine, 488 | Identifier, 489 | InternalIdentifier, 490 | Integer, 491 | Float, 492 | ScopeDelimiter, 493 | Pointer, 494 | Reference, 495 | ArrayLBracket, 496 | ArrayRBracket, 497 | LBracket, 498 | RBracket, 499 | UDTKindSpecifier, 500 | EnumSpecifier, 501 | Comma, 502 | TemplateLBracket, 503 | TemplateRBracket, 504 | BaseClassDelimiter, 505 | TypeModifier, 506 | FundamentalType, 507 | Void, 508 | CallingConvention, 509 | VariadicArguments, 510 | // Extension syntax to the normal token set for partial template specialization matching 511 | TypeWildcard, 512 | IntegerWildcard, 513 | TypeMemberReferenceWildcard, 514 | Invalid 515 | }; 516 | 517 | enum class ETypeModifier : uint8_t 518 | { 519 | None, 520 | SignedModifier, 521 | UnsignedModifier, 522 | LongModifier, 523 | ShortModifier, 524 | Const 525 | }; 526 | 527 | struct TypeTextToken 528 | { 529 | ETypeTextToken Type{ETypeTextToken::Invalid}; 530 | std::wstring_view Identifier{}; 531 | int64_t IntegerValue{}; 532 | double FloatValue{}; 533 | CppUDTKind UdtKindValue{}; 534 | ETypeModifier TypeModifier{ETypeModifier::None}; 535 | EBasicType FundamentalType{EBasicType::Char}; 536 | EInternalIdentifier InternalIdentifier{}; 537 | std::wstring_view UnnamedEnumOrTypeVariableName; 538 | std::int64_t LambdaIndex{}; 539 | ECallingConvention CallingConvention{}; 540 | int32_t WildcardIndex{}; 541 | }; 542 | 543 | class FTypeTextParseHelper 544 | { 545 | const std::wstring& RawText; 546 | int32_t Offset{0}; 547 | bool bAllowWildcards{false}; 548 | public: 549 | FTypeTextParseHelper( const std::wstring& InRawText, const bool InAllowWildcards ) : RawText( InRawText ), bAllowWildcards( InAllowWildcards ) {} 550 | 551 | TypeTextToken PeekNextToken() const; 552 | TypeTextToken PeekNextNextToken() const; 553 | TypeTextToken ConsumeNextToken(); 554 | private: 555 | void SkipWhitespaces( int32_t& InOutOffset ) const; 556 | int32_t PeekNextTokenInternal( int32_t CurrentOffset, TypeTextToken& OutToken ) const; 557 | 558 | // Parses partial simple type declaration. Used for parsing nested type declarations when template instantiations are involved 559 | std::shared_ptr ParsePartialSimpleDeclarationPartial( const std::shared_ptr& OuterType, std::vector AppliedTypeModifiers, bool bHasEnumSpecifier, bool bIsEnumClass, const std::optional& CSUSpecifier ); 560 | // This does not consume the first LBracket, you are expected to do it yourself 561 | std::shared_ptr ParseFunctionPointerDeclaration( const std::shared_ptr& ReturnType ); 562 | // Parses type member name. Uses a simplified set of rules that are used for simple type declarations, and does not permit CSU or global namespaces 563 | TypeMemberReference ParseTypeMemberReference(); 564 | public: 565 | // Parses a complete type declaration 566 | std::shared_ptr ParseCompleteTypeDeclaration(); 567 | 568 | // Parses namespace and the type name from the token stream, like [::][ScopeName::][TypeName] 569 | bool ParseScopeAndTypeName( std::wstring& OutScopeName, std::wstring& OutTypeName, bool& OutIsGlobalNamespace ); 570 | 571 | // Parses simple type declaration from the token stream. Simple type declaration is a type declaration consisting of an optional CSU specifier, a UDT, enum or fundamental type name, and optional const modifier 572 | std::shared_ptr ParseSimpleTypeDeclaration(); 573 | 574 | // Parses template argument list until it reaches the closing bracket or end of stream 575 | bool ParseTemplateArgumentsInternal( std::vector& OutTemplateArguments ); 576 | }; 577 | -------------------------------------------------------------------------------- /include/COFFImportLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Utils/MemoryGrowableBuffer.h" 5 | 6 | class COFFImportLibrary 7 | { 8 | std::string LibraryNameWithExtension; 9 | uint64_t TargetMachine{}; 10 | std::vector> Members; 11 | std::string LibraryName; 12 | std::string ImportDescriptorSymbolName; 13 | std::string NullThunkSymbolName; 14 | uint64_t CurrentOrdinalIndex{}; 15 | public: 16 | COFFImportLibrary( const std::string& InLibraryNameWithExtension, uint64_t InTargetMachine ); 17 | 18 | void AddImport( const std::string& InImportName, bool bIsCodeImport ); 19 | bool WriteLibrary( const std::wstring& InFilename ); 20 | private: 21 | void DiscoverSymbols( std::vector>& OutSymbolToMemberIndex ) const; 22 | 23 | std::pair CreateImportDescriptor() const; 24 | std::pair CreateNullImportDescriptor() const; 25 | std::pair CreateNullThunk() const; 26 | }; 27 | -------------------------------------------------------------------------------- /include/CodeGeneration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Utils/DiaUtils.h" 7 | #include "AST/CppTypeDeclaration.h" 8 | 9 | class ITypeResolutionProvider; 10 | enum class CppUDTKind : uint8_t; 11 | enum class CppAccessModifier : uint8_t; 12 | 13 | namespace CodeGeneration 14 | { 15 | std::shared_ptr FormatTypeName( const CComPtr& InTypeSymbol, ITypeResolutionProvider* TypeProvider ); 16 | void FormatFunctionType( const CComPtr& InFunctionSignatureType, ITypeResolutionProvider* TypeProvider, std::shared_ptr& OutReturnType, std::vector>>& OutArgumentNamesAndTypes, 17 | bool& OutIsVariadicArguments, 18 | std::shared_ptr* OutThisPointerType = nullptr, 19 | const CComPtr& InOwnerFunction = nullptr, 20 | bool* OutIsConstMemberFunction = nullptr ); 21 | std::shared_ptr FormatBasicTypeName(DWORD InBasicType, LONGLONG InBasicTypeSizeBytes ); 22 | bool GenerateDefaultValueForSimpleType( const CComPtr& InTypeSymbol, std::wstring& OutDefaultValue ); 23 | 24 | void PatchupInternalTypeReferences( TypeTemplateArgumentContainer& TemplateArguments, const CComPtr& InTemplateTypeContext ); 25 | 26 | CppAccessModifier ConvertAccessModifier( DWORD InAccessModifier ); 27 | CppUDTKind ConvertUDTKind( DWORD InUDTKind ); 28 | bool IsInternalSymbolName( const SymbolNameInfo& SymbolName ); 29 | 30 | std::wstring GenerateConstantValue( const VARIANT& InVariant ); 31 | } 32 | -------------------------------------------------------------------------------- /include/GeneratedHeaderFile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "TypeDependencyCollector.h" 8 | #include "Utils/DiaUtils.h" 9 | 10 | class IUDTDeclarationMember; 11 | struct TypeTemplateArgument; 12 | class GlobalFunctionDeclaration; 13 | class GlobalDataDeclaration; 14 | struct UDTDeclarationData; 15 | class EnumDeclaration; 16 | class EnumerationInfo; 17 | class ITopLevelDeclaration; 18 | class HeaderGenerator; 19 | class UserDefinedTypeInfo; 20 | struct EnumDeclarationData; 21 | class CppFile; 22 | 23 | class GeneratedHeaderFile 24 | { 25 | HeaderGenerator* OwnerGenerator{}; 26 | std::wstring HeaderFileName; 27 | std::unique_ptr GeneratedFile; 28 | 29 | std::vector ContainedTopLevelTypes; 30 | std::vector ContainedTopLevelEnums; 31 | std::vector> ContainedGlobalFunctions; 32 | std::vector> ContainedGlobalVariables; 33 | public: 34 | GeneratedHeaderFile( HeaderGenerator* InOwnerGenerator, const std::wstring& InHeaderFileName ); 35 | 36 | FORCEINLINE HeaderGenerator* GetHeaderGenerator() const { return OwnerGenerator; } 37 | FORCEINLINE const std::wstring& GetHeaderFileName() const { return HeaderFileName; } 38 | 39 | void AddTopLevelType( UserDefinedTypeInfo* InUserDefinedType ); 40 | void AddTopLevelEnumeration( EnumerationInfo* InEnumerationInfo ); 41 | void AddGlobalFunction( const CComPtr& InGlobalFunction ); 42 | void AddGlobalVariable( const CComPtr& InGlobalVariable ); 43 | void GenerateCppFile(); 44 | private: 45 | void PopulateCppFile() const; 46 | public: 47 | static std::shared_ptr MakeTopLevelData( const CComPtr& InDataSymbol, ITypeResolutionProvider* TypeProvider ); 48 | static std::shared_ptr MakeTopLevelFunction( const CComPtr& InFunctionSymbol, ITypeResolutionProvider* TypeProvider ); 49 | static std::shared_ptr MakeEnum( const CComPtr& EnumTypeSymbol, std::wstring* OutEnumNamespace, ITypeResolutionProvider* TypeProvider ); 50 | static std::shared_ptr MakeUDT( const CComPtr& UDTSymbol, const UserDefinedTypeInfo* TypeInfo, std::wstring* OutTypeNamespace, ITypeResolutionProvider* TypeProvider ); 51 | static std::shared_ptr MakePredeclaration( const CComPtr& InEnumSymbol, ITypeResolutionProvider* TypeProvider ); 52 | static std::shared_ptr MakeTemplateDeclaration( const SymbolNameInfo& TemplateInstantiationName, const CComPtr& TemplateInstantiationSymbol, ITypeResolutionProvider* TypeProvider ); 53 | }; 54 | 55 | class HeaderFileReferenceCollector final : TypeDependencyCollectorBase 56 | { 57 | const GeneratedHeaderFile* OwnerHeader{}; 58 | std::unordered_map TopLevelDeclarations; 59 | std::unordered_set LocalHeaderIncludes; 60 | std::unordered_set SystemHeaderIncludes; 61 | ITopLevelDeclaration* CurrentlyCollectingDeclaration{}; 62 | std::unordered_set DependenciesAlreadyHandled; 63 | std::unordered_map TypeToPredeclarationLookup; 64 | std::unordered_map TemplateToPredeclarationLookup; 65 | std::vector> AllPredeclarations; 66 | public: 67 | explicit HeaderFileReferenceCollector( const GeneratedHeaderFile* InOwnerHeader ); 68 | 69 | void PopulateWithTopLevelDefinitions( const std::unordered_map& InRawDefinitionMap ); 70 | void PopulateGeneratedFileWithDependencies( CppFile& CppFile ) const; 71 | void CollectDependenciesForTypeAndNestedTypes( const UserDefinedTypeInfo* InTypeInfo ); 72 | protected: 73 | ITopLevelDeclaration* FindOrCreateTemplateDeclaration( const SymbolNameInfo& TemplateInstantiationName, const CComPtr& TemplateInstantiationSymbol ); 74 | bool HandleTemplateInstantiationDependency(const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments) override; 75 | void HandleTypedefDependency(const std::wstring& TypedefName, const CComPtr& TypedefType) override; 76 | void AddDependencyInternal(const TypeDependency& TypeDependency, bool bIsPredeclaration) override; 77 | }; 78 | 79 | class NestedTypeDependencyCollector final : TypeDependencyCollectorBase 80 | { 81 | std::unordered_map NestedTypeDeclarations; 82 | IUDTDeclarationMember* CurrentlyCollectingDeclaration{}; 83 | std::unordered_set DependenciesAlreadyHandled; 84 | public: 85 | explicit NestedTypeDependencyCollector( ITypeResolutionProvider* InTypeProvider ); 86 | 87 | void PopulateWithNestedDeclarations( const std::unordered_map& InNestedTypeDeclarations ); 88 | void CollectDependenciesForNestedType( const UserDefinedTypeInfo* InTypeInfo ); 89 | private: 90 | void CollectDependenciesForTypeAndNestedTypes( const UserDefinedTypeInfo* InTypeInfo ); 91 | bool HandleTemplateInstantiationDependency(const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments) override; 92 | void AddDependencyInternal(const TypeDependency& TypeDependency, bool bIsPredeclaration) override; 93 | }; 94 | -------------------------------------------------------------------------------- /include/HeaderGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Utils/DiaUtils.h" 11 | #include "TypeDependencyCollector.h" 12 | #include "Utils/StringUtils.h" 13 | 14 | struct HeaderGeneratorConfig; 15 | struct TypeTemplateArgument; 16 | enum class CppUDTKind : uint8_t; 17 | struct UDTDeclarationData; 18 | enum class CppAccessModifier : uint8_t; 19 | class UDTDeclaration; 20 | class EnumDeclaration; 21 | class ITopLevelDeclaration; 22 | class CppFile; 23 | class HeaderGenerator; 24 | class EnumerationInfo; 25 | 26 | class CompilationUnit 27 | { 28 | HeaderGenerator* OwnerGenerator; 29 | CComPtr CompilationUnitSymbol; 30 | std::wstring CompilationUnitName; 31 | // environment in which the compiland was compiled. Useful for checking potential used includes, defines and source filename 32 | std::unordered_map CompilandEnvironment; 33 | DWORD CompilandPlatform{}; 34 | DWORD CompilandSourceLanguage{}; 35 | 36 | // True if this object file comes from another statically of dynamically linked library 37 | bool bIsExternalCompilationUnit{false}; 38 | public: 39 | // Global functions defined in this object file 40 | std::vector> GlobalFunctions; 41 | std::vector> GlobalVariables; 42 | 43 | std::vector> AllFunctionImplementations; 44 | std::vector> AllDataMemberDefinitions; 45 | bool bIsGlobalScopeUnit{false}; 46 | public: 47 | CompilationUnit( HeaderGenerator* InOwnerGenerator, const CComPtr& InCompilationUnitSymbol, const std::wstring& InCompilationUnitName, bool InIsExternalCompilationUnit ); 48 | 49 | FORCEINLINE const std::wstring& GetCompilationUnitName() const { return CompilationUnitName; } 50 | FORCEINLINE bool IsExternalCompilationUnit() const { return bIsExternalCompilationUnit; } 51 | FORCEINLINE HeaderGenerator* GetHeaderGenerator() const { return OwnerGenerator; } 52 | 53 | int32_t EstimateActionCount() const; 54 | void ProcessActions_Pass1( int32_t& CurrentActionNumber ); 55 | void ProcessActions_Pass2( int32_t& CurrentActionNumber ); 56 | std::wstring GetHeaderFilename() const; 57 | private: 58 | void ProcessCompilandEnvironment(); 59 | void ProcessCompilandDetails(); 60 | void ProcessCompilandFunctionsAndData( int32_t& CurrentActionNumber ); 61 | 62 | bool NeedsHeaderForCompilationUnit() const; 63 | void PushGlobalMembersToHeaderFile( class GeneratedHeaderFile* HeaderFile ) const; 64 | void CollectAndRegisterDependencies( int32_t& CurrentActionNumber ); 65 | static void SanitizeGlobalMemberNameForHeaderFilename( std::wstring& SymbolName ); 66 | }; 67 | 68 | class CompilationUnitReferenceCollector final : public TypeDependencyCollectorBase 69 | { 70 | CompilationUnit* mCompilationUnit{nullptr}; 71 | std::unordered_set VisitedTypes; 72 | public: 73 | explicit CompilationUnitReferenceCollector( CompilationUnit* InCompilationUnit ); 74 | private: 75 | void AddDependencyInternal(const TypeDependency& TypeDependency, bool bIsPredeclaration) override; 76 | bool HandleTemplateInstantiationDependency(const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments) override; 77 | }; 78 | 79 | class UserDefinedTypeInfo 80 | { 81 | HeaderGenerator* OwnerGenerator{}; 82 | CComPtr UserDefinedTypeSymbol; 83 | std::vector NestedTypes; 84 | std::vector NestedEnums; 85 | std::unordered_map>> CompilationUnitDefinitions; 86 | std::unordered_set CompilationUnitReferences; 87 | SymbolNameInfo SymbolName; 88 | public: 89 | UserDefinedTypeInfo( HeaderGenerator* InGenerator, const CComPtr& InUDTSymbol ); 90 | 91 | FORCEINLINE const SymbolNameInfo& GetSymbolName() const { return SymbolName; } 92 | FORCEINLINE CComPtr GetUDTSymbol() const { return UserDefinedTypeSymbol; } 93 | FORCEINLINE const std::vector& GetNestedTypes() const { return NestedTypes; } 94 | FORCEINLINE const std::vector& GetNestedEnums() const { return NestedEnums; } 95 | 96 | void ProcessActions(); 97 | void AddDefinedFunction( const CComPtr& FunctionSymbol, const CompilationUnit* CompilationUnit ); 98 | void AddDefinedVariable( const CComPtr& FieldSymbol, const CompilationUnit* CompilationUnit ); 99 | void AddNestedUserDefinedType( UserDefinedTypeInfo* InNestedUDT ); 100 | void AddNestedEnumeration( EnumerationInfo* InNestedEnumeration ); 101 | // Returns true if the given compilation unit should recursively scan dependencies from this type. That would be the case if this unit is the unit that implements this type, or the first unit to reference the type. 102 | bool AddCompilationUnitReference( const CompilationUnit* FromCompilationUnit ); 103 | private: 104 | std::wstring GetHeaderNameForType() const; 105 | }; 106 | 107 | class EnumerationInfo 108 | { 109 | HeaderGenerator* OwnerGenerator{}; 110 | CComPtr EnumerationSymbol; 111 | std::unordered_set CompilationUnitReferences; 112 | public: 113 | EnumerationInfo( HeaderGenerator* InGenerator, const CComPtr& InEnumSymbol ); 114 | 115 | FORCEINLINE CComPtr GetEnumSymbol() const { return EnumerationSymbol; } 116 | 117 | void ProcessActions(); 118 | void AddCompilationUnitReference( const CompilationUnit* FromCompilationUnit ); 119 | private: 120 | std::wstring DetermineHeaderFilename() const; 121 | }; 122 | 123 | using TemplateInstantiationMap = std::unordered_map>; 124 | 125 | enum class EExternalSymbolType : uint8_t 126 | { 127 | None = 0x00, 128 | Type = 0x01, 129 | Function = 0x02, 130 | Data = 0x04 131 | }; 132 | inline EExternalSymbolType operator|(EExternalSymbolType A, EExternalSymbolType B) 133 | { 134 | return static_cast(static_cast(A) | static_cast(B)); 135 | } 136 | inline EExternalSymbolType operator&(EExternalSymbolType A, EExternalSymbolType B) 137 | { 138 | return static_cast(static_cast(A) & static_cast(B)); 139 | } 140 | 141 | struct TypeSubstitutionCandidate 142 | { 143 | TypeTemplateArgumentContainer FilterTemplateArguments; 144 | std::wstring SubtitutedNamespace; 145 | std::wstring SubtitutedClassName; 146 | TypeTemplateArgumentContainer SubstitutedArguments; 147 | }; 148 | 149 | class HeaderGenerator final : public ITypeResolutionProvider 150 | { 151 | std::filesystem::path OutputDirectoryPath; 152 | std::unordered_map UserDefinedTypesLookup; 153 | std::vector> AllUserDefinedTypes; 154 | std::unordered_map EnumLookup; 155 | std::vector> AllEnums; 156 | std::unordered_map SymbolHeaderFiles; 157 | std::unordered_map GeneratedHeaderFilesLookup; 158 | std::vector> AllGeneratedHeaderFiles; 159 | std::unordered_set ExternalUserDefinedTypes; 160 | std::unordered_set ExternalTemplates; 161 | int32_t TotalActionCount{0}; 162 | CComPtr ExecutableSymbol; 163 | // all symbols defined across all compilation units by their hash. used to detect duplicate symbol definitions and handle them accordingly. 164 | std::unordered_set AllDefinedSymbolNameHashes; 165 | // symbols that have been defined in multiple translation units with the same name. Usually that means they were marked as "static" or "inline", or are template instantiations 166 | std::unordered_set DuplicateSymbolDefinitions; 167 | // Map of external types to the names of the headers where they can be found 168 | std::unordered_map ExternalTypeToHeaderLookup; 169 | std::unordered_set ExternalFunctions; 170 | std::unordered_set ExternalData; 171 | std::unordered_set ExternalNamespaces; 172 | std::unordered_set ExternalTemplatePredeclarationWhitelist; 173 | std::unordered_map, std::vector> TypeSubstitutions; 174 | std::unordered_map HeaderOverrideNameToFilePath; 175 | std::unordered_map HeaderOverridenTypeNameToHeaderName; 176 | // Fallback lookup of external namespace (starting with) to the header. Used to handle libraries with many, many different types where having different types is difficult, such as highly-templated libraries. 177 | std::vector> ExternalNamespaceToHeaderFallback; 178 | std::unordered_map> EnumerationByNameCache; 179 | std::unordered_map> UDTsByNameCache; 180 | std::unordered_map> TemplatesByNameCache; 181 | std::unordered_map PublicSymbolsByRVA; 182 | std::unordered_set ExternalFunctionsAndDataRVAs; 183 | // Cache of nested type ID to the parent type symbol 184 | std::unordered_map> NestedTypeParentCache; 185 | // Cache of lexical parent to it's owner compilation unit 186 | std::vector> AllCompilationUnits; 187 | std::unordered_set LibrariesConsideredInternal; 188 | std::unordered_set AlreadyPrintedLibraryNames; 189 | std::wstring DllName; 190 | public: 191 | explicit HeaderGenerator( const std::wstring& InDllName, const std::filesystem::path& InOutputDirectory ); 192 | void LoadDataFromConfig( const HeaderGeneratorConfig& Config ); 193 | 194 | void Generate( const CComPtr& InGlobalSymbol ); 195 | 196 | FORCEINLINE std::filesystem::path GetOutputDirectory() const { return OutputDirectoryPath; } 197 | FORCEINLINE CComPtr GetExecutableSymbol() const { return ExecutableSymbol; } 198 | FORCEINLINE int32_t GetTotalActionCount() const { return TotalActionCount; } 199 | UserDefinedTypeInfo* FindOrAddUserDefinedType( const CComPtr& ClassSymbol ); 200 | EnumerationInfo* FindOrAddEnum( const CComPtr& EnumSymbol ); 201 | GeneratedHeaderFile* FindOrCreateHeaderFile( const std::wstring& HeaderFilename ); 202 | UserDefinedTypeInfo* FindUserDefinedType( const CComPtr& ClassSymbol ) const; 203 | 204 | // Attempts to resolve the type by full name, which might contain a namespace or another type as a scope. Does not handle enumerations nested into templated types!!!! 205 | CComPtr ResolveEnumByFullName( const std::wstring& InFullEnumName ) override; 206 | CComPtr ResolveUDTByFullName( const std::wstring& InFullClassName ) override; 207 | // Attempts to resolve the type for a specific template instantiation given the template full name and instantiation arguments. This is slow. 208 | CComPtr ResolveTemplateInstantiation( const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& ArgumentContainer ) override; 209 | 210 | bool IsExternalUserDefinedType( const CComPtr& InTypeSymbol ) const; 211 | // Returns true if this type has not been registered as external before 212 | bool RegisterExternalUserDefinedType( const CComPtr& InExternalSymbol ); 213 | // Registers template as external 214 | void RegisterExternalTemplate( const SymbolNameInfo& TemplateName ); 215 | // Registers external symbol by it's RVA. Should only be used on functions and data. Will do nothing if symbol location is not LocIsStatic 216 | void RegisterExternalSymbolByRVA( const CComPtr& InSymbol ); 217 | 218 | // Returns public symbol information for symbol at given relative virtual address, or null if there is no public symbol there 219 | const PublicSymbolInfo* FindPublicSymbolByRVA( DWORD RelativeVirtualAddress ) const override; 220 | CComPtr FindNestedTypeParent(const CComPtr& InNestedType) override; 221 | bool RemapTypeReference(const std::wstring& ClassNamespace, const std::wstring& ClassName, const TypeTemplateArgumentContainer& TypeArguments, std::wstring& OutClassReplacementNamespace, std::wstring& OutReplacementClassName, TypeTemplateArgumentContainer& OutReplacementTypeArguments) const override; 222 | bool DoTemplateArgumentsNeedFullDefinition(const SymbolNameInfo& TemplateName) const override; 223 | // Returns true if symbol at the given RVA (function or data) is marked as external, e.g. has it's definition in one of the external object files (part of either statically or dynamically linked library) 224 | bool IsExternalSymbolByRVA( DWORD RelativeVirtualAddress ) const; 225 | 226 | void PushDefinedSymbol( const std::wstring& InFullSymbolName ); 227 | bool IsDuplicateSymbolDefinition( const std::wstring& InFullSymbolName ) const; 228 | 229 | // Returns true if the given symbol should be marked as external as defined by the user 230 | bool ShouldMarkSymbolAsExternal( const SymbolNameInfo& SymbolName, EExternalSymbolType SymbolType ) const; 231 | const std::wstring* FindExternalHeaderForType( const SymbolNameInfo& SymbolName ) const; 232 | 233 | bool IsHeaderFilenameOccupiedByManualHeader( const std::wstring& InHeaderFilename ) const; 234 | const std::wstring* FindOverridenManualHeaderForType( const SymbolNameInfo& ClassName ) const; 235 | 236 | // Finds the header file that has been associated with the given symbol (either UDT or an enum). Also works for global functions and data 237 | GeneratedHeaderFile* FindHeaderFileForSymbol( const CComPtr& InSymbol ); 238 | void AssociateSymbolWithHeaderFile( const CComPtr& InSymbol, GeneratedHeaderFile* InHeaderFile ); 239 | 240 | static bool IsFunctionGeneratedThunk( const CComPtr& FunctionSymbol ); 241 | private: 242 | std::shared_ptr FindAllTemplateInstantiationSymbols( const SymbolNameInfo& TemplateName ); 243 | CComPtr ResolveSymbolByFullNameNoCache( const std::wstring& InFullSymbolName, enum SymTagEnum InSymbolTag ) const; 244 | bool ResolveScopeForFullName( const std::wstring& InFullName, CComPtr& OutScopeSymbol, std::wstring& OutNameInsideScope ) const; 245 | std::shared_ptr CreateCompilationUnitForCompiland( const CComPtr& CompilandSymbol ); 246 | void CreateShortImportLibrary(); 247 | void DiscoverAllSymbols( int32_t& CurrentActionNumber ); 248 | void DiscoverAllCompilands(); 249 | }; 250 | -------------------------------------------------------------------------------- /include/HeaderGeneratorConfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct ExternalHeaderDefinition 9 | { 10 | std::string IncludeName; 11 | std::vector ContainedTypes; 12 | std::vector ContainedNamespaces; 13 | 14 | friend void to_json(nlohmann::json& Json, const ExternalHeaderDefinition& HeaderDefinition) 15 | { 16 | Json["IncludeName"] = HeaderDefinition.IncludeName; 17 | Json["ContainedTypes"] = HeaderDefinition.ContainedTypes; 18 | Json["ContainedNamespaces"] = HeaderDefinition.ContainedNamespaces; 19 | } 20 | friend void from_json(const nlohmann::json& Json, ExternalHeaderDefinition& HeaderDefinition) 21 | { 22 | Json.at("IncludeName").get_to(HeaderDefinition.IncludeName); 23 | if (Json.contains("ContainedTypes")) 24 | { 25 | Json.at("ContainedTypes").get_to(HeaderDefinition.ContainedTypes); 26 | } 27 | if (Json.contains("ContainedNamespaces")) 28 | { 29 | Json.at("ContainedNamespaces").get_to(HeaderDefinition.ContainedNamespaces); 30 | } 31 | } 32 | }; 33 | 34 | struct TypeRemapDefinition 35 | { 36 | std::string OriginalTypeName; 37 | std::string ReplacementTypeName; 38 | 39 | NLOHMANN_DEFINE_TYPE_INTRUSIVE( TypeRemapDefinition, OriginalTypeName, ReplacementTypeName ); 40 | }; 41 | 42 | struct ManualHeaderDefinition 43 | { 44 | std::string HeaderPath; 45 | std::string HeaderName; 46 | std::vector ContainedTypes; 47 | // Transient, populated on config load 48 | std::filesystem::path HeaderFilesystemPath; 49 | 50 | NLOHMANN_DEFINE_TYPE_INTRUSIVE( ManualHeaderDefinition, HeaderPath, HeaderName, ContainedTypes ); 51 | }; 52 | 53 | struct HeaderGeneratorConfig 54 | { 55 | std::vector DependencyConfigs; 56 | std::vector ExternalHeaders; 57 | std::vector ExternalGlobalData; 58 | std::vector ExternalGlobalFunctions; 59 | std::unordered_map GlobalDataNameRemap; 60 | std::vector ExternalNamespaces; 61 | std::vector ExternalTemplatePredeclarationWhitelist; 62 | std::vector TypeRemap; 63 | std::vector HeaderOverrides; 64 | std::vector LibrariesConsideredInternal; 65 | 66 | void Merge(const HeaderGeneratorConfig& OtherConfig) 67 | { 68 | ExternalGlobalData.insert(ExternalGlobalData.end(), OtherConfig.ExternalGlobalData.begin(), OtherConfig.ExternalGlobalData.end()); 69 | ExternalGlobalFunctions.insert(ExternalGlobalFunctions.end(), OtherConfig.ExternalGlobalFunctions.begin(), OtherConfig.ExternalGlobalFunctions.end()); 70 | ExternalNamespaces.insert(ExternalNamespaces.end(), OtherConfig.ExternalNamespaces.begin(), OtherConfig.ExternalNamespaces.end()); 71 | TypeRemap.insert(TypeRemap.end(), OtherConfig.TypeRemap.begin(), OtherConfig.TypeRemap.end()); 72 | ExternalTemplatePredeclarationWhitelist.insert(ExternalTemplatePredeclarationWhitelist.end(), OtherConfig.ExternalTemplatePredeclarationWhitelist.begin(), OtherConfig.ExternalTemplatePredeclarationWhitelist.end()); 73 | HeaderOverrides.insert(HeaderOverrides.end(), OtherConfig.HeaderOverrides.begin(), OtherConfig.HeaderOverrides.end()); 74 | LibrariesConsideredInternal.insert(LibrariesConsideredInternal.end(), OtherConfig.LibrariesConsideredInternal.begin(), OtherConfig.LibrariesConsideredInternal.end()); 75 | 76 | for ( const auto& [OriginalName, RemappedName] : OtherConfig.GlobalDataNameRemap ) 77 | { 78 | GlobalDataNameRemap.insert_or_assign( OriginalName, RemappedName ); 79 | } 80 | 81 | std::unordered_map ExistingHeaderIndices; 82 | for ( int32_t i = 0; i < ExternalHeaders.size(); i++ ) 83 | { 84 | ExistingHeaderIndices.insert({ ExternalHeaders[i].IncludeName, i }); 85 | } 86 | 87 | for ( const ExternalHeaderDefinition& HeaderDefinition : OtherConfig.ExternalHeaders ) 88 | { 89 | if ( const auto Iterator = ExistingHeaderIndices.find( HeaderDefinition.IncludeName ); Iterator != ExistingHeaderIndices.end() ) 90 | { 91 | ExternalHeaderDefinition& ExistingDefinition = ExternalHeaders[ Iterator->second ]; 92 | ExistingDefinition.ContainedNamespaces.insert( ExistingDefinition.ContainedNamespaces.end(), HeaderDefinition.ContainedNamespaces.begin(), HeaderDefinition.ContainedNamespaces.end() ); 93 | ExistingDefinition.ContainedTypes.insert( ExistingDefinition.ContainedTypes.end(), HeaderDefinition.ContainedTypes.begin(), HeaderDefinition.ContainedTypes.end() ); 94 | } 95 | else 96 | { 97 | ExternalHeaders.push_back( HeaderDefinition ); 98 | } 99 | } 100 | } 101 | 102 | friend void to_json(nlohmann::json& Json, const HeaderGeneratorConfig& Config) 103 | { 104 | Json["DependencyConfigs"] = Config.DependencyConfigs; 105 | Json["ExternalHeaders"] = Config.ExternalHeaders; 106 | Json["ExternalGlobalData"] = Config.ExternalGlobalData; 107 | Json["ExternalGlobalFunctions"] = Config.ExternalGlobalFunctions; 108 | Json["GlobalDataNameRemap"] = Config.GlobalDataNameRemap; 109 | Json["ExternalNamespaces"] = Config.ExternalNamespaces; 110 | Json["ExternalTemplatePredeclarationWhitelist"] = Config.ExternalTemplatePredeclarationWhitelist; 111 | Json["HeaderOverrides"] = Config.HeaderOverrides; 112 | Json["TypeRemap"] = Config.TypeRemap; 113 | Json["LibrariesConsideredInternal"] = Config.LibrariesConsideredInternal; 114 | } 115 | friend void from_json(const nlohmann::json& Json, HeaderGeneratorConfig& Config) 116 | { 117 | if (Json.contains("DependencyConfigs")) 118 | { 119 | Json.at("DependencyConfigs").get_to(Config.DependencyConfigs); 120 | } 121 | if (Json.contains("ExternalHeaders")) 122 | { 123 | Json.at("ExternalHeaders").get_to(Config.ExternalHeaders); 124 | } 125 | if (Json.contains("ExternalGlobalData")) 126 | { 127 | Json.at("ExternalGlobalData").get_to(Config.ExternalGlobalData); 128 | } 129 | if (Json.contains("ExternalGlobalFunctions")) 130 | { 131 | Json.at("ExternalGlobalFunctions").get_to(Config.ExternalGlobalFunctions); 132 | } 133 | if (Json.contains("GlobalDataNameRemap")) 134 | { 135 | Json.at("GlobalDataNameRemap").get_to(Config.GlobalDataNameRemap); 136 | } 137 | if (Json.contains("ExternalNamespaces")) 138 | { 139 | Json.at("ExternalNamespaces").get_to(Config.ExternalNamespaces); 140 | } 141 | if (Json.contains("ExternalTemplatePredeclarationWhitelist")) 142 | { 143 | Json.at("ExternalTemplatePredeclarationWhitelist").get_to(Config.ExternalTemplatePredeclarationWhitelist); 144 | } 145 | if (Json.contains("HeaderOverrides")) 146 | { 147 | Json.at("HeaderOverrides").get_to(Config.HeaderOverrides); 148 | } 149 | if (Json.contains("TypeRemap")) 150 | { 151 | Json.at("TypeRemap").get_to(Config.TypeRemap); 152 | } 153 | if (Json.contains("LibrariesConsideredInternal")) 154 | { 155 | Json.at("LibrariesConsideredInternal").get_to(Config.LibrariesConsideredInternal); 156 | } 157 | } 158 | }; 159 | 160 | class HeaderGeneratorConfigManager 161 | { 162 | std::unordered_set LoadedConfigs; 163 | HeaderGeneratorConfig MergedConfig; 164 | public: 165 | bool LoadConfigFrom( const std::filesystem::path& InConfigFilePath ); 166 | const HeaderGeneratorConfig& GetMergedConfig() const { return MergedConfig; } 167 | }; -------------------------------------------------------------------------------- /include/TypeDependencyCollector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Utils/DiaUtils.h" 10 | 11 | class TypeDependency 12 | { 13 | CComPtr DependencySymbol; 14 | DWORD SymbolId{}; 15 | DWORD SymbolTag{}; 16 | explicit TypeDependency( const CComPtr& InDependencySymbol ); 17 | public: 18 | FORCEINLINE bool IsEnum() const { return SymbolTag == SymTagEnum; } 19 | FORCEINLINE bool IsUserDefinedType() const { return SymbolTag == SymTagUDT; } 20 | FORCEINLINE DWORD GetSymbolUniqueId() const { return SymbolId; } 21 | // Retrieves the underlying symbol. Do not use it for comparison, DIA SDK instantiates symbols on demand, the same symbol ID can have multiple IDiaSymbol objects alive! 22 | FORCEINLINE CComPtr GetSymbol() const { return DependencySymbol; } 23 | 24 | static TypeDependency UserDefinedType( const CComPtr& UserDefinedType ); 25 | static TypeDependency Enum( const CComPtr& Enum ); 26 | 27 | friend bool operator==(const TypeDependency& A, const TypeDependency& B) 28 | { 29 | return A.GetSymbolUniqueId() == B.GetSymbolUniqueId(); 30 | } 31 | }; 32 | 33 | template<> 34 | struct std::hash 35 | { 36 | std::size_t operator()(const TypeDependency& TypeDependency) const noexcept 37 | { 38 | return std::hash{}(TypeDependency.GetSymbolUniqueId()); 39 | } 40 | }; 41 | 42 | struct PublicSymbolInfo 43 | { 44 | bool bIsCLinkage{false}; 45 | }; 46 | 47 | class ITypeResolutionProvider 48 | { 49 | public: 50 | virtual ~ITypeResolutionProvider() = default; 51 | 52 | // Attempts to resolve the type by full name, which might contain a namespace or another type as a scope. Does not handle enumerations nested into templated types!!!! 53 | virtual CComPtr ResolveEnumByFullName( const std::wstring& InFullEnumName ) = 0; 54 | virtual CComPtr ResolveUDTByFullName( const std::wstring& InFullClassName ) = 0; 55 | // Attempts to resolve the type for a specific template instantiation given the template full name and instantiation arguments. This is slow. 56 | virtual CComPtr ResolveTemplateInstantiation( const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& ArgumentContainer ) = 0; 57 | 58 | // Returns public symbol information for symbol at given relative virtual address, or null if there is no public symbol there 59 | virtual const PublicSymbolInfo* FindPublicSymbolByRVA( DWORD RelativeVirtualAddress ) const = 0; 60 | // Attempts to find the parent type for this nested symbol. Returns nullptr if this is a top level symbol 61 | virtual CComPtr FindNestedTypeParent( const CComPtr& InNestedType ) = 0; 62 | 63 | // Returns the top level type for this symbol, or the symbol itself in case it is top level 64 | CComPtr FindTopLevelType( const CComPtr& InNestedType ); 65 | // Attempts to remap type name 66 | virtual bool RemapTypeReference( const std::wstring& ClassNamespace, const std::wstring& ClassName, const TypeTemplateArgumentContainer& TypeArguments, 67 | std::wstring& OutClassReplacementNamespace, std::wstring& OutReplacementClassName, TypeTemplateArgumentContainer& OutReplacementTypeArguments ) const = 0; 68 | // Returns true if this template needs full definition of it's arguments, or just pre-declarations 69 | virtual bool DoTemplateArgumentsNeedFullDefinition( const SymbolNameInfo& TemplateName ) const = 0; 70 | }; 71 | 72 | class TypeDependencyCollectorBase 73 | { 74 | protected: 75 | ITypeResolutionProvider* TypeResolver{}; 76 | bool bNeedsCStdint{false}; 77 | public: 78 | explicit TypeDependencyCollectorBase( ITypeResolutionProvider* InTypeResolver ); 79 | virtual ~TypeDependencyCollectorBase() = default; 80 | 81 | void CollectDependenciesForType( const CComPtr& InTypeSymbol, bool bOnlyWantsPredeclaration = false ); 82 | void CollectDependenciesForUDTDefinition( const CComPtr& InUserDefinedType, bool bInternalMarkAsPredeclaration = false ); 83 | void CollectDependenciesForTemplateInstantiation( const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments ); 84 | FORCEINLINE bool NeedsCStdint() const { return bNeedsCStdint; } 85 | void AddDependency( const TypeDependency& TypeDependency, bool bIsPredeclaration ); 86 | protected: 87 | virtual void HandleTypedefDependency( const std::wstring& TypedefName, const CComPtr& TypedefType ) {} 88 | virtual void AddDependencyInternal( const TypeDependency& TypeDependency, bool bIsPredeclaration ) = 0; 89 | // true if template instnatiation was handled, false if slow template instantiation lookup through type resolution provider should take place 90 | virtual bool HandleTemplateInstantiationDependency( const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments ) { return false; } 91 | }; 92 | 93 | class TypeDependencyCollector final : public TypeDependencyCollectorBase 94 | { 95 | std::unordered_set DeclarationDependencies; 96 | std::unordered_set DefinitionDependencies; 97 | std::unordered_map> TypedefDependencies; 98 | std::unordered_set UserDefinedTypesHandled; 99 | bool bNeedsCStdint{false}; 100 | bool bRecurseIntoUserDefinedTypes{false}; 101 | public: 102 | TypeDependencyCollector( ITypeResolutionProvider* InTypeResolver, bool InRecurseIntoUserDefinedTypes = false ); 103 | 104 | FORCEINLINE const std::unordered_set& GetDeclarationDependencies() const { return DeclarationDependencies; } 105 | FORCEINLINE const std::unordered_set& GetDefinitionDependencies() const { return DefinitionDependencies; } 106 | 107 | std::unordered_set GetAllDependencies() const; 108 | private: 109 | void HandleTypedefDependency(const std::wstring& TypedefName, const CComPtr& TypedefType) override; 110 | void AddDependencyInternal( const TypeDependency& TypeDependency, bool bIsPredeclaration ) override; 111 | }; 112 | -------------------------------------------------------------------------------- /include/Utils/DiaUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define UNDNAME_NAME_ONLY 0x1000 12 | 13 | namespace SymbolAttributeHelpers 14 | { 15 | template 16 | struct DiaSymbolAttributeTypeRetriever; 17 | 18 | template 19 | struct DiaSymbolAttributeTypeRetriever 20 | { 21 | using AttributeType = InAttributeType; 22 | }; 23 | 24 | template 25 | struct DiaSymbolAttributeRetriever; 26 | 27 | // Implementation for non-pointers, does not do any kind of additional freeing or reference counting 28 | template 29 | requires(!std::is_pointer_v) 30 | struct DiaSymbolAttributeRetriever 31 | { 32 | InAttributeType AttributeData{}; 33 | 34 | InAttributeType* GetAttributePtrForWrite() 35 | { 36 | return &AttributeData; 37 | } 38 | InAttributeType CopyAttributeForRead() const 39 | { 40 | return AttributeData; 41 | } 42 | }; 43 | 44 | // Implementation for BSTR 45 | template<> 46 | struct DiaSymbolAttributeRetriever 47 | { 48 | BSTR AttributeData{}; 49 | 50 | ~DiaSymbolAttributeRetriever() 51 | { 52 | SysFreeString(AttributeData); 53 | } 54 | 55 | BSTR* GetAttributePtrForWrite() 56 | { 57 | return &AttributeData; 58 | } 59 | std::wstring CopyAttributeForRead() const 60 | { 61 | if ( AttributeData == nullptr ) 62 | { 63 | return std::wstring(); 64 | } 65 | return std::wstring( AttributeData ); 66 | } 67 | }; 68 | 69 | // Implementation for COM types (child of IUnknown) 70 | template 71 | requires(std::is_base_of_v) 72 | struct DiaSymbolAttributeRetriever 73 | { 74 | CComPtr AttributeData; 75 | 76 | ComType** GetAttributePtrForWrite() 77 | { 78 | return &AttributeData; 79 | } 80 | CComPtr CopyAttributeForRead() const 81 | { 82 | return AttributeData; 83 | } 84 | }; 85 | 86 | // Implementation for VARIANT 87 | template<> 88 | struct DiaSymbolAttributeRetriever 89 | { 90 | CComVariant AttributeData; 91 | 92 | VARIANT* GetAttributePtrForWrite() 93 | { 94 | return &AttributeData; 95 | } 96 | CComVariant CopyAttributeForRead() const 97 | { 98 | return AttributeData; 99 | } 100 | }; 101 | } 102 | 103 | #define GET_SYMBOL_ATTRIBUTE_INTERNAL( Symbol, AttributeName, bChecked ) \ 104 | ([&](){ \ 105 | using AttributeType = SymbolAttributeHelpers::DiaSymbolAttributeTypeRetriever::AttributeType; \ 106 | SymbolAttributeHelpers::DiaSymbolAttributeRetriever AttributeRetriever; \ 107 | const HRESULT RetrievalResult = Symbol ? Symbol->get_##AttributeName( AttributeRetriever.GetAttributePtrForWrite() ) : S_FALSE; \ 108 | assert( SUCCEEDED( RetrievalResult ) ); \ 109 | assert( !bChecked || RetrievalResult == S_OK ); \ 110 | return AttributeRetriever.CopyAttributeForRead(); \ 111 | })() 112 | 113 | #define GET_SYMBOL_ATTRIBUTE_CHECKED( Symbol, AttributeName ) GET_SYMBOL_ATTRIBUTE_INTERNAL( Symbol, AttributeName, true ) 114 | #define GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( Symbol, AttributeName ) GET_SYMBOL_ATTRIBUTE_INTERNAL( Symbol, AttributeName, false ) 115 | 116 | /** Creates DIA data source from the DLL module handle */ 117 | HRESULT CoCreateDiaDataSource(HMODULE DiaDllHandle, CComPtr& OutDataDataSource); 118 | 119 | /** Simple iterator to iterate on DIA symbols */ 120 | class DiaChildSymbolIterator 121 | { 122 | CComPtr UnderlyingEnumerator; 123 | LONG ItemCount{0}; 124 | LONG CurrentIndex{0}; 125 | public: 126 | DiaChildSymbolIterator( const CComPtr& InParentSymbol, enum SymTagEnum InSymbolType = SymTagNull, const WCHAR* InName = nullptr, DWORD CompareFlags = nsNone ); 127 | DiaChildSymbolIterator( const CComPtr& InParentSymbol, enum SymTagEnum InSymbolType, const WCHAR* InName, DWORD CompareFlags, DWORD SymbolRVA ); 128 | 129 | FORCEINLINE LONG GetCurrentIndex() const { return CurrentIndex; } 130 | // ReSharper disable once CppNonExplicitConversionOperator 131 | operator bool() const; 132 | CComPtr operator*() const; 133 | void operator++(); 134 | }; 135 | 136 | struct SymbolNameInfo 137 | { 138 | // Scope of the symbol. That would be the namespace, or class name (including namespace), depending on symbol place in the hierarchy 139 | std::wstring SymbolScope; 140 | // Local name of the symbol inside of it's scope. For example, that would be the raw name of the function inside of the class, or raw name of the class without the namespace information 141 | std::wstring LocalName; 142 | // If this is a template, this is the arguments that were passed to the template instantiation 143 | std::wstring TemplateArguments; 144 | // True if this symbol is a template instantiation. Needs to be separate from TemplateArguments because templates can have no arguments 145 | bool bIsTemplateInstantiation{false}; 146 | // True if this symbol is anonymous, such as an anonymous namespace or union. LocalName will be an internal name of the anonymous symbol. bIsUnnamedType or bIsUnnamedEnum can be additionally set 147 | bool bIsAnonymousSymbol{false}; 148 | // True if this symbol is an unnamed type 149 | bool bIsUnnamedType{false}; 150 | // True if this symbol is an unnamed enum 151 | bool bIsUnnamedEnum{false}; 152 | // True if this symbol is a lambda 153 | bool bIsLambdaSymbol{false}; 154 | // Original name of the symbol, as it was retrieved from the symbol data 155 | std::wstring OriginalFullName; 156 | // The variable name that caused the generation of this anonymous symbol. 157 | std::wstring AnonymousSymbolOriginVariableName; 158 | 159 | enum SymbolNameInfoToStringOptions : uint32_t 160 | { 161 | IncludeLocalNameOnly = 0, 162 | IncludeNamespace = 0x1, 163 | IncludeTemplateArguments = 0x2, 164 | IncludeAll = IncludeNamespace | IncludeTemplateArguments 165 | }; 166 | 167 | // Converts the symbol name struct back into the original name 168 | std::wstring ToString( int32_t InOptions = IncludeAll ) const; 169 | 170 | // Constructs the symbol name info for the symbol 171 | static SymbolNameInfo FromSymbol( const CComPtr& InSymbol ); 172 | static SymbolNameInfo FromSymbolName( std::wstring SymbolName ); 173 | }; 174 | 175 | namespace DiaUtils 176 | { 177 | CComPtr RemoveCVModifiersFromType( const CComPtr& InTypeSymbol ); 178 | 179 | bool AreFunctionTypesEquivalent( const CComPtr& InFunctionTypeA, const CComPtr& InFunctionTypeB, bool bCheckObjectPointer ); 180 | CComPtr FindParentVirtualFunction( const CComPtr& InTypeSymbol, const std::wstring& FunctionName, const CComPtr& InFunctionSignature ); 181 | 182 | bool IsFunctionImplementationTheSame( const CComPtr& FunctionA, const CComPtr& FunctionB ); 183 | std::wstring GetSymbolUndecoratedName( const CComPtr& InSymbol, DWORD UndecorateOptions = 0 ); 184 | } 185 | -------------------------------------------------------------------------------- /include/Utils/MemoryGrowableBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class MemoryGrowableBuffer 8 | { 9 | uint8_t* BufferData{}; 10 | uint64_t CurrentOffset{}; 11 | uint64_t CurrentBufferSize{}; 12 | static constexpr uint64_t BufferGrowSizeMultiple = 4096; 13 | public: 14 | MemoryGrowableBuffer(); 15 | // We do not want to have growable buffers copied implicitly. 16 | MemoryGrowableBuffer( const MemoryGrowableBuffer& Other ) = delete; 17 | MemoryGrowableBuffer( MemoryGrowableBuffer&& Other ) noexcept; 18 | ~MemoryGrowableBuffer(); 19 | 20 | MemoryGrowableBuffer& operator=( const MemoryGrowableBuffer& Other ) = delete; 21 | MemoryGrowableBuffer& operator=( MemoryGrowableBuffer&& Other ) noexcept; 22 | 23 | uint64_t Tell() const { return CurrentOffset; } 24 | uint64_t Max() const { return CurrentBufferSize; } 25 | uint64_t Available() const { return CurrentBufferSize - CurrentOffset; } 26 | uint8_t* GetRawBuffer() const { return BufferData; } 27 | 28 | void Seek( uint64_t NewOffset ); 29 | void Skip( uint64_t NumToSkip ); 30 | void Grow( uint64_t GrowAmount ); 31 | 32 | void EnsureCapacity( uint64_t CapacityNeeded ); 33 | void Append( const void* InData, uint64_t InDataSize ); 34 | void Copy( const void* InData, uint64_t InDataSize ); 35 | 36 | void AppendChar( const char* InData, const uint64_t InDataSize ) 37 | { 38 | Append( InData, InDataSize ); 39 | } 40 | 41 | void Append( const MemoryGrowableBuffer& OtherBuffer ); 42 | 43 | template 44 | void Append( const T& InData ) 45 | { 46 | static_assert( std::is_pod_v, "Only POD types can be appended to the buffer memory directly" ); 47 | Append( &InData, sizeof(T) ); 48 | } 49 | 50 | bool WriteToFile(const std::filesystem::path& FilePath) const; 51 | }; 52 | -------------------------------------------------------------------------------- /include/Utils/StringUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::wstring ConvertMbStringToWide( const char* InString ); 7 | 8 | std::wstring StringPrintf( const wchar_t* InFormat, ... ); 9 | 10 | std::wstring StringUtf8ToWide( const std::string& InUtf8String ); 11 | 12 | template 13 | std::wstring JoinToString( const TRangeType& RangeType, const std::wstring& InDelimeter, const TCallable& Callable) 14 | { 15 | std::wostringstream ResultString; 16 | bool bFirstElement = true; 17 | for ( const auto& Element : RangeType ) 18 | { 19 | if ( !bFirstElement ) 20 | { 21 | ResultString << InDelimeter; 22 | } 23 | bFirstElement = false; 24 | ResultString << Callable( Element ); 25 | } 26 | return ResultString.str(); 27 | } 28 | 29 | std::wstring ReplaceAll(std::wstring str, const std::wstring& from, const std::wstring& to); 30 | 31 | template 32 | std::wstring JoinToString( const TRangeType& RangeType, const std::wstring& InDelimeter) 33 | { 34 | std::wostringstream ResultString; 35 | bool bFirstElement = true; 36 | for ( const auto& Element : RangeType ) 37 | { 38 | if ( !bFirstElement ) 39 | { 40 | ResultString << InDelimeter; 41 | } 42 | bFirstElement = false; 43 | ResultString << Element; 44 | } 45 | return ResultString.str(); 46 | } 47 | 48 | // TODO: This is not quite string utils, this should be in a separate header. 49 | 50 | template 51 | void HashCombine(std::size_t& seed, const T& v) 52 | { 53 | std::hash hasher; 54 | seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); 55 | } 56 | 57 | template 58 | struct std::hash> 59 | { 60 | size_t operator()(const std::pair& Pair) const noexcept 61 | { 62 | std::size_t ResultHash = std::hash()(Pair.first); 63 | HashCombine(ResultHash, Pair.second); 64 | return ResultHash; 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /include/Utils/TextWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class FormattedTextWriter 8 | { 9 | std::wostringstream WriterBuffer; 10 | int32_t CurrentIndentionLevel{0}; 11 | bool bNewlineIndentPlaced{false}; 12 | public: 13 | FormattedTextWriter& Append( const std::wstring& InString ); 14 | FormattedTextWriter& Append( const wchar_t* InLiteral ); 15 | 16 | FormattedTextWriter& AppendNewline( const std::wstring& InString ); 17 | FormattedTextWriter& AppendNewline( const wchar_t* InLiteral ); 18 | 19 | FormattedTextWriter& AppendFormat( const wchar_t* InFormat, ... ); 20 | FormattedTextWriter& AppendFormatNewline( const wchar_t* InFormat, ... ); 21 | FormattedTextWriter& AppendFormatNewlineNoIndent( const wchar_t* InFormat, ... ); 22 | FormattedTextWriter& AppendNewline(); 23 | 24 | void PlaceNewlineIndent( int32_t IndentLevelsToIgnore = 0 ); 25 | void ResetNewlineIndent(); 26 | 27 | void IncrementIndentionLevel(); 28 | void DecrementIndentionLevel(); 29 | 30 | void WriteToFile( const std::filesystem::path& FilePath ) const; 31 | std::wstring ToString() const; 32 | }; 33 | 34 | class ScopedIndentionLevel 35 | { 36 | FormattedTextWriter& Writer; 37 | public: 38 | explicit ScopedIndentionLevel( FormattedTextWriter& InWriter ) : Writer( InWriter ) 39 | { 40 | Writer.IncrementIndentionLevel(); 41 | } 42 | ~ScopedIndentionLevel() 43 | { 44 | Writer.DecrementIndentionLevel(); 45 | } 46 | }; -------------------------------------------------------------------------------- /include/Utils/TopoSort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /// topological sort, Kahn's algorithm 7 | template 8 | void topological_sort(I first, S last, F edge) { 9 | const std::size_t n = std::ranges::distance(first, last); 10 | std::vector in_degree(n); 11 | 12 | for (std::size_t i = 0; i < n; ++i) { 13 | for (std::size_t j = 0; j < n; ++j) { 14 | in_degree[i] += static_cast(edge(first[j], first[i])); 15 | } 16 | } 17 | 18 | // [s_first, s_last) are the sources of the sub-graph [s_first, last) 19 | auto s_first = first; 20 | auto s_last = s_first; 21 | 22 | for (std::size_t i = 0; i < n; ++i) { 23 | if (in_degree[i] == 0) { 24 | std::swap(first[i], *s_last); 25 | std::swap(in_degree[i], in_degree[s_last - first]); 26 | ++s_last; 27 | } 28 | } 29 | 30 | for (; s_first != s_last; ++s_first) { 31 | for (auto t_it = s_last; t_it != last; ++t_it) { 32 | if (edge(*s_first, *t_it) && --in_degree[t_it - first] == 0) { 33 | std::swap(*t_it, *s_last); 34 | std::swap(in_degree[t_it - first], in_degree[s_last - first]); 35 | ++s_last; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /resources/config-libraries.json: -------------------------------------------------------------------------------- 1 | { 2 | "DependencyConfigs": [ 3 | "config-stdlib", 4 | "config-windows" 5 | ], 6 | "ExternalHeaders": [ 7 | { 8 | "IncludeName": "format.h", 9 | "ContainedNamespaces": [ "fmt" ] 10 | }, 11 | { 12 | "IncludeName": "msgpack.hpp", 13 | "ContainedNamespaces": [ "msgpack" ] 14 | }, 15 | { 16 | "IncludeName": "boost/container/static_vector.hpp", 17 | "ContainedTypes": [ 18 | "boost::container::static_vector" 19 | ] 20 | }, 21 | { 22 | "IncludeName": "boost/qvm/lite.hpp", 23 | "ContainedNamespaces": [ "boost::qvm" ] 24 | } 25 | ], 26 | "ExternalNamespaces": 27 | [ 28 | "fmt", 29 | "msgpack", 30 | "boost" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /resources/config-stdlib.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExternalHeaders": 3 | [ 4 | { 5 | "IncludeName": "unordered_map", 6 | "ContainedTypes": [ "std::unordered_map" ] 7 | }, 8 | { 9 | "IncludeName": "unordered_set", 10 | "ContainedTypes": [ "std::unordered_set" ] 11 | }, 12 | { 13 | "IncludeName": "set", 14 | "ContainedTypes": [ "std::set" ] 15 | }, 16 | { 17 | "IncludeName": "map", 18 | "ContainedTypes": [ "std::map" ] 19 | }, 20 | { 21 | "IncludeName": "vector", 22 | "ContainedTypes": [ "std::vector" ] 23 | }, 24 | { 25 | "IncludeName": "list", 26 | "ContainedTypes": 27 | [ 28 | "std::list", 29 | "std::_List_const_iterator" 30 | ] 31 | }, 32 | { 33 | "IncludeName": "array", 34 | "ContainedTypes": [ "std::array" ] 35 | }, 36 | { 37 | "IncludeName": "deque", 38 | "ContainedTypes": [ "std::deque" ] 39 | }, 40 | { 41 | "IncludeName": "stack", 42 | "ContainedTypes": [ "std::stack" ] 43 | }, 44 | { 45 | "IncludeName": "string", 46 | "ContainedTypes": 47 | [ 48 | "std::basic_string", 49 | "std::char_traits" 50 | ] 51 | }, 52 | { 53 | "IncludeName": "memory", 54 | "ContainedTypes": 55 | [ 56 | "std::unique_ptr", 57 | "std::shared_ptr", 58 | "std::weak_ptr", 59 | "std::allocator" 60 | ] 61 | }, 62 | { 63 | "IncludeName": "atomic", 64 | "ContainedTypes": [ "std::atomic" ] 65 | }, 66 | { 67 | "IncludeName": "initializer_list", 68 | "ContainedTypes": [ "std::initializer_list" ] 69 | }, 70 | { 71 | "IncludeName": "queue", 72 | "ContainedTypes": [ "std::queue" ] 73 | }, 74 | { 75 | "IncludeName": "thread", 76 | "ContainedTypes": 77 | [ 78 | "std::thread" 79 | ] 80 | }, 81 | { 82 | "IncludeName": "mutex", 83 | "ContainedTypes": 84 | [ 85 | "std::condition_variable", 86 | "std::scoped_lock", 87 | "std::mutex" 88 | ] 89 | }, 90 | { 91 | "IncludeName": "tuple", 92 | "ContainedTypes": [ "std::tuple" ] 93 | }, 94 | { 95 | "IncludeName": "utility", 96 | "ContainedTypes": 97 | [ 98 | "std::pair" 99 | ] 100 | }, 101 | { 102 | "IncludeName": "exception", 103 | "ContainedTypes": 104 | [ 105 | "std::exception", 106 | "std::bad_cast", 107 | "std::runtime_error" 108 | ] 109 | }, 110 | { 111 | "IncludeName": "fstream", 112 | "ContainedTypes": 113 | [ 114 | "std::basic_ofstream", 115 | "std::basic_ifstream" 116 | ] 117 | }, 118 | { 119 | "IncludeName": "filesystem", 120 | "ContainedTypes": 121 | [ 122 | "std::filesystem::path" 123 | ] 124 | }, 125 | { 126 | "IncludeName": "iterator", 127 | "ContainedTypes": 128 | [ 129 | "std::iterator", 130 | "std::back_insert_iterator" 131 | ] 132 | }, 133 | { 134 | "IncludeName": "type_traits", 135 | "ContainedTypes": 136 | [ 137 | "std::hash", 138 | "std::less", 139 | "std::equal_to" 140 | ] 141 | }, 142 | { 143 | "IncludeName": "typeinfo", 144 | "ContainedTypes": [ "std::type_info" ] 145 | }, 146 | { 147 | "IncludeName": "typeindex", 148 | "ContainedTypes": [ "std::type_index" ] 149 | } 150 | ], 151 | "ExternalNamespaces": 152 | [ 153 | "std" 154 | ], 155 | "TypeRemap": 156 | [ 157 | { 158 | "OriginalTypeName": "std::vector>", 159 | "ReplacementTypeName": "std::vector" 160 | }, 161 | { 162 | "OriginalTypeName": "std::map, std::allocator>>", 163 | "ReplacementTypeName": "std::map" 164 | }, 165 | { 166 | "OriginalTypeName": "std::set, std::allocator>", 167 | "ReplacementTypeName": "std::set" 168 | }, 169 | { 170 | "OriginalTypeName": "std::basic_string, std::allocator>", 171 | "ReplacementTypeName": "std::string" 172 | }, 173 | { 174 | "OriginalTypeName": "std::basic_string, std::allocator>", 175 | "ReplacementTypeName": "std::wstring" 176 | } 177 | ] 178 | } -------------------------------------------------------------------------------- /resources/config-windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExternalHeaders": [ 3 | { 4 | "IncludeName": "guiddef.h", 5 | "ContainedTypes": [ 6 | "_GUID" 7 | ] 8 | } 9 | ], 10 | "ExternalGlobalData": 11 | [ 12 | "FOLDERID_LocalAppDataLow" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/AST/CppDeclarationTree.cpp: -------------------------------------------------------------------------------- 1 | #include "AST/CppDeclarationTree.h" 2 | #include 3 | #include 4 | #include 5 | #include "Utils/TextWriter.h" 6 | #include "Utils/TopoSort.h" 7 | 8 | CppFile::CppFile(const DataModel::EDataModel InDataModel, const uint32_t InTypeFormatFlags) 9 | { 10 | FormatRules.BaseTypeFormatRules = TypeFormattingRules{ InDataModel, InTypeFormatFlags }; 11 | } 12 | 13 | void CppFile::Print(FormattedTextWriter& TextWriter) const 14 | { 15 | if ( bIsHeaderFile ) 16 | { 17 | TextWriter.AppendNewline(L"#pragma once").AppendNewline(); 18 | } 19 | else 20 | { 21 | TextWriter.AppendFormatNewline(L"#include \"%s.h\"", FileName.c_str()); 22 | } 23 | 24 | for ( const std::wstring& LocalInclude : LocalIncludes ) 25 | { 26 | TextWriter.AppendFormatNewline(L"#include \"%s.h\"", LocalInclude.c_str()); 27 | } 28 | if ( !LocalIncludes.empty() ) 29 | { 30 | TextWriter.AppendNewline(); 31 | } 32 | 33 | for ( const std::wstring& SystemInclude : SystemIncludes ) 34 | { 35 | TextWriter.AppendFormatNewline(L"#include <%s>", SystemInclude.c_str()); 36 | } 37 | if ( !SystemIncludes.empty() ) 38 | { 39 | TextWriter.AppendNewline(); 40 | } 41 | 42 | // Sort top level declarations based on their relative dependencies 43 | std::vector> DeclarationsCopy = Declarations; 44 | 45 | // Sort top level declarations based on their dependencies 46 | topological_sort( DeclarationsCopy.begin(), DeclarationsCopy.end(), [](const std::shared_ptr& From, const std::shared_ptr& To) 47 | { 48 | return To->Dependencies.contains(From.get()); 49 | } ); 50 | 51 | // Generate top level declarations inside of their relevant namespaces 52 | std::wstring CurrentNamespace = L""; 53 | const wchar_t* LastElementType = nullptr; 54 | 55 | for ( const std::shared_ptr& Declaration : DeclarationsCopy ) 56 | { 57 | bool bNewlineAlreadyAppended = false; 58 | // If the current namespace does not match, close it 59 | if ( Declaration->Namespace != CurrentNamespace ) 60 | { 61 | // Close the current namespace if we have one open 62 | if ( !CurrentNamespace.empty() ) 63 | { 64 | TextWriter.DecrementIndentionLevel(); 65 | TextWriter.AppendNewline(L"}"); 66 | TextWriter.AppendNewline(); 67 | bNewlineAlreadyAppended = true; 68 | } 69 | CurrentNamespace.clear(); 70 | } 71 | 72 | // If this declaration is not inline, or the type of the previous declaration is different, we want to emit an additional newline 73 | const wchar_t* CurrentElementType = Declaration->GetDeclarationTypeName(); 74 | if ( LastElementType == nullptr || wcscmp( LastElementType, CurrentElementType ) != 0 || !Declaration->IsInlineDeclaration() ) 75 | { 76 | // Only append newline if we are not the first declaration in the file 77 | if ( LastElementType != nullptr && !bNewlineAlreadyAppended ) 78 | { 79 | TextWriter.AppendNewline(); 80 | } 81 | LastElementType = CurrentElementType; 82 | } 83 | 84 | // If current namespace does not match, open a new one 85 | if ( Declaration->Namespace != CurrentNamespace ) 86 | { 87 | CurrentNamespace = Declaration->Namespace; 88 | 89 | // Open a new namespace 90 | if ( !CurrentNamespace.empty() ) 91 | { 92 | TextWriter.AppendFormatNewline(L"namespace %s", CurrentNamespace.c_str()); 93 | TextWriter.AppendNewline(L"{"); 94 | TextWriter.IncrementIndentionLevel(); 95 | } 96 | } 97 | Declaration->Print( TextWriter, FormatRules.AppendScope( CurrentNamespace ) ); 98 | } 99 | 100 | // Close the final namespace if we have one open 101 | if ( !CurrentNamespace.empty() ) 102 | { 103 | TextWriter.DecrementIndentionLevel(); 104 | TextWriter.AppendNewline(L"}"); 105 | TextWriter.AppendNewline(); 106 | } 107 | } 108 | 109 | namespace Foo 110 | { 111 | 112 | } 113 | 114 | void CppFile::WriteToFile(const std::filesystem::path& DirectoryPath) const 115 | { 116 | FormattedTextWriter TextWriter; 117 | Print( TextWriter ); 118 | create_directories(DirectoryPath); 119 | 120 | std::filesystem::path Filename(FileName); 121 | Filename.replace_extension( std::filesystem::path( bIsHeaderFile ? L"h" : L"cpp" ) ); 122 | const std::filesystem::path FinalFilePath = absolute( DirectoryPath / Filename ); 123 | 124 | std::wcout << L"Writing file: " << FinalFilePath.generic_wstring() << std::endl; 125 | TextWriter.WriteToFile( FinalFilePath ); 126 | } 127 | 128 | void TypedefDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 129 | { 130 | TextWriter.AppendFormatNewline(L"typedef %s %s;", TypeName.c_str(), TypedefName.c_str()); 131 | } 132 | 133 | void EnumDeclarationData::PrintEnum(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const bool bIsAnonymousEnum) const 134 | { 135 | assert( UnderlyingType->GetId() == ETypeDeclarationId::FundamentalType && L"Enum underlying type should be a fundamental integral type" ); 136 | const bool bIsUnsigned = static_cast( UnderlyingType.get() )->bIsUnsigned; 137 | if ( !bIsAnonymousEnum ) 138 | { 139 | 140 | TextWriter.AppendFormat(L"%s %s : ", bIsScoped ? L"enum class" : L"enum", EnumName.c_str()); 141 | UnderlyingType->Print(TextWriter, Rules); 142 | TextWriter.AppendNewline(); 143 | } 144 | else 145 | { 146 | TextWriter.Append(L"enum : "); 147 | UnderlyingType->Print(TextWriter, Rules); 148 | TextWriter.AppendNewline(); 149 | } 150 | TextWriter.AppendNewline(L"{"); 151 | TextWriter.IncrementIndentionLevel(); 152 | 153 | for ( const auto& [ValueName, ValueInt] : Values ) 154 | { 155 | TextWriter.AppendFormatNewline(bIsUnsigned ? L"%s = %llu," : L"%s = %lld,", ValueName.c_str(), ValueInt); 156 | } 157 | TextWriter.DecrementIndentionLevel(); 158 | if ( !bIsAnonymousEnum ) 159 | { 160 | TextWriter.AppendNewline(L"};"); 161 | } 162 | else 163 | { 164 | TextWriter.Append(L"}"); 165 | } 166 | } 167 | 168 | EnumDeclaration::EnumDeclaration(const std::shared_ptr& InDeclData) : Data( InDeclData ) 169 | { 170 | } 171 | 172 | void EnumDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 173 | { 174 | Data->PrintEnum(TextWriter, Rules.BaseTypeFormatRules); 175 | } 176 | 177 | UDTNestedEnumDeclaration::UDTNestedEnumDeclaration(const std::shared_ptr& InDeclData) : Data( InDeclData ) 178 | { 179 | } 180 | 181 | void UDTNestedEnumDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 182 | { 183 | Data->PrintEnum(TextWriter, Rules); 184 | } 185 | 186 | PredeclarationStatement::PredeclarationStatement(const std::shared_ptr& InTypeStatement) : PredeclarationType( InTypeStatement ) 187 | { 188 | // Extract namespace from the type to use it 189 | if ( InTypeStatement->GetId() == ETypeDeclarationId::Enum ) 190 | { 191 | const EnumTypeDeclaration* EnumDeclaration = static_cast( InTypeStatement.get() ); 192 | Namespace = EnumDeclaration->OuterScope; 193 | assert( EnumDeclaration->OuterType == nullptr && L"Predeclarations cannot be nested" ); 194 | } 195 | else if ( InTypeStatement->GetId() == ETypeDeclarationId::UDT ) 196 | { 197 | const UDTTypeDeclaration* UDTDeclaration = static_cast( InTypeStatement.get() ); 198 | Namespace = UDTDeclaration->OuterScope; 199 | assert( UDTDeclaration->OuterType == nullptr && L"Predeclarations cannot be nested" ); 200 | } 201 | } 202 | 203 | void PredeclarationStatement::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 204 | { 205 | // Predeclarations always need CSU/Enum specifiers. Essentially, they are type names with specifiers. 206 | // We also do not emit outer scope since it is handled by the namespace of the top level statement 207 | PredeclarationType->Print(TextWriter, Rules.BaseTypeFormatRules 208 | .AppendFlagsNonInheritable( TypeFormattingRules::EmitEnumSpecifier | TypeFormattingRules::EmitCSUSpecifier | TypeFormattingRules::SkipOuterScope ) ); 209 | TextWriter.AppendNewline(L";"); 210 | } 211 | 212 | const wchar_t* CppAccessModifierToString(const CppAccessModifier AccessModifier) 213 | { 214 | switch ( AccessModifier ) 215 | { 216 | case CppAccessModifier::Private: return L"private"; 217 | case CppAccessModifier::Protected: return L"protected"; 218 | case CppAccessModifier::Public: return L"public"; 219 | } 220 | assert(0); 221 | return L""; 222 | } 223 | 224 | CppAccessModifier DefaultAccessModifierForCppUDTKind(const CppUDTKind Kind) 225 | { 226 | switch ( Kind ) 227 | { 228 | case CppUDTKind::Class: return CppAccessModifier::Private; 229 | case CppUDTKind::Struct: return CppAccessModifier::Public; 230 | case CppUDTKind::Union: return CppAccessModifier::Public; 231 | } 232 | assert(0); 233 | return CppAccessModifier::Private; 234 | } 235 | 236 | void UDTFunctionDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 237 | { 238 | if ( !Comment.empty() ) 239 | { 240 | TextWriter.AppendFormatNewline( L"/* %s */", Comment.c_str() ); 241 | } 242 | if ( bIsTemplateSpecialization ) 243 | { 244 | TextWriter.Append(L"template<> "); 245 | } 246 | if ( !InlineImplementation.empty() ) 247 | { 248 | TextWriter.Append(L"__forceinline "); 249 | } 250 | if ( bIsExplicit ) 251 | { 252 | TextWriter.Append(L"explicit "); 253 | } 254 | if ( bIsStatic ) 255 | { 256 | TextWriter.Append(L"static "); 257 | } 258 | else if ( bIsVirtual ) 259 | { 260 | TextWriter.Append(L"virtual "); 261 | } 262 | 263 | if ( !bNoReturnType ) 264 | { 265 | ReturnType->Print(TextWriter, Rules); 266 | TextWriter.Append(L" "); 267 | } 268 | TextWriter.AppendFormat(L"%s", MemberName.c_str()); 269 | if ( bIsTemplateSpecialization ) 270 | { 271 | TextWriter.Append(L"<"); 272 | bool bIsFirstTemplateArgument = true; 273 | for ( const TypeTemplateArgument& Argument : TemplateArguments ) 274 | { 275 | if ( !bIsFirstTemplateArgument ) 276 | { 277 | TextWriter.Append(L", "); 278 | } 279 | bIsFirstTemplateArgument = false; 280 | Argument.Print( TextWriter, Rules ); 281 | } 282 | TextWriter.Append(L">"); 283 | } 284 | TextWriter.Append(L"("); 285 | 286 | bool bIsFirstParameter = true; 287 | for ( const auto& [ParameterName, ParameterType] : ParameterNamesAndTypes ) 288 | { 289 | if ( !bIsFirstParameter ) 290 | { 291 | TextWriter.Append(L", "); 292 | } 293 | bIsFirstParameter = false; 294 | if ( !ParameterName.empty() ) 295 | { 296 | ParameterType->Print(TextWriter, Rules); 297 | TextWriter.AppendFormat(L" %s", ParameterName.c_str()); 298 | } 299 | else 300 | { 301 | ParameterType->Print(TextWriter, Rules); 302 | } 303 | } 304 | if ( bIsVariadicArguments ) 305 | { 306 | if ( !bIsFirstParameter ) 307 | { 308 | TextWriter.Append(L", "); 309 | } 310 | TextWriter.Append(L"..."); 311 | } 312 | TextWriter.Append(L")"); 313 | if ( bIsConst ) 314 | { 315 | TextWriter.Append(L" const"); 316 | } 317 | if ( !bIsStatic && bIsVirtual ) 318 | { 319 | if ( bIsOverride ) 320 | { 321 | TextWriter.Append(L" override"); 322 | } 323 | if ( bIsPureVirtual ) 324 | { 325 | TextWriter.Append(L" = 0"); 326 | } 327 | } 328 | if ( !InlineImplementation.empty() ) 329 | { 330 | TextWriter.Append( InlineImplementation ); 331 | TextWriter.AppendNewline(); 332 | } 333 | else 334 | { 335 | TextWriter.AppendNewline(L";"); 336 | } 337 | } 338 | 339 | void UDTDataMemberDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 340 | { 341 | if ( bIsStatic ) 342 | { 343 | TextWriter.Append(L"static "); 344 | } 345 | if ( bIsConstexpr ) 346 | { 347 | TextWriter.Append(L"constexpr "); 348 | } 349 | else if ( bIsConst ) 350 | { 351 | TextWriter.Append(L"const "); 352 | } 353 | else if ( bIsThreadLocal ) 354 | { 355 | TextWriter.Append(L"thread_local "); 356 | } 357 | 358 | // Print type and member name 359 | MemberType->PrintVariableType( TextWriter, Rules, MemberName ); 360 | if ( BitfieldSize != -1 ) 361 | { 362 | TextWriter.AppendFormat(L": %d", BitfieldSize); 363 | } 364 | 365 | if ( !ConstantValue.empty() ) 366 | { 367 | assert( BitfieldSize == -1 ); // bitfields cannot have value assigned to them 368 | TextWriter.AppendFormat(L" = %s", ConstantValue.c_str()); 369 | } 370 | else if ( bWantsDefaultInitializer ) 371 | { 372 | assert( BitfieldSize == -1 ); // bitfields cannot have default initializers 373 | TextWriter.Append(L"{}"); 374 | } 375 | TextWriter.AppendNewline(L";"); 376 | } 377 | 378 | void UDTDeclarationBaseClass::Print(FormattedTextWriter& TextWriter, const CppUDTKind ClassKind, const TypeFormattingRules& Rules) const 379 | { 380 | // Only emit access modifier if it is different from the default one 381 | if ( AccessModifier != DefaultAccessModifierForCppUDTKind( ClassKind ) ) 382 | { 383 | TextWriter.AppendFormat(L"%s ", CppAccessModifierToString( AccessModifier )); 384 | } 385 | if ( bIsVirtual ) 386 | { 387 | TextWriter.Append(L"virtual "); 388 | } 389 | assert( BaseClass->IsInlineDeclaration() && L"Base class type declarations should always be inline" ); 390 | assert( BaseClass->GetId() == ETypeDeclarationId::UDT && L"Base class type should be an UDT type declaration" ); 391 | BaseClass->Print(TextWriter, Rules); 392 | } 393 | 394 | void UDTDeclarationData::PrintUDT(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules, const bool bIsAnonymousType ) const 395 | { 396 | if ( !bIsAnonymousType ) 397 | { 398 | if ( bIsTemplateSpecialization ) 399 | { 400 | TextWriter.Append(L"template<> "); 401 | } 402 | TextWriter.AppendFormat(L"%s ", CppUDTKindToString(Kind)); 403 | 404 | if ( bIsDllImport ) 405 | { 406 | TextWriter.Append(L"DLLIMPORT "); 407 | } 408 | TextWriter.AppendFormat(L"%s", ClassName.c_str()); 409 | 410 | if ( bIsTemplateSpecialization ) 411 | { 412 | TextWriter.Append(L"<"); 413 | bool bIsFirstTemplateArgument = true; 414 | for ( const TypeTemplateArgument& Argument : TemplateArguments ) 415 | { 416 | if ( !bIsFirstTemplateArgument ) 417 | { 418 | TextWriter.Append(L", "); 419 | } 420 | bIsFirstTemplateArgument = false; 421 | Argument.Print( TextWriter, Rules ); 422 | } 423 | TextWriter.Append(L">"); 424 | } 425 | 426 | if ( bIsFinal ) 427 | { 428 | TextWriter.Append(L" final"); 429 | } 430 | if ( !BaseClasses.empty() ) 431 | { 432 | TextWriter.Append(L" : "); 433 | bool bIsFirstBaseClass = true; 434 | for ( const UDTDeclarationBaseClass& BaseClass : BaseClasses ) 435 | { 436 | if ( !bIsFirstBaseClass ) 437 | { 438 | TextWriter.Append(L", "); 439 | } 440 | bIsFirstBaseClass = false; 441 | BaseClass.Print( TextWriter, Kind, Rules ); 442 | } 443 | } 444 | } 445 | else 446 | { 447 | TextWriter.Append( CppUDTKindToString(Kind) ); 448 | } 449 | TextWriter.AppendNewline(); 450 | TextWriter.AppendNewline(L"{"); 451 | TextWriter.IncrementIndentionLevel(); 452 | 453 | // Sort top level declarations based on their dependencies 454 | std::vector> MembersCopy = Members; 455 | topological_sort( MembersCopy.begin(), MembersCopy.end(), [](const std::shared_ptr& From, const std::shared_ptr& To) 456 | { 457 | if ( To->Dependencies.contains(From.get()) ) 458 | { 459 | return true; 460 | } 461 | if ( From->Priority > To->Priority ) 462 | { 463 | return true; 464 | } 465 | return false; 466 | } ); 467 | 468 | CppAccessModifier CurrentAccessModifier = DefaultAccessModifierForCppUDTKind( Kind ); 469 | for ( const std::shared_ptr& MemberDeclaration : MembersCopy ) 470 | { 471 | // Update access modifier if it does not match the current one 472 | if ( MemberDeclaration->AccessModifier != CurrentAccessModifier ) 473 | { 474 | TextWriter.AppendFormatNewlineNoIndent(L"%s:", CppAccessModifierToString(MemberDeclaration->AccessModifier)); 475 | CurrentAccessModifier = MemberDeclaration->AccessModifier; 476 | } 477 | MemberDeclaration->Print(TextWriter, bIsAnonymousType ? Rules : Rules.AppendScope(ClassName) ); 478 | } 479 | 480 | TextWriter.DecrementIndentionLevel(); 481 | if ( !bIsAnonymousType ) 482 | { 483 | TextWriter.AppendNewline(L"};"); 484 | } 485 | else 486 | { 487 | TextWriter.Append(L"}"); 488 | } 489 | } 490 | 491 | UDTNestedTypeDeclaration::UDTNestedTypeDeclaration(const std::shared_ptr& InDeclData) : Data( InDeclData ) 492 | { 493 | } 494 | 495 | UDTDeclaration::UDTDeclaration(const std::shared_ptr& InDeclData) : Data( InDeclData ) 496 | { 497 | } 498 | 499 | void UDTNestedTypeDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 500 | { 501 | Data->PrintUDT(TextWriter, Rules); 502 | } 503 | 504 | void UDTDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 505 | { 506 | Data->PrintUDT(TextWriter, Rules.BaseTypeFormatRules); 507 | } 508 | 509 | void GlobalDataDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 510 | { 511 | // Make sure we follow the name mangling scheme used by the symbol, otherwise we will not be able to resolve imports 512 | if ( bIsExternCLinkage ) 513 | { 514 | assert( !bIsTemplateSpecialization ); 515 | TextWriter.Append(L"extern \"C\" "); 516 | } 517 | if ( bIsTemplateSpecialization ) 518 | { 519 | TextWriter.Append(L"template<> "); 520 | } 521 | if ( bIsThreadLocal ) 522 | { 523 | TextWriter.Append(L"thread_local "); 524 | } 525 | if ( bIsConstexpr ) 526 | { 527 | TextWriter.Append(L"constexpr "); 528 | } 529 | if ( bIsDllImport ) 530 | { 531 | TextWriter.Append(L"DLLIMPORT "); 532 | } 533 | 534 | // Print type and member name 535 | DataType->PrintVariableType( TextWriter, Rules.BaseTypeFormatRules, DataName ); 536 | 537 | if ( bIsTemplateSpecialization ) 538 | { 539 | TextWriter.Append(L"<"); 540 | bool bIsFirstTemplateArgument = true; 541 | for ( const TypeTemplateArgument& Argument : TemplateArguments ) 542 | { 543 | if ( !bIsFirstTemplateArgument ) 544 | { 545 | TextWriter.Append(L", "); 546 | } 547 | bIsFirstTemplateArgument = false; 548 | Argument.Print( TextWriter, Rules.BaseTypeFormatRules ); 549 | } 550 | TextWriter.Append(L">"); 551 | } 552 | if ( !ConstantValue.empty() ) 553 | { 554 | TextWriter.AppendFormat(L" = %s", ConstantValue.c_str()); 555 | } 556 | TextWriter.AppendNewline(L";"); 557 | } 558 | 559 | void GlobalFunctionDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 560 | { 561 | // Make sure we follow the name mangling scheme used by the symbol, otherwise we will not be able to resolve imports 562 | if ( bIsExternCLinkage ) 563 | { 564 | assert( !bIsTemplateSpecialization ); 565 | TextWriter.Append(L"extern \"C\" "); 566 | } 567 | else if ( bIsDllImport ) 568 | { 569 | TextWriter.Append(L"extern "); 570 | } 571 | 572 | if ( bIsTemplateSpecialization ) 573 | { 574 | TextWriter.Append(L"template<> "); 575 | } 576 | if ( bIsDllImport ) 577 | { 578 | TextWriter.Append(L"__declspec(dllimport) "); 579 | } 580 | ReturnType->Print(TextWriter, Rules.BaseTypeFormatRules); 581 | TextWriter.AppendFormat(L" %s", FunctionName.c_str()); 582 | 583 | if ( bIsTemplateSpecialization ) 584 | { 585 | TextWriter.Append(L"<"); 586 | bool bIsFirstTemplateArgument = true; 587 | for ( const TypeTemplateArgument& Argument : TemplateArguments ) 588 | { 589 | if ( !bIsFirstTemplateArgument ) 590 | { 591 | TextWriter.Append(L", "); 592 | } 593 | bIsFirstTemplateArgument = false; 594 | Argument.Print( TextWriter, Rules.BaseTypeFormatRules ); 595 | } 596 | TextWriter.Append(L">"); 597 | } 598 | TextWriter.Append(L"("); 599 | 600 | bool bIsFirstParameter = true; 601 | for ( const auto& [ParameterName, ParameterType] : ParameterNamesAndTypes ) 602 | { 603 | if ( !bIsFirstParameter ) 604 | { 605 | TextWriter.Append(L", "); 606 | } 607 | bIsFirstParameter = false; 608 | if ( !ParameterName.empty() ) 609 | { 610 | ParameterType->PrintVariableType(TextWriter, Rules.BaseTypeFormatRules, ParameterName); 611 | } 612 | else 613 | { 614 | ParameterType->Print(TextWriter, Rules.BaseTypeFormatRules); 615 | } 616 | } 617 | if ( bIsVariadicArguments ) 618 | { 619 | if ( !bIsFirstParameter ) 620 | { 621 | TextWriter.Append(L", "); 622 | } 623 | TextWriter.Append(L"..."); 624 | } 625 | TextWriter.Append(L")"); 626 | TextWriter.AppendNewline(L";"); 627 | } 628 | 629 | void AnonymousUDTTypeDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 630 | { 631 | Data->PrintUDT(TextWriter, Rules, true); 632 | } 633 | 634 | bool AnonymousUDTTypeDeclaration::Identical(const std::shared_ptr& InOtherDeclaration) const 635 | { 636 | // Two anonymous declarations are only identical if they are the same object 637 | return this == InOtherDeclaration.get(); 638 | } 639 | 640 | std::shared_ptr AnonymousUDTTypeDeclaration::Clone() const 641 | { 642 | return std::make_shared( *this ); 643 | } 644 | 645 | std::shared_ptr AnonymousEnumTypeDeclaration::Clone() const 646 | { 647 | return std::make_shared( *this ); 648 | } 649 | 650 | size_t AnonymousUDTTypeDeclaration::GetDeclarationHash() const 651 | { 652 | return std::hash()( this ); 653 | } 654 | 655 | void AnonymousEnumTypeDeclaration::Print(FormattedTextWriter& TextWriter, const TypeFormattingRules& Rules) const 656 | { 657 | Data->PrintEnum(TextWriter, Rules, true); 658 | } 659 | 660 | bool AnonymousEnumTypeDeclaration::Identical(const std::shared_ptr& InOtherDeclaration) const 661 | { 662 | // Two anonymous declarations are only identical if they are the same object 663 | return this == InOtherDeclaration.get(); 664 | } 665 | 666 | size_t AnonymousEnumTypeDeclaration::GetDeclarationHash() const 667 | { 668 | return std::hash()( this ); 669 | } 670 | 671 | void TemplateDeclarationArgument::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 672 | { 673 | if ( Type == ETemplateDeclarationArgumentType::Typename ) 674 | { 675 | TextWriter.Append(L"typename"); 676 | } 677 | else if ( Type == ETemplateDeclarationArgumentType::TypeValue ) 678 | { 679 | TypeValueKind->Print(TextWriter, Rules.BaseTypeFormatRules); 680 | } 681 | } 682 | 683 | void TemplateTypeDeclaration::Print(FormattedTextWriter& TextWriter, const DeclarationPrintRules& Rules) const 684 | { 685 | TextWriter.Append(L"template"); 686 | TextWriter.Append(L"<"); 687 | bool bIsFirstArgument = true; 688 | for ( const TemplateDeclarationArgument& Argument : Arguments ) 689 | { 690 | if ( !bIsFirstArgument ) 691 | { 692 | TextWriter.Append(L", "); 693 | } 694 | bIsFirstArgument = false; 695 | Argument.Print( TextWriter, Rules ); 696 | } 697 | TextWriter.Append(L">"); 698 | TextWriter.Append(L" "); 699 | 700 | TextWriter.AppendFormatNewline(L"%s %s;", CppUDTKindToString(UDTKind), ClassName.c_str()); 701 | } 702 | -------------------------------------------------------------------------------- /src/CodeGeneration.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGeneration.h" 2 | #include "GeneratedHeaderFile.h" 3 | #include "AST/CppDeclarationTree.h" 4 | #include "Utils/StringUtils.h" 5 | #include 6 | 7 | CppAccessModifier CodeGeneration::ConvertAccessModifier(const DWORD InAccessModifier) 8 | { 9 | switch ( InAccessModifier ) 10 | { 11 | case CV_private: return CppAccessModifier::Private; 12 | case CV_protected: return CppAccessModifier::Protected; 13 | case CV_public: return CppAccessModifier::Public; 14 | default:; 15 | } 16 | assert(!L"unknown DIA SDK access modifier value"); 17 | return CppAccessModifier::Public; 18 | } 19 | 20 | CppUDTKind CodeGeneration::ConvertUDTKind(const DWORD InUDTKind) 21 | { 22 | switch ( InUDTKind ) 23 | { 24 | case UdtClass: return CppUDTKind::Class; 25 | case UdtInterface: return CppUDTKind::Class; 26 | case UdtStruct: return CppUDTKind::Struct; 27 | case UdtUnion: return CppUDTKind::Union; 28 | case UdtTaggedUnion: return CppUDTKind::Union; 29 | default:; 30 | } 31 | assert(!L"unknown DIA SDK UDT kind"); 32 | return CppUDTKind::Class; 33 | } 34 | 35 | static bool IsInternalSymbolNameInternal(const std::wstring& InFunctionName) 36 | { 37 | // C++ runtime and standard library functions, starting with 2 underscores or an underscore and an uppercase letter 38 | if ( InFunctionName.size() >= 2 && InFunctionName[0] == L'_' && ( InFunctionName[1] == L'_' || iswupper( InFunctionName[1] ) ) ) 39 | { 40 | return true; 41 | } 42 | // $initializer$ symbols are dynamic initializer function pointers, and symbols starting with tilde (`) are dynamic initializer arrays 43 | // TODO: $initializer$ is a valid function name, we could potentially be treating a normal user-defined function as special 44 | if( InFunctionName.find(L'`') != std::wstring::npos || InFunctionName.find(L"$initializer$") != std::wstring::npos ) 45 | { 46 | return true; 47 | } 48 | // Should not generate headers for DllMain as well 49 | if ( InFunctionName == L"DllMain" ) 50 | { 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | bool CodeGeneration::IsInternalSymbolName(const SymbolNameInfo& SymbolName) 57 | { 58 | return IsInternalSymbolNameInternal( SymbolName.LocalName ) || 59 | IsInternalSymbolNameInternal( SymbolName.SymbolScope ) || 60 | (SymbolName.SymbolScope == L"std" || SymbolName.SymbolScope.starts_with(L"std::")); 61 | } 62 | 63 | std::wstring EscapeString( std::wstring CharLiteral ) 64 | { 65 | CharLiteral = ReplaceAll( CharLiteral, L"\\", L"\\\\" ); 66 | CharLiteral = ReplaceAll( CharLiteral, L"\"", L"\\\"" ); 67 | CharLiteral = ReplaceAll( CharLiteral, L"\'", L"\\\'" ); 68 | CharLiteral = ReplaceAll( CharLiteral, L"\?", L"\\\?" ); 69 | CharLiteral = ReplaceAll( CharLiteral, L"\a", L"\\\a" ); 70 | CharLiteral = ReplaceAll( CharLiteral, L"\b", L"\\\b" ); 71 | CharLiteral = ReplaceAll( CharLiteral, L"\f", L"\\\f" ); 72 | CharLiteral = ReplaceAll( CharLiteral, L"\n", L"\\\n" ); 73 | CharLiteral = ReplaceAll( CharLiteral, L"\r", L"\\\r" ); 74 | CharLiteral = ReplaceAll( CharLiteral, L"\t", L"\\\t" ); 75 | CharLiteral = ReplaceAll( CharLiteral, L"\v", L"\\\v" ); 76 | return CharLiteral; 77 | } 78 | 79 | std::wstring CodeGeneration::GenerateConstantValue(const VARIANT& InVariant) 80 | { 81 | if ( InVariant.vt == VT_I1 || InVariant.vt == VT_I2 || InVariant.vt == VT_I4 || InVariant.vt == VT_I8 || InVariant.vt == VT_INT ) 82 | { 83 | return StringPrintf(L"%lld", InVariant.vt == VT_I1 ? InVariant.cVal : InVariant.vt == VT_I2 ? InVariant.iVal : InVariant.vt == VT_I4 ? InVariant.lVal : InVariant.lVal == VT_I8 ? InVariant.llVal : InVariant.intVal ); 84 | } 85 | if ( InVariant.vt == VT_UI1 || InVariant.vt == VT_UI2 || InVariant.vt == VT_UI4 || InVariant.vt == VT_UI8 || InVariant.vt == VT_UINT ) 86 | { 87 | return StringPrintf(L"%llu", InVariant.vt == VT_UI1 ? InVariant.bVal : InVariant.vt == VT_UI2 ? InVariant.uiVal : InVariant.vt == VT_UI4 ? InVariant.ulVal : InVariant.lVal == VT_UI8 ? InVariant.ullVal : InVariant.uintVal ); 88 | } 89 | if ( InVariant.vt == VT_R4 || InVariant.vt == VT_R8 ) 90 | { 91 | return StringPrintf(L"%.2f", InVariant.vt == VT_R4 ? InVariant.fltVal : InVariant.dblVal ); 92 | } 93 | if ( InVariant.vt == VT_BSTR ) 94 | { 95 | // Replace quotes with escaped quotes, slashes with escaped slashes, and special characters with their equivalents 96 | const std::wstring CharLiteral = InVariant.bstrVal; 97 | return StringPrintf(L"\"%s\"", EscapeString( CharLiteral ).c_str()); 98 | } 99 | assert( L"Unsupported VARIANT for constant value generation" ); 100 | return L""; 101 | } 102 | 103 | std::shared_ptr CodeGeneration::FormatTypeName(const CComPtr& InTypeSymbol, ITypeResolutionProvider* TypeProvider ) 104 | { 105 | const DWORD SymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, symTag ); 106 | 107 | if ( SymbolTag == SymTagArrayType ) 108 | { 109 | const CComPtr ElementType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ); 110 | const DWORD ArrayCount = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, count ); 111 | 112 | const std::shared_ptr ElementTypeDecl = FormatTypeName( ElementType, TypeProvider ); 113 | const std::shared_ptr ArrayTypeDecl = std::make_shared(); 114 | ArrayTypeDecl->ElementType = ElementTypeDecl; 115 | if ( ArrayCount != 0 ) 116 | { 117 | ArrayTypeDecl->ArrayDimension = ArrayCount; 118 | } 119 | return ArrayTypeDecl; 120 | } 121 | if ( SymbolTag == SymTagBaseType ) 122 | { 123 | const DWORD BaseType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, baseType ); 124 | const LONGLONG BaseTypeSize = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, length ); 125 | const BOOL bIsBaseTypeConst = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, constType ); 126 | 127 | const std::shared_ptr BasicTypeDecl = FormatBasicTypeName( BaseType, BaseTypeSize ); 128 | BasicTypeDecl->bIsConst = bIsBaseTypeConst; 129 | return BasicTypeDecl; 130 | } 131 | if ( SymbolTag == SymTagCustomType ) 132 | { 133 | const DWORD VendorDefinedTypeId = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, oemId ); 134 | const SymbolNameInfo CustomTypeName = SymbolNameInfo::FromSymbol( InTypeSymbol ); 135 | 136 | const std::shared_ptr TypeDecl = std::make_shared(); 137 | TypeDecl->OuterScope = CustomTypeName.SymbolScope; 138 | TypeDecl->ClassName = CustomTypeName.LocalName.empty() ? StringPrintf(L"OEM_TYPE_%d", VendorDefinedTypeId) : CustomTypeName.LocalName; 139 | return TypeDecl; 140 | } 141 | if ( SymbolTag == SymTagEnum ) 142 | { 143 | // Enumerations are named user defined types. We could use underlying basic type here, but we prefer to just emit the enumeration name instead 144 | const BOOL bIsEnumConst = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, constType ); 145 | const SymbolNameInfo EnumerationNameInfo = SymbolNameInfo::FromSymbol( InTypeSymbol ); 146 | 147 | // If this is an anonymous type, generate it 148 | if ( EnumerationNameInfo.bIsAnonymousSymbol ) 149 | { 150 | const std::shared_ptr TypeDecl = std::make_shared(); 151 | TypeDecl->Data = GeneratedHeaderFile::MakeEnum( InTypeSymbol, nullptr, TypeProvider ); 152 | TypeDecl->bIsConst = bIsEnumConst; 153 | return TypeDecl; 154 | } 155 | 156 | // This is a non-anonymous enumeration symbol, generate the type name 157 | const CComPtr UnderlyingType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ); 158 | const BOOL bIsEnumScoped = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, scoped ); 159 | 160 | const std::shared_ptr TypeDecl = std::make_shared(); 161 | TypeDecl->OuterScope = EnumerationNameInfo.SymbolScope; 162 | TypeDecl->EnumName = EnumerationNameInfo.LocalName; 163 | TypeDecl->bIsConst = bIsEnumConst; 164 | 165 | // Attach additional data in case we want to emit enum predeclaration 166 | TypeDecl->UnderlyingType = FormatTypeName( UnderlyingType, TypeProvider ); 167 | TypeDecl->bIsEnumClass = bIsEnumScoped; 168 | return TypeDecl; 169 | } 170 | if ( SymbolTag == SymTagPointerType ) 171 | { 172 | const CComPtr PointeeType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ); 173 | const BOOL bIsPointerConst = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, constType ); 174 | const BOOL bIsPointerReference = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, reference ); 175 | const DWORD PointeeSymTag = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( PointeeType, symTag ); 176 | 177 | // If we are pointing at the function signature, we do not need to do anything, just let it handle itself. Non-pointer function signatures are not a thing. 178 | if ( PointeeSymTag == SymTagFunctionType ) 179 | { 180 | const std::shared_ptr FunctionSignatureType = FormatTypeName( PointeeType, TypeProvider ); 181 | assert( FunctionSignatureType->GetId() == ETypeDeclarationId::FunctionType ); 182 | static_cast( FunctionSignatureType.get() )->bIsFunctionPointer = true; 183 | return FunctionSignatureType; 184 | } 185 | 186 | const std::shared_ptr PointeeTypeDecl = FormatTypeName( PointeeType, TypeProvider ); 187 | 188 | const std::shared_ptr PointerTypeDecl = std::make_shared(); 189 | PointerTypeDecl->PointeeType = PointeeTypeDecl; 190 | PointerTypeDecl->bIsReference = bIsPointerReference; 191 | PointerTypeDecl->bIsConst = bIsPointerConst; 192 | return PointerTypeDecl; 193 | } 194 | if ( SymbolTag == SymTagTypedef ) 195 | { 196 | // TODO: Typedefs require a separate type that needs to be transparent, e.g. retrospectable. For now, emit the underlying type instead 197 | const CComPtr TypedefSymbol = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ); 198 | return FormatTypeName( TypedefSymbol, TypeProvider ); 199 | } 200 | if ( SymbolTag == SymTagFunctionType ) 201 | { 202 | const std::shared_ptr FunctionSignatureType = std::make_shared(); 203 | FormatFunctionType( InTypeSymbol, TypeProvider, FunctionSignatureType->ReturnType, FunctionSignatureType->Arguments, FunctionSignatureType->bIsVariadicArguments, 204 | &FunctionSignatureType->OwnerType, nullptr, &FunctionSignatureType->bIsConstMemberFunction ); 205 | 206 | return FunctionSignatureType; 207 | } 208 | if ( SymbolTag == SymTagUDT ) 209 | { 210 | const SymbolNameInfo UserDefinedTypeName = SymbolNameInfo::FromSymbol( InTypeSymbol ); 211 | const BOOL bIsTypeConst = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, constType ); 212 | 213 | // If this is an anonymous type, generate it 214 | if ( UserDefinedTypeName.bIsAnonymousSymbol ) 215 | { 216 | const std::shared_ptr TypeDecl = std::make_shared(); 217 | TypeDecl->Data = GeneratedHeaderFile::MakeUDT( InTypeSymbol, nullptr, nullptr, TypeProvider ); 218 | TypeDecl->bIsConst = bIsTypeConst; 219 | return TypeDecl; 220 | } 221 | 222 | // This is a non-anonymous UDT symbol, generate the type name 223 | const DWORD TypeUDTKind = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, udtKind ); 224 | const std::shared_ptr TypeDecl = std::make_shared(); 225 | TypeDecl->OuterScope = UserDefinedTypeName.SymbolScope; 226 | TypeDecl->ClassName = UserDefinedTypeName.LocalName; 227 | TypeDecl->bIsConst = bIsTypeConst; 228 | 229 | // Parse template instantiation arguments. This will allow us to pretty-print them and also gather their dependencies, 230 | // as well as substitute more verbose template instantiations with simpler declarations 231 | // TODO: This will not correctly generate template arguments for nested types declared inside of namespaced types. 232 | if ( UserDefinedTypeName.bIsTemplateInstantiation ) 233 | { 234 | TypeTemplateArgumentContainer TemplateArguments; 235 | assert( TypeTemplateArgumentContainer::ParseTemplateArguments( UserDefinedTypeName.TemplateArguments, TemplateArguments ) && L"Failed to parse template instantiation arguments for UDT type" ); 236 | PatchupInternalTypeReferences( TemplateArguments, InTypeSymbol ); 237 | TypeDecl->TemplateArguments = TemplateArguments; 238 | } 239 | 240 | // Potentially remap type name to another type name with less verbose template instantiation 241 | std::wstring ReplacementNamespace, ReplacementClassName; 242 | TypeTemplateArgumentContainer ReplacementTemplate; 243 | if ( TypeProvider->RemapTypeReference( TypeDecl->OuterScope, TypeDecl->ClassName, TypeDecl->TemplateArguments, ReplacementNamespace, ReplacementClassName, ReplacementTemplate ) ) 244 | { 245 | TypeDecl->OuterType = nullptr; 246 | TypeDecl->OuterScope = ReplacementNamespace; 247 | TypeDecl->ClassName = ReplacementClassName; 248 | TypeDecl->TemplateArguments = ReplacementTemplate; 249 | } 250 | 251 | // Attach additional data in case we need to generate a predeclaration 252 | TypeDecl->UDTKind = ConvertUDTKind( TypeUDTKind ); 253 | return TypeDecl; 254 | } 255 | assert(!L"unsupported type symbol tag"); 256 | return nullptr; 257 | } 258 | 259 | bool CodeGeneration::GenerateDefaultValueForSimpleType(const CComPtr& InTypeSymbol, std::wstring& OutDefaultValue) 260 | { 261 | const DWORD SymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, symTag ); 262 | 263 | // We do not need to handle arrays because functions cannot return arrays. 264 | // Enumerations need to be casted to their type because they can be an enumeration class 265 | if ( SymbolTag == SymTagEnum ) 266 | { 267 | const SymbolNameInfo EnumerationNameInfo = SymbolNameInfo::FromSymbol( InTypeSymbol ); 268 | 269 | // Anonymous enumerations should never need to be casted to their type 270 | if ( EnumerationNameInfo.bIsAnonymousSymbol || EnumerationNameInfo.bIsUnnamedEnum ) 271 | { 272 | OutDefaultValue = L"0"; 273 | } 274 | else 275 | { 276 | // TODO: This will give ugly results for enumeration types nested into templated types, as they will not go through template expansion. 277 | OutDefaultValue = StringPrintf(L"static_cast<%s>(0)", EnumerationNameInfo.OriginalFullName.c_str()); 278 | } 279 | return true; 280 | } 281 | // Nullptr is the default value for pointer types 282 | if ( SymbolTag == SymTagPointerType ) 283 | { 284 | OutDefaultValue = L"nullptr"; 285 | return true; 286 | } 287 | if ( SymbolTag == SymTagBaseType ) 288 | { 289 | const DWORD BaseType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, baseType ); 290 | if ( BaseType == btBool ) 291 | { 292 | OutDefaultValue = L"false"; 293 | return true; 294 | } 295 | if ( BaseType == btFloat ) 296 | { 297 | OutDefaultValue = L"0.0f"; 298 | return true; 299 | } 300 | if ( BaseType == btChar || BaseType == btInt || BaseType == btLong || BaseType == btUInt || BaseType == btULong ) 301 | { 302 | OutDefaultValue = L"0"; 303 | return true; 304 | } 305 | if ( BaseType == btWChar || BaseType == btChar8 || BaseType == btChar16 || BaseType == btChar32 ) 306 | { 307 | OutDefaultValue = L"'\0'"; 308 | return true; 309 | } 310 | } 311 | return false; 312 | } 313 | 314 | void CodeGeneration::FormatFunctionType(const CComPtr& InFunctionSignatureType, ITypeResolutionProvider* TypeProvider, 315 | std::shared_ptr& OutReturnType, 316 | std::vector>>& OutArgumentNamesAndTypes, 317 | bool& OutIsVariadicArguments, 318 | std::shared_ptr* OutThisPointerType, const CComPtr& InOwnerFunction, 319 | bool* OutIsConstMemberFunction) 320 | { 321 | assert( GET_SYMBOL_ATTRIBUTE_CHECKED( InFunctionSignatureType, symTag ) == SymTagFunctionType ); 322 | 323 | if ( const CComPtr FunctionReturnType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InFunctionSignatureType, type ) ) 324 | { 325 | // Some template instantion function return values somehow have btNoType as their return value? Observed on std::_Tree::insert specialization. original function returns either iterator or void 326 | bool bHasInvalidReturnValue = false; 327 | if ( const DWORD SymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionReturnType, symTag ); SymbolTag == SymTagBaseType ) 328 | { 329 | if ( const DWORD BaseTypeTag = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionReturnType, baseType ); BaseTypeTag == btNoType ) 330 | { 331 | const std::wstring FunctionName = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InOwnerFunction, name ); 332 | std::wcerr << L"Return value with invalid type () was found when generating signature for function " << FunctionName << L". Substituing with void" << std::endl; 333 | bHasInvalidReturnValue = true; 334 | } 335 | } 336 | 337 | if ( bHasInvalidReturnValue ) 338 | { 339 | OutReturnType = std::make_shared(); 340 | } 341 | else 342 | { 343 | OutReturnType = FormatTypeName( FunctionReturnType, TypeProvider ); 344 | } 345 | } 346 | else 347 | { 348 | OutReturnType = std::make_shared(); 349 | } 350 | 351 | bool bHasObjectPointerArgument = false; 352 | if ( const CComPtr ObjectPointerType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InFunctionSignatureType, objectPointerType ) ) 353 | { 354 | // Object pointer type should always be a pointer to the UDT 355 | const DWORD ObjectPointerSymTag = GET_SYMBOL_ATTRIBUTE_CHECKED( ObjectPointerType, symTag ); 356 | assert( ObjectPointerSymTag == SymTagPointerType ); 357 | bHasObjectPointerArgument = true; 358 | 359 | const CComPtr CVModifiedObjectType = GET_SYMBOL_ATTRIBUTE_CHECKED( ObjectPointerType, type ); 360 | const CComPtr ThisObjectType = DiaUtils::RemoveCVModifiersFromType( CVModifiedObjectType ); 361 | if ( OutIsConstMemberFunction ) 362 | { 363 | *OutIsConstMemberFunction = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( CVModifiedObjectType, constType ); 364 | } 365 | if ( OutThisPointerType ) 366 | { 367 | *OutThisPointerType = FormatTypeName( ThisObjectType, TypeProvider ); 368 | } 369 | } 370 | 371 | bool bIsVariadicFunction = false; 372 | std::vector ArgumentTypeSymbolIds; 373 | for ( DiaChildSymbolIterator It( InFunctionSignatureType, SymTagFunctionArgType ); It; ++It ) 374 | { 375 | const CComPtr FunctionArgumentSymbol = *It; 376 | const CComPtr FunctionArgumentType = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionArgumentSymbol, type ); 377 | 378 | // Check against vararg function arguments. Variadic arguments (C style) will appear as argument with no type information 379 | if (const DWORD ArgumentTypeSymTag = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionArgumentType, symTag ); ArgumentTypeSymTag == SymTagBaseType ) 380 | { 381 | if (const DWORD BaseTypeKind = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionArgumentType, baseType ); BaseTypeKind == btNoType ) 382 | { 383 | assert( !bIsVariadicFunction ); 384 | bIsVariadicFunction = true; 385 | continue; 386 | } 387 | } 388 | 389 | const DWORD TypeSymbolId = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionArgumentType, symIndexId ); 390 | const std::shared_ptr ArgumentType = FormatTypeName( FunctionArgumentType, TypeProvider ); 391 | 392 | // Push the argument with no name, and record it's type so we can potentially match it up with a name using local variable lookup later 393 | OutArgumentNamesAndTypes.push_back({L"", ArgumentType}); 394 | ArgumentTypeSymbolIds.push_back(TypeSymbolId); 395 | } 396 | 397 | // Attempt to match a name with the argument by looking up at the data variables defined in the function 398 | std::wstring FunctionArgumentName; 399 | if ( InOwnerFunction ) 400 | { 401 | // Only local variables defined directly inside of the function should be it's parameters, any user-defined local variables would be children of the blocks and not the function directly 402 | int32_t CurrentArgumentIndex = 0; 403 | for ( DiaChildSymbolIterator LocalVariableIt( InOwnerFunction, SymTagData ); LocalVariableIt && CurrentArgumentIndex < ArgumentTypeSymbolIds.size(); ++LocalVariableIt ) 404 | { 405 | // Skip over the first local variable if this is an instance function because it will be the object pointer argument, which is hidden and does not appear as a real function argument 406 | if ( bHasObjectPointerArgument && LocalVariableIt.GetCurrentIndex() == 0 ) continue; 407 | 408 | const CComPtr FunctionDataSymbol = *LocalVariableIt; 409 | const CComPtr LocalVariableType = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionDataSymbol, type ); 410 | 411 | // All local variables should be either contained inside of the registers or relative to the RSP stack register 412 | const DWORD LocalVariableLocationType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionDataSymbol, locationType ); 413 | const DWORD LocalVariableTypeId = GET_SYMBOL_ATTRIBUTE_CHECKED( LocalVariableType, symIndexId ); 414 | 415 | // Skip constants and static/thread local variables 416 | if ( LocalVariableLocationType == LocIsStatic || LocalVariableLocationType == LocIsTLS || LocalVariableLocationType == LocIsConstant ) continue; 417 | 418 | // The rest should be register relative and enrigestered 419 | assert( LocalVariableLocationType == LocIsRegRel || LocalVariableLocationType == LocIsEnregistered ); 420 | 421 | // If the local variable type does not match the argument type, we failed to map the parameters to the names. But having incorrect names is better than having wrong names, 422 | // so we just skip it and continue instead of breaking out of the loop 423 | if ( ArgumentTypeSymbolIds[ CurrentArgumentIndex ] != LocalVariableTypeId ) continue; 424 | 425 | const std::wstring LocalVariableName = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionDataSymbol, name ); 426 | OutArgumentNamesAndTypes[ CurrentArgumentIndex ].first = LocalVariableName; 427 | CurrentArgumentIndex++; 428 | } 429 | } 430 | 431 | // Add variadic argument last if we found one. 432 | OutIsVariadicArguments = bIsVariadicFunction; 433 | } 434 | 435 | void CodeGeneration::PatchupInternalTypeReferences(TypeTemplateArgumentContainer& TemplateArguments, const CComPtr& InTemplateTypeContext) 436 | { 437 | // TODO: Implement. This would require pattern matching members inside of the template with anonymous/unnamed template arguments 438 | } 439 | 440 | std::shared_ptr CodeGeneration::FormatBasicTypeName(const DWORD InBasicType, const LONGLONG InBasicTypeSizeBytes ) 441 | { 442 | const std::shared_ptr ResultType = std::make_shared(); 443 | switch (InBasicType) 444 | { 445 | case btVoid: 446 | assert(InBasicTypeSizeBytes == 0 && L"void should be 0 in size"); 447 | return std::make_shared(); 448 | case btChar: 449 | // TODO: Why is there no btUChar? Is there a way to know whenever the original character was signed or unsigned? 450 | assert(InBasicTypeSizeBytes == 1 && L"btChar should always be 1 byte long on all platforms"); 451 | ResultType->BasicType = EBasicType::Char; 452 | return ResultType; 453 | case btWChar: 454 | assert((InBasicTypeSizeBytes == 2 || InBasicTypeSizeBytes == 4) && L"btWChar should be either 2 bytes on Windows or 4 bytes on other platforms, unusual size detected"); 455 | ResultType->BasicType = EBasicType::WideChar; 456 | return ResultType; 457 | case btChar8: 458 | ResultType->BasicType = EBasicType::Char8; 459 | return ResultType; 460 | case btChar16: 461 | ResultType->BasicType = EBasicType::Char16; 462 | return ResultType; 463 | case btChar32: 464 | ResultType->BasicType = EBasicType::Char32; 465 | return ResultType; 466 | case btBool: 467 | // We do not know the size of this type, since MS docs describe it as BOOL, and BOOL is a define for int. 468 | // But it looks like thsi documentation is outdated, and btBool matches the size of the bool type, size of which is implementation-defined. So we emit bool here. 469 | // Still, let's make sure it's 1 byte in size for now to catch any unusual cases that might need special treatment 470 | assert( InBasicTypeSizeBytes == 1 ); 471 | ResultType->BasicType = EBasicType::Bool; 472 | return ResultType; 473 | case btFloat: 474 | assert((InBasicTypeSizeBytes == 4 || InBasicTypeSizeBytes == 8) && L"btFloat has unusual size, should be either 4 bytes for float or 8 bytes for double"); 475 | ResultType->BasicType = InBasicTypeSizeBytes == 8 ? EBasicType::Double : EBasicType::Float; 476 | return ResultType; 477 | case btInt: 478 | case btLong: 479 | // Use fixed length integer types since we know the actual byte size of the type 480 | ResultType->BasicType = GetFixedLengthIntType( InBasicTypeSizeBytes ); 481 | ResultType->bIsUnsigned = false; 482 | return ResultType; 483 | case btUInt: 484 | case btULong: 485 | // Use fixed length integer types since we know the actual byte size of the type 486 | ResultType->BasicType = GetFixedLengthIntType( InBasicTypeSizeBytes ); 487 | ResultType->bIsUnsigned = true; 488 | return ResultType; 489 | case btCurrency: 490 | // CURRENCY type from win32 API 491 | return std::make_shared(L"CURRENCY"); 492 | case btDate: 493 | // DATE type from win32 API 494 | return std::make_shared(L"DATE"); 495 | case btVariant: 496 | // VARIANT type from win32 API 497 | return std::make_shared(L"VARIANT"); 498 | case btBSTR: 499 | // BSTR type from win32 API 500 | return std::make_shared(L"BSTR"); 501 | case btHresult: 502 | // HRESULT type from win32 API 503 | return std::make_shared(L"HRESULT"); 504 | case btBCD: 505 | // ??? No clue what type this is, it does not seem to exist in the standard, MSVC stdlib or win32 API even 506 | assert(!L"btBCD (Binary Coded Decimal) types are not supported"); 507 | return nullptr; 508 | case btComplex: 509 | // ??? No clue what type this is, it does not seem to exist in the standard, MSVC stdlib or win32 API even 510 | assert(!L"btComplex (complex float/double number type) is not a valid type in C++"); 511 | return nullptr; 512 | case btBit: 513 | assert(!L"btBit type is not supported, there is no valid C++ type it can be reliably mapped to"); 514 | return nullptr; 515 | default: 516 | assert(!L"unknown basic type"); 517 | return nullptr; 518 | } 519 | } 520 | -------------------------------------------------------------------------------- /src/HeaderGeneratorConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "HeaderGeneratorConfig.h" 2 | #include 3 | #include 4 | 5 | bool HeaderGeneratorConfigManager::LoadConfigFrom(const std::filesystem::path& InConfigFilePath) 6 | { 7 | const std::string ConfigBareName = InConfigFilePath.filename().replace_extension().generic_string(); 8 | if ( LoadedConfigs.contains( ConfigBareName ) ) 9 | { 10 | return true; 11 | } 12 | LoadedConfigs.insert( ConfigBareName ); 13 | 14 | std::ifstream ConfigInputStream( InConfigFilePath ); 15 | if ( !ConfigInputStream.good() ) 16 | { 17 | std::cerr << "Failed to load configuration file from " << InConfigFilePath.generic_string() << " because file could not be open for read." << std::endl; 18 | return false; 19 | } 20 | HeaderGeneratorConfig LoadedConfig; 21 | try 22 | { 23 | const nlohmann::json ParsedJson = nlohmann::json::parse( ConfigInputStream ); 24 | ParsedJson.get_to( LoadedConfig ); 25 | } 26 | catch (const std::exception& ex) 27 | { 28 | std::cerr << "Failed to parse configuration file at " << InConfigFilePath.generic_string() << " as valid JSON: " << ex.what() << std::endl; 29 | return false; 30 | } 31 | 32 | // Attempt to load the dependencies first. We assume dependencies to be located in the same folder as this configuration file, and has the same extension 33 | for ( const std::string& DependencyConfigName : LoadedConfig.DependencyConfigs ) 34 | { 35 | const std::filesystem::path DependencyConfigPath = InConfigFilePath.parent_path().operator/=(DependencyConfigName).replace_extension( InConfigFilePath.extension() ); 36 | if ( !LoadConfigFrom( DependencyConfigPath ) ) 37 | { 38 | std::cerr << "Refusing to load configuration file at " << InConfigFilePath.generic_string() << " because it's dependency config file " << DependencyConfigPath.generic_string() << " could not be loaded." << std::endl; 39 | return false; 40 | } 41 | } 42 | 43 | // Generate filesystem paths for header overrides in this config 44 | for ( ManualHeaderDefinition& HeaderDefinition : LoadedConfig.HeaderOverrides ) 45 | { 46 | HeaderDefinition.HeaderFilesystemPath = InConfigFilePath.parent_path() / HeaderDefinition.HeaderPath; 47 | } 48 | 49 | // Merge the current config file with the global one, overwriting the dependencies or appending to them 50 | MergedConfig.Merge(LoadedConfig); 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /src/TypeDependencyCollector.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeDependencyCollector.h" 2 | #include 3 | #include 4 | #include 5 | 6 | TypeDependency::TypeDependency(const CComPtr& InDependencySymbol) : DependencySymbol(InDependencySymbol) 7 | { 8 | // Make sure to always grab the unmodified copy of the symbol when it is available, to avoid duplicates with different CV modifiers 9 | if ( const CComPtr UnmodifiedTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( DependencySymbol, unmodifiedType ) ) 10 | { 11 | DependencySymbol = UnmodifiedTypeSymbol; 12 | } 13 | SymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( DependencySymbol, symTag ); 14 | SymbolId = GET_SYMBOL_ATTRIBUTE_CHECKED( DependencySymbol, symIndexId ); 15 | } 16 | 17 | TypeDependency TypeDependency::UserDefinedType(const CComPtr& UserDefinedType) 18 | { 19 | assert( GET_SYMBOL_ATTRIBUTE_CHECKED( UserDefinedType, symTag ) == SymTagUDT ); 20 | return TypeDependency{UserDefinedType}; 21 | } 22 | 23 | TypeDependency TypeDependency::Enum(const CComPtr& Enum) 24 | { 25 | assert( GET_SYMBOL_ATTRIBUTE_CHECKED( Enum, symTag ) == SymTagEnum ); 26 | return TypeDependency{Enum}; 27 | } 28 | 29 | CComPtr ITypeResolutionProvider::FindTopLevelType(const CComPtr& InNestedType) 30 | { 31 | CComPtr CurrentSymbol = InNestedType; 32 | while ( const CComPtr NewParentSymbol = FindNestedTypeParent( CurrentSymbol ) ) 33 | { 34 | CurrentSymbol = NewParentSymbol; 35 | } 36 | return CurrentSymbol; 37 | } 38 | 39 | TypeDependencyCollectorBase::TypeDependencyCollectorBase(ITypeResolutionProvider* InTypeResolver) : TypeResolver( InTypeResolver ) 40 | { 41 | } 42 | 43 | void TypeDependencyCollectorBase::CollectDependenciesForType( const CComPtr& InTypeSymbol, const bool bOnlyWantsPredeclaration ) 44 | { 45 | const DWORD SymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, symTag ); 46 | if ( SymbolTag == SymTagArrayType ) 47 | { 48 | if ( const CComPtr ElementType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ) ) 49 | { 50 | // Also always want a definition for the array index type, which is normally one of the base types 51 | CollectDependenciesForType( ElementType, false ); 52 | } 53 | if ( const CComPtr IndexType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, arrayIndexType ) ) 54 | { 55 | // We always want full type definitions for array element types to know how much memory to allocate for them 56 | CollectDependenciesForType( IndexType, false ); 57 | } 58 | } 59 | else if ( SymbolTag == SymTagBaseType ) 60 | { 61 | // Base types cannot be user defined, no dependencies for them other than to declare, but we need to include for them 62 | bNeedsCStdint = true; 63 | } 64 | else if ( SymbolTag == SymTagCustomType ) 65 | { 66 | // We do not really support custom types, but might handle them for the sake of completeness 67 | DWORD NumCustomTypeDependencies = 0; 68 | assert( InTypeSymbol->get_types( 0, &NumCustomTypeDependencies, nullptr ) == S_OK ); 69 | 70 | // This code only works if CComPtr internally is just a raw COM pointer 71 | std::vector> CustomTypes; 72 | static_assert(sizeof(CComPtr) == sizeof(IDiaSymbol*)); 73 | 74 | CustomTypes.resize( NumCustomTypeDependencies ); 75 | assert( InTypeSymbol->get_types( NumCustomTypeDependencies, &NumCustomTypeDependencies, (IDiaSymbol**) CustomTypes.data() ) == S_OK ); 76 | 77 | for ( const CComPtr& ReferencedType : CustomTypes ) 78 | { 79 | // We always want definitions for custom types, since we have no idea how they use underlying types 80 | CollectDependenciesForType( ReferencedType, false ); 81 | } 82 | } 83 | else if ( SymbolTag == SymTagEnum ) 84 | { 85 | // We never want definitions for enum types, just the pre-declaration 86 | AddDependency( TypeDependency::Enum( InTypeSymbol ), true ); 87 | 88 | // We need a definition for the underlying enum type, even for a pre-declaration 89 | // TODO this is technically an implementation detail and maybe should be handled by AddDependency internally instead 90 | if ( const CComPtr UnderlyingType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, type ) ) 91 | { 92 | CollectDependenciesForType( UnderlyingType, false ); 93 | } 94 | } 95 | else if ( SymbolTag == SymTagPointerType ) 96 | { 97 | // Pointers do not need the full definition of their pointee type, just the pre-declaration 98 | if ( const CComPtr PointeeType = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ) ) 99 | { 100 | // We always want full type definitions for array element types to know how much memory to allocate for them 101 | CollectDependenciesForType( PointeeType, true ); 102 | } 103 | } 104 | else if ( SymbolTag == SymTagTypedef ) 105 | { 106 | const std::wstring TypedefName = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, name ); 107 | 108 | // For typedefs, we want a full definition of the symbol it is defining, and also to keep track of it 109 | // Duplicate typedefs are allowed in C++ as long as they point to the same underlying type, so we want to emit typedefs of all dependencies for each header we generate 110 | if ( const CComPtr TypedefDefinitionSymbol = GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, type ) ) 111 | { 112 | HandleTypedefDependency(TypedefName, TypedefDefinitionSymbol); 113 | CollectDependenciesForType( TypedefDefinitionSymbol, false ); 114 | } 115 | } 116 | else if ( SymbolTag == SymTagUDT ) 117 | { 118 | // Always want the original type if possible, since we can end up here with a CV modified type instead 119 | CComPtr SourceUDTType = DiaUtils::RemoveCVModifiersFromType( InTypeSymbol ); 120 | 121 | // Whenever we want a declaration or a definition for UDT depends on whenever the type that is enveloping us (for example, a pointer) wants the full definition or not 122 | AddDependency( TypeDependency::UserDefinedType( InTypeSymbol ), bOnlyWantsPredeclaration ); 123 | } 124 | else if ( SymbolTag == SymTagFunctionType ) 125 | { 126 | // We need a full definition for the return type symbol, since it might need to be copied to call the function 127 | if ( const CComPtr ReturnTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, type ) ) 128 | { 129 | CollectDependenciesForType( ReturnTypeSymbol, false ); 130 | } 131 | // Object pointer type should always be a pointer when present, so whenever we specify predeclaration here or not does not matter 132 | if ( const CComPtr ObjectPointerType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, type ) ) 133 | { 134 | CollectDependenciesForType( ObjectPointerType, false ); 135 | } 136 | 137 | // Iterate function argument types and handle each one of them 138 | for ( DiaChildSymbolIterator It( InTypeSymbol, SymTagFunctionArgType ); It; ++It ) 139 | { 140 | const CComPtr FunctionArgumentSymbol = *It; 141 | if ( const CComPtr FunctionArgumentType = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionArgumentSymbol, type ) ) 142 | { 143 | CollectDependenciesForType( FunctionArgumentType, false ); 144 | } 145 | } 146 | } 147 | else 148 | { 149 | // We should have handled all possible type symbols above. Passing anything else to this function is a mistake on the caller 150 | std::wcerr << L"Unhandled Symbol Tag: " << SymbolTag << L" for Type Symbol " << GET_SYMBOL_ATTRIBUTE_CHECKED( InTypeSymbol, name ) << std::endl; 151 | assert(false); 152 | } 153 | 154 | // If this type is contained inside of another type, we need to add the dependency to that type too, with definition visibility, since nested types cannot be predeclared 155 | if ( const CComPtr ClassParentSymbol = TypeResolver->FindNestedTypeParent( InTypeSymbol ) ) 156 | { 157 | CollectDependenciesForType( ClassParentSymbol, false ); 158 | } 159 | } 160 | 161 | void TypeDependencyCollectorBase::CollectDependenciesForUDTDefinition( const CComPtr& InUserDefinedType, const bool bInternalMarkAsPredeclaration ) 162 | { 163 | assert( GET_SYMBOL_ATTRIBUTE_CHECKED( InUserDefinedType, symTag ) == SymTagUDT ); 164 | 165 | // Gather base class dependencies for this type 166 | for ( DiaChildSymbolIterator It( InUserDefinedType, SymTagBaseClass ); It; ++It ) 167 | { 168 | const CComPtr BaseClassSymbol = *It; 169 | const CComPtr BaseClassType = GET_SYMBOL_ATTRIBUTE_CHECKED( BaseClassSymbol, type ); 170 | 171 | CollectDependenciesForType( BaseClassType, bInternalMarkAsPredeclaration ); 172 | } 173 | 174 | // Gather dependencies for the functions 175 | for ( DiaChildSymbolIterator It( InUserDefinedType, SymTagFunction ); It; ++It ) 176 | { 177 | const CComPtr FunctionSymbol = *It; 178 | const CComPtr FunctionSignatureType = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionSymbol, type ); 179 | 180 | // Do not collect dependencies for non-virtual functions with no valid location. This introduces unneeded dependencies between headers without any real benefit to the end user. 181 | const BOOL bIsVirtualFunction = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionSymbol, virtual ); 182 | const DWORD FunctionLocationType = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionSymbol, locationType ); 183 | if ( !bIsVirtualFunction && FunctionLocationType == LocIsNull ) continue; 184 | 185 | CollectDependenciesForType( FunctionSignatureType, bInternalMarkAsPredeclaration ); 186 | } 187 | 188 | // Gather dependencies for the data members 189 | for ( DiaChildSymbolIterator It( InUserDefinedType, SymTagData ); It; ++It ) 190 | { 191 | const CComPtr DataSymbol = *It; 192 | const CComPtr VariableDataType = GET_SYMBOL_ATTRIBUTE_CHECKED( DataSymbol, type ); 193 | 194 | CollectDependenciesForType( VariableDataType, bInternalMarkAsPredeclaration ); 195 | } 196 | } 197 | 198 | void TypeDependencyCollectorBase::CollectDependenciesForTemplateInstantiation( const SymbolNameInfo& TemplateName, const TypeTemplateArgumentContainer& TemplateArguments ) 199 | { 200 | std::vector> DeclarationsToCheck; 201 | for ( const TypeTemplateArgument& Argument : TemplateArguments.Arguments ) 202 | { 203 | if ( Argument.Type == ETemplateArgumentType::TypeDeclaration ) 204 | { 205 | DeclarationsToCheck.push_back(Argument.TypeConstant); 206 | } 207 | if ( Argument.Type == ETemplateArgumentType::TypeMemberReference ) 208 | { 209 | DeclarationsToCheck.push_back(Argument.TypeMemberReference.OwnerType); 210 | } 211 | } 212 | 213 | std::vector TopLevelEnums; 214 | std::vector TopLevelTypes; 215 | 216 | for ( int32_t i = 0; i < DeclarationsToCheck.size(); i++ ) 217 | { 218 | const std::shared_ptr Decl = DeclarationsToCheck[i]; 219 | if ( Decl->GetId() == ETypeDeclarationId::PointerType ) 220 | { 221 | const PointerTypeDeclaration* PointerType = static_cast( Decl.get() ); 222 | DeclarationsToCheck.push_back( PointerType->PointeeType ); 223 | if ( PointerType->OwnerType ) 224 | { 225 | DeclarationsToCheck.push_back( PointerType->OwnerType ); 226 | } 227 | } 228 | else if ( Decl->GetId() == ETypeDeclarationId::ArrayType ) 229 | { 230 | DeclarationsToCheck.push_back( static_cast( Decl.get() )->ElementType ); 231 | } 232 | else if ( Decl->GetId() == ETypeDeclarationId::FunctionType ) 233 | { 234 | const FunctionTypeDeclaration* FunctionType = static_cast( Decl.get() ); 235 | DeclarationsToCheck.push_back( FunctionType->ReturnType ); 236 | if ( FunctionType->OwnerType ) 237 | { 238 | DeclarationsToCheck.push_back( FunctionType->OwnerType ); 239 | } 240 | for (const auto& ArgumentType: FunctionType->Arguments | std::views::values) 241 | { 242 | DeclarationsToCheck.push_back(ArgumentType); 243 | } 244 | } 245 | else if ( Decl->GetId() == ETypeDeclarationId::Enum ) 246 | { 247 | const EnumTypeDeclaration* EnumType = static_cast( Decl.get() ); 248 | if ( EnumType->OuterType == nullptr ) 249 | { 250 | TopLevelEnums.push_back(EnumType); 251 | } 252 | else 253 | { 254 | DeclarationsToCheck.push_back(EnumType->OuterType); 255 | } 256 | } 257 | else if ( Decl->GetId() == ETypeDeclarationId::UDT ) 258 | { 259 | const UDTTypeDeclaration* UDTType = static_cast( Decl.get() ); 260 | if ( UDTType->OuterType == nullptr ) 261 | { 262 | TopLevelTypes.push_back(UDTType); 263 | } 264 | else 265 | { 266 | DeclarationsToCheck.push_back(UDTType->OuterType); 267 | } 268 | } 269 | } 270 | 271 | // Collect dependencies on the top level enums first, since they cannot be templated 272 | for ( const EnumTypeDeclaration* EnumDeclaration : TopLevelEnums ) 273 | { 274 | SymbolNameInfo SymbolNameInfo; 275 | SymbolNameInfo.SymbolScope = EnumDeclaration->OuterScope; 276 | SymbolNameInfo.LocalName = EnumDeclaration->EnumName; 277 | 278 | // Resolve the underlying enum type from the the symbol storage. We should ALWAYS have data for all types used as template instantiation arguments. 279 | // At least, cases where the data is not available should be the vast minority and need to be carefully investigated. 280 | const CComPtr EnumerationSymbol = TypeResolver->ResolveEnumByFullName( SymbolNameInfo.ToString( SymbolNameInfo::IncludeNamespace ) ); 281 | assert( EnumerationSymbol && L"Failed to resolve enumeration type used as a template instantiation argument" ); 282 | 283 | // We only want the pre-declaration for the template instantiation enum parameters, full definition is never necessary under any conditions 284 | if ( EnumerationSymbol ) 285 | { 286 | AddDependency( TypeDependency::Enum( EnumerationSymbol ), true ); 287 | } 288 | } 289 | 290 | // Some templates need full typename arguments, and others only need pre-declarations 291 | const bool bTemplateNeedsFullType = TypeResolver->DoTemplateArgumentsNeedFullDefinition( TemplateName ); 292 | 293 | // Collect dependencies on the top level UDTs now. These can very much be templated and as such we need to use a slower lookup in case the template data is used 294 | for ( const UDTTypeDeclaration* TypeDeclaration : TopLevelTypes ) 295 | { 296 | SymbolNameInfo SymbolNameInfo; 297 | SymbolNameInfo.SymbolScope = TypeDeclaration->OuterScope; 298 | SymbolNameInfo.LocalName = TypeDeclaration->ClassName; 299 | 300 | // If there are no template arguments, we can do fast resolution based only on the full symbol name 301 | if ( TypeDeclaration->TemplateArguments.Arguments.empty() ) 302 | { 303 | const CComPtr UDTSymbol = TypeResolver->ResolveUDTByFullName( SymbolNameInfo.ToString( SymbolNameInfo::IncludeNamespace ) ); 304 | 305 | // We only need a pre-declaration for this type since it has no template arguments 306 | if ( UDTSymbol ) 307 | { 308 | AddDependency( TypeDependency::UserDefinedType( UDTSymbol ), !bTemplateNeedsFullType ); 309 | } 310 | else 311 | { 312 | std::wcerr << L"Failed to resolve UDT type used as a template instantiation argument: " << SymbolNameInfo.ToString() << L". This will result in a compilation error unless manual definition is provided." << std::endl; 313 | } 314 | } 315 | // If we have template arguments, we need to either resolve the template instantiation symbol (which is extremely slow for templates with huge amount of instantiations) 316 | // Or attempt to resolve the dependency without having to resolve the name into the symbol 317 | else 318 | { 319 | // Always collect dependencies for template instantiation regardless of whenever we do fast or slow template processing 320 | CollectDependenciesForTemplateInstantiation( SymbolNameInfo, TypeDeclaration->TemplateArguments ); 321 | 322 | // Check if this is a symbol that we consider special or external. If it is, lookup the external file 323 | if ( HandleTemplateInstantiationDependency( SymbolNameInfo, TypeDeclaration->TemplateArguments ) ) 324 | { 325 | continue; 326 | } 327 | 328 | // Use slow path using the iteration across all template instantiations in the database in case this is our own type. 329 | if ( const CComPtr InstantiationSymbol = TypeResolver->ResolveTemplateInstantiation( SymbolNameInfo, TypeDeclaration->TemplateArguments ) ) 330 | { 331 | AddDependency( TypeDependency::UserDefinedType( InstantiationSymbol ), !bTemplateNeedsFullType ); 332 | } 333 | else 334 | { 335 | FormattedTextWriter TemplateArgumentsText; 336 | TypeDeclaration->TemplateArguments.Print(TemplateArgumentsText, TypeFormattingRules()); 337 | 338 | // This a pretty serious error that will result in a compilation error, but it is normal for some template types in some cases (e.g. non-type arguments only templates) to be absent 339 | std::wcerr << L"Failed to resolve template instantiation used as a template argument for non-external type " << SymbolNameInfo.ToString() << L". Template Arguments: " << TemplateArgumentsText.ToString() << std::endl; 340 | } 341 | } 342 | } 343 | } 344 | 345 | void TypeDependencyCollectorBase::AddDependency(const TypeDependency& TypeDependency, bool bIsPredeclaration) 346 | { 347 | // Nested types cannot be pre-declared. 348 | if ( TypeResolver->FindNestedTypeParent( TypeDependency.GetSymbol() ) ) 349 | { 350 | bIsPredeclaration = false; 351 | } 352 | AddDependencyInternal( TypeDependency, bIsPredeclaration ); 353 | } 354 | 355 | TypeDependencyCollector::TypeDependencyCollector( ITypeResolutionProvider* InTypeResolver, const bool InRecurseIntoUserDefinedTypes) : TypeDependencyCollectorBase( InTypeResolver ), bRecurseIntoUserDefinedTypes( InRecurseIntoUserDefinedTypes ) 356 | { 357 | } 358 | 359 | std::unordered_set TypeDependencyCollector::GetAllDependencies() const 360 | { 361 | std::unordered_set AllDependencies; 362 | AllDependencies.insert(DeclarationDependencies.begin(), DeclarationDependencies.end()); 363 | AllDependencies.insert(DefinitionDependencies.begin(), DefinitionDependencies.end()); 364 | return AllDependencies; 365 | } 366 | 367 | void TypeDependencyCollector::HandleTypedefDependency(const std::wstring& TypedefName, const CComPtr& TypedefType) 368 | { 369 | TypedefDependencies.insert({TypedefName, TypedefType}); 370 | } 371 | 372 | void TypeDependencyCollector::AddDependencyInternal(const TypeDependency& TypeDependency, bool bIsPredeclaration) 373 | { 374 | bool bIsNewDependency = false; 375 | if ( bIsPredeclaration ) 376 | { 377 | // Only add pre-declaration dependency if we do not already have a definition dependency for the same type 378 | if ( !DefinitionDependencies.contains(TypeDependency) ) 379 | { 380 | bIsNewDependency = DeclarationDependencies.insert(TypeDependency).second; 381 | } 382 | } 383 | else 384 | { 385 | // Erase declaration dependency if we are adding a definition for the same type 386 | if ( DefinitionDependencies.insert(TypeDependency).second ) 387 | { 388 | DeclarationDependencies.erase(TypeDependency); 389 | bIsNewDependency = true; 390 | } 391 | } 392 | 393 | // If we want to recurse into the user defined types referenced, collect all of the dependencies needed for the definition of the type 394 | if ( bIsNewDependency && bRecurseIntoUserDefinedTypes && TypeDependency.IsUserDefinedType() ) 395 | { 396 | const DWORD UniqueSymbolId = GET_SYMBOL_ATTRIBUTE_CHECKED( TypeDependency.GetSymbol(), symIndexId ); 397 | 398 | if ( !UserDefinedTypesHandled.contains( UniqueSymbolId ) ) 399 | { 400 | UserDefinedTypesHandled.insert( UniqueSymbolId ); 401 | CollectDependenciesForUDTDefinition( TypeDependency.GetSymbol(), false ); 402 | } 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /src/Utils/DiaUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils/DiaUtils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | HRESULT CoCreateDiaDataSource(const HMODULE DiaDllHandle, CComPtr& OutDataDataSource) 8 | { 9 | const auto DllGetClassObject = reinterpret_cast( GetProcAddress( DiaDllHandle, "DllGetClassObject" ) ); 10 | if (!DllGetClassObject) 11 | { 12 | return HRESULT_FROM_WIN32(GetLastError()); 13 | } 14 | CComPtr pClassFactory; 15 | if ( const HRESULT Result = DllGetClassObject(CLSID_DiaSource, IID_IClassFactory, reinterpret_cast(&pClassFactory)); FAILED(Result) ) 16 | { 17 | return Result; 18 | } 19 | if ( const HRESULT Result = pClassFactory->CreateInstance(nullptr, IID_IDiaDataSource, (void **) &OutDataDataSource); FAILED(Result) ) 20 | { 21 | return Result; 22 | } 23 | return S_OK; 24 | } 25 | 26 | DiaChildSymbolIterator::DiaChildSymbolIterator( const CComPtr& InParentSymbol, enum SymTagEnum InSymbolType, const WCHAR* InName, DWORD CompareFlags ) 27 | { 28 | const HRESULT EnumeratorOpenResult = InParentSymbol->findChildren(InSymbolType, InName, CompareFlags, &UnderlyingEnumerator); 29 | assert( SUCCEEDED( EnumeratorOpenResult ) ); 30 | assert( SUCCEEDED( UnderlyingEnumerator->get_Count( &ItemCount ) ) ); 31 | } 32 | 33 | DiaChildSymbolIterator::DiaChildSymbolIterator(const CComPtr& InParentSymbol, enum SymTagEnum InSymbolType, const WCHAR* InName, DWORD CompareFlags, DWORD SymbolRVA) 34 | { 35 | const HRESULT EnumeratorOpenResult = InParentSymbol->findChildrenExByRVA(InSymbolType, InName, CompareFlags, SymbolRVA, &UnderlyingEnumerator); 36 | assert( SUCCEEDED( EnumeratorOpenResult ) ); 37 | assert( SUCCEEDED( UnderlyingEnumerator->get_Count( &ItemCount ) ) ); 38 | } 39 | 40 | DiaChildSymbolIterator::operator bool() const 41 | { 42 | return CurrentIndex < ItemCount; 43 | } 44 | 45 | CComPtr DiaChildSymbolIterator::operator*() const 46 | { 47 | CComPtr NextSymbol; 48 | assert( SUCCEEDED( UnderlyingEnumerator->Item( CurrentIndex, &NextSymbol ) ) ); 49 | return NextSymbol; 50 | } 51 | 52 | void DiaChildSymbolIterator::operator++() 53 | { 54 | CurrentIndex++; 55 | } 56 | 57 | static size_t FindTemplateArgumentsStartIndex( const std::wstring& InFullName, const size_t TemplateEndIndex ) 58 | { 59 | assert( InFullName[ TemplateEndIndex ] == L'>' ); 60 | 61 | int32_t CurrentIndex = TemplateEndIndex - 1; 62 | int32_t CurrentNestingLevel = 1; 63 | 64 | while ( CurrentIndex >= 0 && CurrentNestingLevel > 0 ) 65 | { 66 | if ( InFullName[ CurrentIndex ] == L'<' ) 67 | { 68 | CurrentNestingLevel--; 69 | } 70 | else if ( InFullName[ CurrentIndex ] == L'>' ) 71 | { 72 | CurrentNestingLevel++; 73 | } 74 | CurrentIndex--; 75 | } 76 | // We will overshoot by 1 character to the left 77 | return CurrentNestingLevel == 0 ? ( CurrentIndex + 1 ) : std::wstring::npos; 78 | } 79 | 80 | SymbolNameInfo SymbolNameInfo::FromSymbol(const CComPtr& InSymbol) 81 | { 82 | const std::wstring SymbolName = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InSymbol, name ); 83 | return FromSymbolName( SymbolName ); 84 | } 85 | 86 | SymbolNameInfo SymbolNameInfo::FromSymbolName(std::wstring SymbolName) 87 | { 88 | SymbolNameInfo ResultNameInfo; 89 | ResultNameInfo.OriginalFullName = SymbolName; 90 | 91 | // Symbol with an empty name. Skip any kind of name parsing 92 | if ( SymbolName.empty() ) 93 | { 94 | return ResultNameInfo; 95 | } 96 | 97 | // Make sure not to cut off namespaces inside of the template arguments. So look for the first template argument start 98 | // Special case to not accidentally treat operator-> as template specialization 99 | if ( SymbolName[ SymbolName.size() - 1 ] == L'>' && 100 | !SymbolName.ends_with(L"operator->") && !SymbolName.ends_with(L"operator>") && !SymbolName.ends_with(L"operator>>") && 101 | !SymbolName.ends_with(L"operator>=") && !SymbolName.ends_with(L"operator<=>") ) 102 | { 103 | const size_t ArgumentStartIndex = FindTemplateArgumentsStartIndex( SymbolName, SymbolName.size() - 1 ); 104 | assert( ArgumentStartIndex != std::wstring::npos && L"Invalid nesting depth inside of the template specialization name" ); 105 | 106 | const std::wstring TemplateArgumentsOrAnonymousTypeName = SymbolName.substr( ArgumentStartIndex + 1, SymbolName.size() - (ArgumentStartIndex + 1) - 1 ); 107 | 108 | // Check if this is a nameless anonymous type that does not indicate which field it comes from. These do not have much information on them. 109 | if ( TemplateArgumentsOrAnonymousTypeName == L"anonymous-tag" || TemplateArgumentsOrAnonymousTypeName == L"unnamed-tag" ) 110 | { 111 | ResultNameInfo.bIsAnonymousSymbol = true; 112 | } 113 | // Check for unnamed types with variable name in them 114 | else if ( TemplateArgumentsOrAnonymousTypeName.starts_with(L"unnamed-type-") ) 115 | { 116 | ResultNameInfo.bIsAnonymousSymbol = true; 117 | ResultNameInfo.bIsUnnamedType = true; 118 | ResultNameInfo.AnonymousSymbolOriginVariableName = TemplateArgumentsOrAnonymousTypeName.substr( 13 ); 119 | } 120 | // Check for unnamed enums with variable name in them 121 | else if ( TemplateArgumentsOrAnonymousTypeName.starts_with(L"unnamed-enum-") ) 122 | { 123 | ResultNameInfo.bIsAnonymousSymbol = true; 124 | ResultNameInfo.bIsUnnamedEnum = true; 125 | ResultNameInfo.AnonymousSymbolOriginVariableName = TemplateArgumentsOrAnonymousTypeName.substr( 13 ); 126 | } 127 | // Check if this is a lambda 128 | else if ( TemplateArgumentsOrAnonymousTypeName.starts_with(L"lambda_") ) 129 | { 130 | ResultNameInfo.bIsLambdaSymbol = true; 131 | } 132 | // Otherwise, it is a normal template instantiation 133 | else 134 | { 135 | ResultNameInfo.bIsTemplateInstantiation = true; 136 | ResultNameInfo.TemplateArguments = TemplateArgumentsOrAnonymousTypeName; 137 | SymbolName = SymbolName.substr( 0, ArgumentStartIndex ); 138 | } 139 | } 140 | 141 | // Look for the namespace now 142 | if ( const size_t LastNamespaceIndex = SymbolName.rfind(L"::"); LastNamespaceIndex != std::wstring::npos ) 143 | { 144 | ResultNameInfo.SymbolScope = SymbolName.substr( 0, LastNamespaceIndex ); 145 | SymbolName = SymbolName.substr( LastNamespaceIndex + 2 ); 146 | } 147 | 148 | // The remaining bit is a local symbol name 149 | ResultNameInfo.LocalName = SymbolName; 150 | return ResultNameInfo; 151 | } 152 | 153 | std::wstring SymbolNameInfo::ToString( const int32_t InOptions ) const 154 | { 155 | std::wostringstream ResultStr; 156 | 157 | if ( !SymbolScope.empty() && ( InOptions & IncludeNamespace ) != 0 ) 158 | { 159 | ResultStr << SymbolScope << L"::"; 160 | } 161 | ResultStr << LocalName; 162 | 163 | if ( bIsTemplateInstantiation && ( InOptions & IncludeTemplateArguments ) != 0 ) 164 | { 165 | ResultStr << L"<" << TemplateArguments << L">"; 166 | } 167 | return ResultStr.str(); 168 | } 169 | 170 | CComPtr DiaUtils::RemoveCVModifiersFromType(const CComPtr& InTypeSymbol) 171 | { 172 | // Retrieve the unmodified type first 173 | if ( const CComPtr UnmodifiedTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InTypeSymbol, unmodifiedType ) ) 174 | { 175 | return UnmodifiedTypeSymbol; 176 | } 177 | return InTypeSymbol; 178 | } 179 | 180 | bool DiaUtils::IsFunctionImplementationTheSame(const CComPtr& FunctionA, const CComPtr& FunctionB) 181 | { 182 | const DWORD LocationTypeA = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionA, locationType ); 183 | const DWORD LocationTypeB = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionB, locationType ); 184 | 185 | if ( LocationTypeA != LocationTypeB ) return false; 186 | assert( LocationTypeA == LocIsNull || LocationTypeA == LocIsStatic ); 187 | 188 | if ( LocationTypeA == LocIsStatic ) 189 | { 190 | const DWORD RelativeVirtualAddressA = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionA, relativeVirtualAddress ); 191 | const DWORD RelativeVirtualAddressB = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionB, relativeVirtualAddress ); 192 | 193 | // Implementation is the same if both functions point to the same virtual address 194 | return RelativeVirtualAddressA == RelativeVirtualAddressB; 195 | } 196 | if ( LocationTypeA == LocIsNull ) 197 | { 198 | return true; 199 | } 200 | return false; 201 | } 202 | 203 | std::wstring DiaUtils::GetSymbolUndecoratedName(const CComPtr& InSymbol, DWORD UndecorateOptions) 204 | { 205 | BSTR UndecoratedName = nullptr; 206 | const HRESULT RetrievalResult = InSymbol->get_undecoratedNameEx( UndecorateOptions, &UndecoratedName ); 207 | assert( SUCCEEDED( RetrievalResult ) ); 208 | return std::wstring( UndecoratedName ); 209 | } 210 | 211 | CComPtr DiaUtils::FindParentVirtualFunction(const CComPtr& InTypeSymbol, const std::wstring& FunctionName, const CComPtr& InFunctionSignature) 212 | { 213 | std::unordered_set VisitedBaseClassTypes; 214 | std::vector> BaseClassTypesExploded; 215 | 216 | // Recursively explore all UDTs in this class hierarchy 217 | BaseClassTypesExploded.push_back( InTypeSymbol ); 218 | for ( int32_t CurrentTypeIndex = 0; CurrentTypeIndex < BaseClassTypesExploded.size(); CurrentTypeIndex++ ) 219 | { 220 | const CComPtr BaseClassTypeSymbol = BaseClassTypesExploded[ CurrentTypeIndex ]; 221 | 222 | for ( DiaChildSymbolIterator It( BaseClassTypeSymbol, SymTagBaseClass ); It; ++It ) 223 | { 224 | const CComPtr BaseClassSymbol = *It; 225 | CComPtr ParentClassTypeSymbol = GET_SYMBOL_ATTRIBUTE_CHECKED( BaseClassSymbol, type ); 226 | 227 | if ( const CComPtr UnmodifiedTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( ParentClassTypeSymbol, unmodifiedType ) ) 228 | { 229 | ParentClassTypeSymbol = UnmodifiedTypeSymbol; 230 | } 231 | if (const DWORD ParentClassSymbolId = GET_SYMBOL_ATTRIBUTE_CHECKED( ParentClassTypeSymbol, symIndexId ); !VisitedBaseClassTypes.contains( ParentClassSymbolId ) ) 232 | { 233 | VisitedBaseClassTypes.insert( ParentClassSymbolId ); 234 | BaseClassTypesExploded.push_back( ParentClassTypeSymbol ); 235 | } 236 | } 237 | } 238 | 239 | // Attempt to find the function with the same name and matching signature in each of the classes 240 | for ( const CComPtr& ParentClassSymbol : BaseClassTypesExploded ) 241 | { 242 | // Skip the original type 243 | if ( ParentClassSymbol == InTypeSymbol ) continue; 244 | 245 | for ( DiaChildSymbolIterator It( ParentClassSymbol, SymTagFunction, FunctionName.c_str() ); It; ++It ) 246 | { 247 | const CComPtr FunctionSymbol = *It; 248 | const BOOL bIsVirtualFunction = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( FunctionSymbol, virtual ); 249 | const CComPtr FunctionTypeSymbol = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionSymbol, type ); 250 | 251 | // Skip non-virtual functions, they might have the same name and even the same signature 252 | if ( !bIsVirtualFunction ) continue; 253 | 254 | // Skip special functions that do not have a valid type 255 | // ReSharper disable once CppTooWideScopeInitStatement 256 | const DWORD FunctionTypeSymbolTag = GET_SYMBOL_ATTRIBUTE_CHECKED( FunctionTypeSymbol, symTag ); 257 | if ( FunctionTypeSymbolTag != SymTagFunctionType ) continue; 258 | 259 | if ( AreFunctionTypesEquivalent( InFunctionSignature, FunctionTypeSymbol, false ) ) 260 | { 261 | return FunctionSymbol; 262 | } 263 | } 264 | } 265 | return nullptr; 266 | } 267 | 268 | bool DiaUtils::AreFunctionTypesEquivalent(const CComPtr& InFunctionTypeA, const CComPtr& InFunctionTypeB, const bool bCheckObjectPointer) 269 | { 270 | const CComPtr ThisObjectPointerTypeA = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InFunctionTypeA, objectPointerType ); 271 | const CComPtr ThisObjectPointerTypeB = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( InFunctionTypeA, objectPointerType ); 272 | 273 | // Either both functions should have object pointer or neither 274 | if ( ( ThisObjectPointerTypeA == nullptr ) != ( ThisObjectPointerTypeB == nullptr ) ) return false; 275 | 276 | if ( bCheckObjectPointer ) 277 | { 278 | // If we are asked to check if object pointers are matching, check that they point to the same symbol index 279 | if ( GET_SYMBOL_ATTRIBUTE_CHECKED( ThisObjectPointerTypeA, symIndexId ) != GET_SYMBOL_ATTRIBUTE_CHECKED( ThisObjectPointerTypeB, symIndexId ) ) return false; 280 | } 281 | 282 | std::vector> ArgumentTypesA; 283 | std::vector> ArgumentTypesB; 284 | 285 | for ( DiaChildSymbolIterator It( InFunctionTypeA, SymTagFunctionArgType ); It; ++It ) 286 | { 287 | const CComPtr ArgumentSymbol = *It; 288 | const CComPtr ArgumentTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( ArgumentSymbol, type ); 289 | ArgumentTypesA.push_back( ArgumentTypeSymbol ); 290 | } 291 | for ( DiaChildSymbolIterator It( InFunctionTypeB, SymTagFunctionArgType ); It; ++It ) 292 | { 293 | const CComPtr ArgumentSymbol = *It; 294 | const CComPtr ArgumentTypeSymbol = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( ArgumentSymbol, type ); 295 | ArgumentTypesB.push_back( ArgumentTypeSymbol ); 296 | } 297 | 298 | // Argument count should always match 299 | if ( ArgumentTypesA.size() != ArgumentTypesB.size() ) return false; 300 | 301 | for ( int32_t ArgumentIndex = 0; ArgumentIndex < ArgumentTypesA.size(); ArgumentIndex++ ) 302 | { 303 | const DWORD ArgumentTypeIdA = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( ArgumentTypesA[ ArgumentIndex ], symIndexId ); 304 | const DWORD ArgumentTypeIdB = GET_SYMBOL_ATTRIBUTE_OR_DEFAULT( ArgumentTypesB[ ArgumentIndex ], symIndexId ); 305 | 306 | // Argument indices should match 307 | if ( ArgumentTypeIdA != ArgumentTypeIdB ) return false; 308 | } 309 | return true; 310 | } 311 | -------------------------------------------------------------------------------- /src/Utils/MemoryGrowableBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils/MemoryGrowableBuffer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | MemoryGrowableBuffer::MemoryGrowableBuffer() 10 | { 11 | } 12 | 13 | MemoryGrowableBuffer::MemoryGrowableBuffer(MemoryGrowableBuffer&& Other) noexcept : BufferData( Other.BufferData ), CurrentOffset( Other.CurrentOffset ), CurrentBufferSize( Other.CurrentBufferSize ) 14 | { 15 | Other.BufferData = nullptr; 16 | Other.CurrentOffset = 0; 17 | Other.CurrentBufferSize = 0; 18 | } 19 | 20 | MemoryGrowableBuffer::~MemoryGrowableBuffer() 21 | { 22 | if ( BufferData ) 23 | { 24 | free( BufferData ); 25 | CurrentBufferSize = 0; 26 | } 27 | } 28 | 29 | MemoryGrowableBuffer& MemoryGrowableBuffer::operator=(MemoryGrowableBuffer&& Other) noexcept 30 | { 31 | if ( &Other != this ) 32 | { 33 | std::swap( BufferData, Other.BufferData ); 34 | std::swap( CurrentOffset, Other.CurrentOffset ); 35 | std::swap( CurrentBufferSize, Other.CurrentBufferSize ); 36 | } 37 | return *this; 38 | } 39 | 40 | void MemoryGrowableBuffer::Seek(const uint64_t NewOffset) 41 | { 42 | assert( NewOffset <= CurrentBufferSize ); 43 | CurrentOffset = NewOffset; 44 | } 45 | 46 | void MemoryGrowableBuffer::Skip(const uint64_t NumToSkip) 47 | { 48 | assert( CurrentOffset + NumToSkip <= CurrentBufferSize ); 49 | CurrentOffset += NumToSkip; 50 | } 51 | 52 | void MemoryGrowableBuffer::Grow(const uint64_t GrowAmount) 53 | { 54 | CurrentBufferSize += (GrowAmount + BufferGrowSizeMultiple - 1) / BufferGrowSizeMultiple * BufferGrowSizeMultiple; 55 | BufferData = static_cast(realloc(BufferData, CurrentBufferSize)); 56 | } 57 | 58 | void MemoryGrowableBuffer::EnsureCapacity(const uint64_t CapacityNeeded) 59 | { 60 | if ( CurrentOffset + CapacityNeeded >= CurrentBufferSize ) 61 | { 62 | Grow( CapacityNeeded ); 63 | } 64 | } 65 | 66 | void MemoryGrowableBuffer::Append(const void* InData, const uint64_t InDataSize) 67 | { 68 | EnsureCapacity(InDataSize); 69 | memcpy( BufferData + CurrentOffset, InData, InDataSize ); 70 | CurrentOffset += InDataSize; 71 | } 72 | 73 | void MemoryGrowableBuffer::Append(const MemoryGrowableBuffer& OtherBuffer) 74 | { 75 | EnsureCapacity(OtherBuffer.Tell()); 76 | memcpy(BufferData + CurrentOffset, OtherBuffer.GetRawBuffer(), OtherBuffer.Tell()); 77 | CurrentOffset += OtherBuffer.Tell(); 78 | } 79 | 80 | void MemoryGrowableBuffer::Copy(const void* InData, const uint64_t InDataSize) 81 | { 82 | EnsureCapacity(InDataSize); 83 | memcpy( BufferData + CurrentOffset, InData, InDataSize ); 84 | } 85 | 86 | bool MemoryGrowableBuffer::WriteToFile(const std::filesystem::path& FilePath) const 87 | { 88 | std::ofstream FileStream(FilePath, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); 89 | if (!FileStream.good()) 90 | { 91 | std::wcerr << L"Failed to open file for writing: " << FilePath.wstring() << std::endl; 92 | return false; 93 | } 94 | FileStream.write( reinterpret_cast(BufferData), CurrentOffset ); 95 | FileStream.flush(); 96 | return true; 97 | } 98 | -------------------------------------------------------------------------------- /src/Utils/StringUtils.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 3 | #include "Utils/StringUtils.h" 4 | #include 5 | #include 6 | 7 | std::wstring ConvertMbStringToWide( const char* InString ) 8 | { 9 | wchar_t ConvertedBuffer[1024]; 10 | ConvertedBuffer[0] = '\0'; 11 | mbstowcs_s( nullptr, ConvertedBuffer, InString, _TRUNCATE ); 12 | return ConvertedBuffer; 13 | } 14 | 15 | std::wstring ReplaceAll(std::wstring str, const std::wstring& from, const std::wstring& to) { 16 | size_t start_pos = 0; 17 | while((start_pos = str.find(from, start_pos)) != std::string::npos) { 18 | str.replace(start_pos, from.length(), to); 19 | start_pos += to.length(); // Handles case where 'to' is a substring of 'from' 20 | } 21 | return str; 22 | } 23 | 24 | std::wstring StringPrintf( const wchar_t* InFormat, ... ) 25 | { 26 | va_list ArgumentList; 27 | va_start( ArgumentList, InFormat ); 28 | const int32_t BufferSize = _vsnwprintf( nullptr, 0, InFormat, ArgumentList ) + 1; 29 | 30 | wchar_t* FormatBuffer = static_cast(malloc(BufferSize * sizeof(wchar_t))); 31 | memset( FormatBuffer, 0, BufferSize * sizeof(wchar_t) ); 32 | _vsnwprintf( FormatBuffer, BufferSize, InFormat, ArgumentList ); 33 | 34 | std::wstring ResultString = FormatBuffer; 35 | free( FormatBuffer ); 36 | va_end( ArgumentList ); 37 | return ResultString; 38 | } 39 | 40 | std::wstring StringUtf8ToWide( const std::string& InUtf8String ) 41 | { 42 | std::wstring_convert> Codecvt; 43 | return Codecvt.from_bytes( InUtf8String ); 44 | } 45 | -------------------------------------------------------------------------------- /src/Utils/TextWriter.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include "Utils/TextWriter.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | FormattedTextWriter& FormattedTextWriter::Append(const std::wstring& InString) 10 | { 11 | PlaceNewlineIndent(); 12 | WriterBuffer << InString; 13 | return *this; 14 | } 15 | 16 | FormattedTextWriter& FormattedTextWriter::Append(const wchar_t* InLiteral) 17 | { 18 | PlaceNewlineIndent(); 19 | WriterBuffer << InLiteral; 20 | return *this; 21 | } 22 | 23 | FormattedTextWriter& FormattedTextWriter::AppendNewline(const std::wstring& InString) 24 | { 25 | PlaceNewlineIndent(); 26 | WriterBuffer << InString; 27 | AppendNewline(); 28 | return *this; 29 | } 30 | 31 | FormattedTextWriter& FormattedTextWriter::AppendNewline(const wchar_t* InLiteral) 32 | { 33 | PlaceNewlineIndent(); 34 | WriterBuffer << InLiteral; 35 | AppendNewline(); 36 | return *this; 37 | } 38 | 39 | FormattedTextWriter& FormattedTextWriter::AppendFormat( const wchar_t* InFormat, ... ) 40 | { 41 | va_list ArgumentList; 42 | va_start( ArgumentList, InFormat ); 43 | const int32_t BufferSize = _vsnwprintf( nullptr, 0, InFormat, ArgumentList ) + 1; 44 | 45 | wchar_t* FormatBuffer = static_cast(malloc(BufferSize * sizeof(wchar_t))); 46 | memset( FormatBuffer, 0, BufferSize * sizeof(wchar_t) ); 47 | _vsnwprintf( FormatBuffer, BufferSize, InFormat, ArgumentList ); 48 | 49 | PlaceNewlineIndent(); 50 | WriterBuffer << FormatBuffer; 51 | 52 | free( FormatBuffer ); 53 | va_end( ArgumentList ); 54 | return *this; 55 | } 56 | 57 | FormattedTextWriter& FormattedTextWriter::AppendFormatNewline( const wchar_t* InFormat, ... ) 58 | { 59 | va_list ArgumentList; 60 | va_start( ArgumentList, InFormat ); 61 | const int32_t BufferSize = _vsnwprintf( nullptr, 0, InFormat, ArgumentList ) + 1; 62 | 63 | wchar_t* FormatBuffer = static_cast(malloc(BufferSize * sizeof(wchar_t))); 64 | memset( FormatBuffer, 0, BufferSize * sizeof(wchar_t) ); 65 | _vsnwprintf( FormatBuffer, BufferSize, InFormat, ArgumentList ); 66 | 67 | PlaceNewlineIndent(); 68 | WriterBuffer << FormatBuffer; 69 | AppendNewline(); 70 | 71 | free( FormatBuffer ); 72 | va_end( ArgumentList ); 73 | return *this; 74 | } 75 | 76 | FormattedTextWriter& FormattedTextWriter::AppendFormatNewlineNoIndent(const wchar_t* InFormat, ...) 77 | { 78 | va_list ArgumentList; 79 | va_start( ArgumentList, InFormat ); 80 | const int32_t BufferSize = _vsnwprintf( nullptr, 0, InFormat, ArgumentList ) + 1; 81 | 82 | wchar_t* FormatBuffer = static_cast(malloc(BufferSize * sizeof(wchar_t))); 83 | memset( FormatBuffer, 0, BufferSize * sizeof(wchar_t) ); 84 | _vsnwprintf( FormatBuffer, BufferSize, InFormat, ArgumentList ); 85 | 86 | PlaceNewlineIndent(1); 87 | WriterBuffer << FormatBuffer; 88 | AppendNewline(); 89 | 90 | free( FormatBuffer ); 91 | va_end( ArgumentList ); 92 | return *this; 93 | } 94 | 95 | FormattedTextWriter& FormattedTextWriter::AppendNewline() 96 | { 97 | PlaceNewlineIndent(); 98 | WriterBuffer << std::endl; 99 | ResetNewlineIndent(); 100 | return *this; 101 | } 102 | 103 | void FormattedTextWriter::PlaceNewlineIndent( int32_t IndentLevelsToIgnore ) 104 | { 105 | if ( !bNewlineIndentPlaced ) 106 | { 107 | if ( IndentLevelsToIgnore > CurrentIndentionLevel ) 108 | { 109 | IndentLevelsToIgnore = CurrentIndentionLevel; 110 | } 111 | WriterBuffer << std::wstring( CurrentIndentionLevel - IndentLevelsToIgnore, L'\t' ); 112 | bNewlineIndentPlaced = true; 113 | } 114 | } 115 | 116 | void FormattedTextWriter::ResetNewlineIndent() 117 | { 118 | bNewlineIndentPlaced = false; 119 | } 120 | 121 | void FormattedTextWriter::IncrementIndentionLevel() 122 | { 123 | CurrentIndentionLevel++; 124 | } 125 | 126 | void FormattedTextWriter::DecrementIndentionLevel() 127 | { 128 | assert( CurrentIndentionLevel > 0 ); 129 | CurrentIndentionLevel--; 130 | } 131 | 132 | void FormattedTextWriter::WriteToFile(const std::filesystem::path& FilePath) const 133 | { 134 | std::wofstream FileStream( FilePath, std::ios_base::out | std::ios_base::trunc ); 135 | 136 | if (!FileStream.good()) 137 | { 138 | std::wcerr << L"Failed to open file for read: " << FilePath.wstring() << std::endl; 139 | return; 140 | } 141 | const std::wstring ResultText = WriterBuffer.str(); 142 | FileStream.write( ResultText.c_str(), ResultText.size() ); 143 | FileStream.flush(); 144 | FileStream.close(); 145 | } 146 | 147 | std::wstring FormattedTextWriter::ToString() const 148 | { 149 | return WriterBuffer.str(); 150 | } 151 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Utils/DiaUtils.h" 7 | #include "Utils/StringUtils.h" 8 | 9 | int main( int ArgC, const char** ArgV ) 10 | { 11 | if ( const HRESULT Result = CoInitializeEx( nullptr, COINIT_MULTITHREADED ); FAILED(Result) ) 12 | { 13 | const _com_error ComError(Result); 14 | std::wcerr << L"COM Initialization failed: " << ComError.ErrorMessage() << std::endl; 15 | return 1; 16 | } 17 | 18 | CComPtr DataSource; 19 | if ( const HRESULT Result = CoCreateInstance( CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), (void**) &DataSource ); FAILED(Result) ) 20 | { 21 | const _com_error ComError(Result); 22 | std::wcout << L"Failed to create COM component for DIA SDK Source: " << ComError.ErrorMessage() << L". Make sure msdia140.dll is registered as a COM server." << std::endl; 23 | std::wcout << L"Attempting to fallback to loading msdia140.dll from the binary directory of the executable." << std::endl; 24 | } 25 | if ( DataSource == nullptr ) 26 | { 27 | if (const HMODULE DiaSdkDLL = LoadLibraryW(L"msdia140.dll"); DiaSdkDLL == nullptr ) 28 | { 29 | const _com_error ComError(HRESULT_FROM_WIN32(GetLastError())); 30 | std::wcout << L"Failed to load msdia140.dll from executable directory: " << ComError.ErrorMessage() << std::endl; 31 | } 32 | else if ( const HRESULT Result = CoCreateDiaDataSource( DiaSdkDLL, DataSource ); FAILED(Result) ) 33 | { 34 | const _com_error ComError(Result); 35 | std::wcout << L"Failed to retrieve COM component for DIA SDK Source from DIA DLL: " << ComError.ErrorMessage() << std::endl; 36 | } 37 | } 38 | if ( DataSource == nullptr ) 39 | { 40 | std::wcerr << L"Failed to load or find COM component for DIA SDK. Check output messages for more information" << std::endl; 41 | return 1; 42 | } 43 | 44 | if ( ArgC != 4 ) 45 | { 46 | std::wcerr << L"Usage: " << ConvertMbStringToWide( ArgV[0] ) << L" Path/To/Executable.exe Path/To/Config.json Path/To/Output/Directory" << std::endl; 47 | return 2; 48 | } 49 | const std::filesystem::path ExecutableFilePath = ConvertMbStringToWide(ArgV[1]); 50 | const std::filesystem::path ConfigFilePath = ConvertMbStringToWide(ArgV[2]); 51 | const std::filesystem::path OutputDirectoryPath = ConvertMbStringToWide(ArgV[3]); 52 | 53 | if ( const HRESULT Result = DataSource->loadDataForExe( ExecutableFilePath.c_str(), ExecutableFilePath.parent_path().c_str(), nullptr ); Result != S_OK ) 54 | { 55 | const _com_error ComError(Result); 56 | std::wcerr << L"Failed to load data from executable file at " << ExecutableFilePath.wstring() << L": " << ComError.ErrorMessage() << std::endl; 57 | return 1; 58 | } 59 | 60 | CComPtr DiaSession; 61 | if ( const HRESULT Result = DataSource->openSession( &DiaSession ); FAILED(Result) ) 62 | { 63 | const _com_error ComError(Result); 64 | std::wcerr << L"Failed to create DIA SDK session: " << ComError.ErrorMessage() << std::endl; 65 | return 1; 66 | } 67 | assert( DiaSession->put_loadAddress(0) == S_OK ); 68 | 69 | CComPtr GlobalExecutableSymbol; 70 | if ( const HRESULT Result = DiaSession->get_globalScope( &GlobalExecutableSymbol ); FAILED(Result) ) 71 | { 72 | const _com_error ComError(Result); 73 | std::wcerr << L"Failed to retrieve SymTagExe global symbol from PDB data store: " << ComError.ErrorMessage() << std::endl; 74 | return 1; 75 | } 76 | 77 | HeaderGeneratorConfigManager ConfigManager; 78 | if ( !ConfigManager.LoadConfigFrom( ConfigFilePath ) ) 79 | { 80 | std::wcerr << L"Failed to load config from " << ConfigFilePath.generic_wstring() << std::endl; 81 | return 1; 82 | } 83 | 84 | HeaderGenerator HeaderGenerator{ ExecutableFilePath.filename().wstring(), OutputDirectoryPath }; 85 | HeaderGenerator.LoadDataFromConfig( ConfigManager.GetMergedConfig() ); 86 | 87 | time_t StartTime = time(nullptr); 88 | HeaderGenerator.Generate( GlobalExecutableSymbol ); 89 | time_t TotalTime = time(nullptr) - StartTime; 90 | std::cout << "Done with header generation in " << TotalTime << " seconds" << std::endl; 91 | return 0; 92 | } 93 | --------------------------------------------------------------------------------