From ef596235062f6008cdb1e813bc23bc762f7840f7 Mon Sep 17 00:00:00 2001 From: duckdoom5 Date: Wed, 5 Feb 2025 17:52:47 +0100 Subject: [PATCH] Fix broken name mangler --- src/CppParser/ASTNameMangler.cpp | 138 +++++++++++++++++++++++++++++++ src/CppParser/ASTNameMangler.h | 50 +++++++++++ src/CppParser/Parser.cpp | 82 ++++++++---------- src/CppParser/Parser.h | 6 +- 4 files changed, 227 insertions(+), 49 deletions(-) create mode 100644 src/CppParser/ASTNameMangler.cpp create mode 100644 src/CppParser/ASTNameMangler.h diff --git a/src/CppParser/ASTNameMangler.cpp b/src/CppParser/ASTNameMangler.cpp new file mode 100644 index 00000000..d7bb20f0 --- /dev/null +++ b/src/CppParser/ASTNameMangler.cpp @@ -0,0 +1,138 @@ +/************************************************************************ +* +* CppSharp +* Licensed under the simplified BSD license. All rights reserved. +* +************************************************************************/ + +#include "ASTNameMangler.h" + +#include +#include +#include +#include + +using namespace clang; +using namespace CppSharp::CppParser; + +namespace { + enum ObjCKind { + ObjCClass, + ObjCMetaclass, + }; + + StringRef getClassSymbolPrefix(ObjCKind Kind, const ASTContext& Context) { + if (Context.getLangOpts().ObjCRuntime.isGNUFamily()) + return Kind == ObjCMetaclass ? "_OBJC_METACLASS_" : "_OBJC_CLASS_"; + return Kind == ObjCMetaclass ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_"; + } + + void WriteObjCClassName(const ObjCInterfaceDecl* D, raw_ostream& OS) { + OS << getClassSymbolPrefix(ObjCClass, D->getASTContext()); + OS << D->getObjCRuntimeNameAsString(); + } +} + +ASTNameMangler::ASTNameMangler(ASTContext& Ctx) + : MC(Ctx.createMangleContext()) + , DL(Ctx.getTargetInfo().getDataLayoutString()) +{ +} + +std::string ASTNameMangler::GetName(const Decl* D) { + std::string Name; + { + llvm::raw_string_ostream OS(Name); + WriteName(D, OS); + } + return Name; +} + +bool ASTNameMangler::WriteName(const Decl* D, raw_ostream& OS) { + // First apply frontend mangling. + SmallString<128> FrontendBuf; + llvm::raw_svector_ostream FrontendBufOS(FrontendBuf); + if (auto* FD = dyn_cast(D)) { + if (FD->isDependentContext()) + return true; + if (WriteFuncOrVarName(FD, FrontendBufOS)) + return true; + } + else if (auto* VD = dyn_cast(D)) { + if (WriteFuncOrVarName(VD, FrontendBufOS)) + return true; + } + else if (auto* MD = dyn_cast(D)) { + MC->mangleObjCMethodName(MD, OS, /*includePrefixByte=*/false, + /*includeCategoryNamespace=*/true); + return false; + } + else if (auto* ID = dyn_cast(D)) { + WriteObjCClassName(ID, FrontendBufOS); + } + else { + return true; + } + + // Now apply backend mangling. + llvm::Mangler::getNameWithPrefix(OS, FrontendBufOS.str(), DL); + return false; +} + +std::string ASTNameMangler::GetMangledStructor(const NamedDecl* ND, unsigned StructorType) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + GlobalDecl GD; + if (const auto* CD = dyn_cast_or_null(ND)) + GD = GlobalDecl(CD, static_cast(StructorType)); + else if (const auto* DD = dyn_cast_or_null(ND)) + GD = GlobalDecl(DD, static_cast(StructorType)); + MC->mangleName(GD, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FrontendBuf, DL); + + return BackendBuf; +} + +std::string ASTNameMangler::GetMangledThunk(const CXXMethodDecl* MD, const ThunkInfo& T, bool /*ElideOverrideInfo*/) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + // TODO: Enable `ElideOverrideInfo` param if clang is updated to 19 + MC->mangleThunk(MD, T, /*ElideOverrideInfo,*/ FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FrontendBuf, DL); + + return BackendBuf; +} + +bool ASTNameMangler::WriteFuncOrVarName(const NamedDecl* D, raw_ostream& OS) const +{ + if (!MC->shouldMangleDeclName(D)) { + const IdentifierInfo* II = D->getIdentifier(); + if (!II) + return true; + OS << II->getName(); + return false; + } + + GlobalDecl GD; + if (const auto* CtorD = dyn_cast(D)) + GD = GlobalDecl(CtorD, Ctor_Complete); + else if (const auto* DtorD = dyn_cast(D)) + GD = GlobalDecl(DtorD, Dtor_Complete); + else if (D->hasAttr()) + GD = GlobalDecl(cast(D)); + else + GD = GlobalDecl(D); + + MC->mangleName(GD, OS); + return false; +} diff --git a/src/CppParser/ASTNameMangler.h b/src/CppParser/ASTNameMangler.h new file mode 100644 index 00000000..acd8e14d --- /dev/null +++ b/src/CppParser/ASTNameMangler.h @@ -0,0 +1,50 @@ +/************************************************************************ +* +* CppSharp +* Licensed under the simplified BSD license. All rights reserved. +* +************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace clang +{ + class ASTContext; + class MangleContext; + struct ThunkInfo; +} + +namespace llvm +{ + class raw_ostream; +} + +namespace CppSharp::CppParser { + +/// +/// Helper class for getting the mangled name of a declaration +/// +/// Source adapted from https://clang.llvm.org/doxygen/Mangle_8cpp_source.html#l00394 +class ASTNameMangler +{ +public: + explicit ASTNameMangler(clang::ASTContext& Ctx); + + std::string GetName(const clang::Decl* D); + bool WriteName(const clang::Decl* D, llvm::raw_ostream& OS); + +private: + std::string GetMangledStructor(const clang::NamedDecl* ND, unsigned StructorType); + std::string GetMangledThunk(const clang::CXXMethodDecl* MD, const clang::ThunkInfo& T, bool ElideOverrideInfo); + bool WriteFuncOrVarName(const clang::NamedDecl* D, llvm::raw_ostream& OS) const; + + std::unique_ptr MC; + llvm::DataLayout DL; +}; + +} diff --git a/src/CppParser/Parser.cpp b/src/CppParser/Parser.cpp index 467d8ca6..5ab3b02a 100644 --- a/src/CppParser/Parser.cpp +++ b/src/CppParser/Parser.cpp @@ -58,6 +58,8 @@ #include #include +#include "ASTNameMangler.h" + #if defined(__APPLE__) || defined(__linux__) #ifndef _GNU_SOURCE #define _GNU_SOURCE @@ -468,69 +470,55 @@ void Parser::Setup(bool Compile) PP.getLangOpts()); c->createASTContext(); + NameMangler.reset(new ASTNameMangler(c->getASTContext())); } //-----------------------------------// -std::string Parser::GetDeclMangledName(const clang::Decl* D) +std::string Parser::GetDeclMangledName(const clang::Decl* D) const { - using namespace clang; + // Source adapted from https://clang.llvm.org/doxygen/JSONNodeDumper_8cpp_source.html#l00845 - if(!D || !isa(D)) - return ""; + using namespace clang; - bool CanMangle = isa(D) || isa(D) - || isa(D) || isa(D); + auto ND = dyn_cast_or_null(D); + if (!ND || !ND->getDeclName()) + return {}; - if (!CanMangle) return ""; + // If the declaration is dependent or is in a dependent context, then the + // mangling is unlikely to be meaningful (and in some cases may cause + // "don't know how to mangle this" assertion failures.) + if (ND->isTemplated()) + return {}; - auto ND = cast(D); - std::unique_ptr MC; - - auto& AST = c->getASTContext(); - auto targetABI = c->getTarget().getCXXABI().getKind(); - switch(targetABI) - { - default: - MC.reset(ItaniumMangleContext::create(AST, AST.getDiagnostics())); - break; - case TargetCXXABI::Microsoft: - MC.reset(MicrosoftMangleContext::create(AST, AST.getDiagnostics())); - break; - } - - if (!MC) - llvm_unreachable("Unknown mangling ABI"); - - std::string Mangled; - llvm::raw_string_ostream Out(Mangled); + /*bool CanMangle = isa(D) || isa(D) + || isa(D) || isa(D); - bool IsDependent = false; - if (const ValueDecl *VD = dyn_cast(ND)) - IsDependent |= VD->getType()->isDependentType(); + if (!CanMangle) + return {};*/ - if (!IsDependent) - IsDependent |= ND->getDeclContext()->isDependentContext(); + // FIXME: There are likely other contexts in which it makes no sense to ask + // for a mangled name. + if (isa(ND->getDeclContext())) + return {}; - if (!MC->shouldMangleDeclName(ND) || IsDependent) - return ND->getDeclName().getAsString(); + // Do not mangle template deduction guides. + if (isa(ND)) + return {}; - GlobalDecl GD; - if (const CXXConstructorDecl *CD = dyn_cast(ND)) - GD = GlobalDecl(CD, Ctor_Base); - else if (const CXXDestructorDecl *DD = dyn_cast(ND)) - GD = GlobalDecl(DD, Dtor_Base); - else - GD = GlobalDecl(ND); - MC->mangleName(GD, Out); + // Mangled names are not meaningful for locals, and may not be well-defined + // in the case of VLAs. + auto* VD = dyn_cast(ND); + if (VD && VD->hasLocalStorage()) + return {}; - Out.flush(); + std::string MangledName = NameMangler->GetName(ND); // Strip away LLVM name marker. - if(!Mangled.empty() && Mangled[0] == '\01') - Mangled = Mangled.substr(1); + if (!MangledName.empty() && MangledName[0] == '\01') + MangledName = MangledName.substr(1); - return Mangled; + return MangledName; } //-----------------------------------// @@ -632,7 +620,7 @@ static clang::SourceLocation GetDeclStartLocation(clang::CompilerInstance* C, return GetDeclStartLocation(C, prevDecl); } -std::string Parser::GetTypeName(const clang::Type* Type) +std::string Parser::GetTypeName(const clang::Type* Type) const { using namespace clang; diff --git a/src/CppParser/Parser.h b/src/CppParser/Parser.h index da0ddcc6..26c39656 100644 --- a/src/CppParser/Parser.h +++ b/src/CppParser/Parser.h @@ -47,6 +47,7 @@ namespace clang { #define Debug printf namespace CppSharp { namespace CppParser { + class ASTNameMangler; class Parser { @@ -133,8 +134,8 @@ private: // Clang helpers SourceLocationKind GetLocationKind(const clang::SourceLocation& Loc); bool IsValidDeclaration(const clang::SourceLocation& Loc); - std::string GetDeclMangledName(const clang::Decl* D); - std::string GetTypeName(const clang::Type* Type); + std::string GetDeclMangledName(const clang::Decl* D) const; + std::string GetTypeName(const clang::Type* Type) const; bool CanCheckCodeGenInfo(const clang::Type* Ty); void CompleteIfSpecializationType(const clang::QualType& QualType); Parameter* WalkParameter(const clang::ParmVarDecl* PVD, @@ -181,6 +182,7 @@ private: int index; std::unique_ptr c; llvm::LLVMContext LLVMCtx; + std::unique_ptr NameMangler; std::unique_ptr LLVMModule; std::unique_ptr CGM; std::unique_ptr codeGenTypes;