Skip to content

Commit ef59623

Browse files
committed
Fix broken name mangler
1 parent 33a9dea commit ef59623

File tree

4 files changed

+227
-49
lines changed

4 files changed

+227
-49
lines changed

src/CppParser/ASTNameMangler.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/************************************************************************
2+
*
3+
* CppSharp
4+
* Licensed under the simplified BSD license. All rights reserved.
5+
*
6+
************************************************************************/
7+
8+
#include "ASTNameMangler.h"
9+
10+
#include <clang/AST/GlobalDecl.h>
11+
#include <clang/AST/Mangle.h>
12+
#include <clang/Basic/TargetInfo.h>
13+
#include <llvm/IR/Mangler.h>
14+
15+
using namespace clang;
16+
using namespace CppSharp::CppParser;
17+
18+
namespace {
19+
enum ObjCKind {
20+
ObjCClass,
21+
ObjCMetaclass,
22+
};
23+
24+
StringRef getClassSymbolPrefix(ObjCKind Kind, const ASTContext& Context) {
25+
if (Context.getLangOpts().ObjCRuntime.isGNUFamily())
26+
return Kind == ObjCMetaclass ? "_OBJC_METACLASS_" : "_OBJC_CLASS_";
27+
return Kind == ObjCMetaclass ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_";
28+
}
29+
30+
void WriteObjCClassName(const ObjCInterfaceDecl* D, raw_ostream& OS) {
31+
OS << getClassSymbolPrefix(ObjCClass, D->getASTContext());
32+
OS << D->getObjCRuntimeNameAsString();
33+
}
34+
}
35+
36+
ASTNameMangler::ASTNameMangler(ASTContext& Ctx)
37+
: MC(Ctx.createMangleContext())
38+
, DL(Ctx.getTargetInfo().getDataLayoutString())
39+
{
40+
}
41+
42+
std::string ASTNameMangler::GetName(const Decl* D) {
43+
std::string Name;
44+
{
45+
llvm::raw_string_ostream OS(Name);
46+
WriteName(D, OS);
47+
}
48+
return Name;
49+
}
50+
51+
bool ASTNameMangler::WriteName(const Decl* D, raw_ostream& OS) {
52+
// First apply frontend mangling.
53+
SmallString<128> FrontendBuf;
54+
llvm::raw_svector_ostream FrontendBufOS(FrontendBuf);
55+
if (auto* FD = dyn_cast<FunctionDecl>(D)) {
56+
if (FD->isDependentContext())
57+
return true;
58+
if (WriteFuncOrVarName(FD, FrontendBufOS))
59+
return true;
60+
}
61+
else if (auto* VD = dyn_cast<VarDecl>(D)) {
62+
if (WriteFuncOrVarName(VD, FrontendBufOS))
63+
return true;
64+
}
65+
else if (auto* MD = dyn_cast<ObjCMethodDecl>(D)) {
66+
MC->mangleObjCMethodName(MD, OS, /*includePrefixByte=*/false,
67+
/*includeCategoryNamespace=*/true);
68+
return false;
69+
}
70+
else if (auto* ID = dyn_cast<ObjCInterfaceDecl>(D)) {
71+
WriteObjCClassName(ID, FrontendBufOS);
72+
}
73+
else {
74+
return true;
75+
}
76+
77+
// Now apply backend mangling.
78+
llvm::Mangler::getNameWithPrefix(OS, FrontendBufOS.str(), DL);
79+
return false;
80+
}
81+
82+
std::string ASTNameMangler::GetMangledStructor(const NamedDecl* ND, unsigned StructorType) {
83+
std::string FrontendBuf;
84+
llvm::raw_string_ostream FOS(FrontendBuf);
85+
86+
GlobalDecl GD;
87+
if (const auto* CD = dyn_cast_or_null<CXXConstructorDecl>(ND))
88+
GD = GlobalDecl(CD, static_cast<CXXCtorType>(StructorType));
89+
else if (const auto* DD = dyn_cast_or_null<CXXDestructorDecl>(ND))
90+
GD = GlobalDecl(DD, static_cast<CXXDtorType>(StructorType));
91+
MC->mangleName(GD, FOS);
92+
93+
std::string BackendBuf;
94+
llvm::raw_string_ostream BOS(BackendBuf);
95+
96+
llvm::Mangler::getNameWithPrefix(BOS, FrontendBuf, DL);
97+
98+
return BackendBuf;
99+
}
100+
101+
std::string ASTNameMangler::GetMangledThunk(const CXXMethodDecl* MD, const ThunkInfo& T, bool /*ElideOverrideInfo*/) {
102+
std::string FrontendBuf;
103+
llvm::raw_string_ostream FOS(FrontendBuf);
104+
105+
// TODO: Enable `ElideOverrideInfo` param if clang is updated to 19
106+
MC->mangleThunk(MD, T, /*ElideOverrideInfo,*/ FOS);
107+
108+
std::string BackendBuf;
109+
llvm::raw_string_ostream BOS(BackendBuf);
110+
111+
llvm::Mangler::getNameWithPrefix(BOS, FrontendBuf, DL);
112+
113+
return BackendBuf;
114+
}
115+
116+
bool ASTNameMangler::WriteFuncOrVarName(const NamedDecl* D, raw_ostream& OS) const
117+
{
118+
if (!MC->shouldMangleDeclName(D)) {
119+
const IdentifierInfo* II = D->getIdentifier();
120+
if (!II)
121+
return true;
122+
OS << II->getName();
123+
return false;
124+
}
125+
126+
GlobalDecl GD;
127+
if (const auto* CtorD = dyn_cast<CXXConstructorDecl>(D))
128+
GD = GlobalDecl(CtorD, Ctor_Complete);
129+
else if (const auto* DtorD = dyn_cast<CXXDestructorDecl>(D))
130+
GD = GlobalDecl(DtorD, Dtor_Complete);
131+
else if (D->hasAttr<CUDAGlobalAttr>())
132+
GD = GlobalDecl(cast<FunctionDecl>(D));
133+
else
134+
GD = GlobalDecl(D);
135+
136+
MC->mangleName(GD, OS);
137+
return false;
138+
}

src/CppParser/ASTNameMangler.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/************************************************************************
2+
*
3+
* CppSharp
4+
* Licensed under the simplified BSD license. All rights reserved.
5+
*
6+
************************************************************************/
7+
8+
#pragma once
9+
10+
#include <clang/AST/ASTFwd.h>
11+
#include <llvm/IR/DataLayout.h>
12+
13+
#include <string>
14+
15+
namespace clang
16+
{
17+
class ASTContext;
18+
class MangleContext;
19+
struct ThunkInfo;
20+
}
21+
22+
namespace llvm
23+
{
24+
class raw_ostream;
25+
}
26+
27+
namespace CppSharp::CppParser {
28+
29+
/// <summary>
30+
/// Helper class for getting the mangled name of a declaration
31+
/// </summary>
32+
/// <remarks>Source adapted from https://clang.llvm.org/doxygen/Mangle_8cpp_source.html#l00394</remarks>
33+
class ASTNameMangler
34+
{
35+
public:
36+
explicit ASTNameMangler(clang::ASTContext& Ctx);
37+
38+
std::string GetName(const clang::Decl* D);
39+
bool WriteName(const clang::Decl* D, llvm::raw_ostream& OS);
40+
41+
private:
42+
std::string GetMangledStructor(const clang::NamedDecl* ND, unsigned StructorType);
43+
std::string GetMangledThunk(const clang::CXXMethodDecl* MD, const clang::ThunkInfo& T, bool ElideOverrideInfo);
44+
bool WriteFuncOrVarName(const clang::NamedDecl* D, llvm::raw_ostream& OS) const;
45+
46+
std::unique_ptr<clang::MangleContext> MC;
47+
llvm::DataLayout DL;
48+
};
49+
50+
}

src/CppParser/Parser.cpp

Lines changed: 35 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
#include <Driver/ToolChains/Linux.h>
5959
#include <Driver/ToolChains/MSVC.h>
6060

61+
#include "ASTNameMangler.h"
62+
6163
#if defined(__APPLE__) || defined(__linux__)
6264
#ifndef _GNU_SOURCE
6365
#define _GNU_SOURCE
@@ -468,69 +470,55 @@ void Parser::Setup(bool Compile)
468470
PP.getLangOpts());
469471

470472
c->createASTContext();
473+
NameMangler.reset(new ASTNameMangler(c->getASTContext()));
471474
}
472475

473476
//-----------------------------------//
474477

475-
std::string Parser::GetDeclMangledName(const clang::Decl* D)
478+
std::string Parser::GetDeclMangledName(const clang::Decl* D) const
476479
{
477-
using namespace clang;
480+
// Source adapted from https://clang.llvm.org/doxygen/JSONNodeDumper_8cpp_source.html#l00845
478481

479-
if(!D || !isa<NamedDecl>(D))
480-
return "";
482+
using namespace clang;
481483

482-
bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D)
483-
|| isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D);
484+
auto ND = dyn_cast_or_null<NamedDecl>(D);
485+
if (!ND || !ND->getDeclName())
486+
return {};
484487

485-
if (!CanMangle) return "";
488+
// If the declaration is dependent or is in a dependent context, then the
489+
// mangling is unlikely to be meaningful (and in some cases may cause
490+
// "don't know how to mangle this" assertion failures.)
491+
if (ND->isTemplated())
492+
return {};
486493

487-
auto ND = cast<NamedDecl>(D);
488-
std::unique_ptr<MangleContext> MC;
489-
490-
auto& AST = c->getASTContext();
491-
auto targetABI = c->getTarget().getCXXABI().getKind();
492-
switch(targetABI)
493-
{
494-
default:
495-
MC.reset(ItaniumMangleContext::create(AST, AST.getDiagnostics()));
496-
break;
497-
case TargetCXXABI::Microsoft:
498-
MC.reset(MicrosoftMangleContext::create(AST, AST.getDiagnostics()));
499-
break;
500-
}
501-
502-
if (!MC)
503-
llvm_unreachable("Unknown mangling ABI");
504-
505-
std::string Mangled;
506-
llvm::raw_string_ostream Out(Mangled);
494+
/*bool CanMangle = isa<FunctionDecl>(D) || isa<VarDecl>(D)
495+
|| isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D);
507496
508-
bool IsDependent = false;
509-
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ND))
510-
IsDependent |= VD->getType()->isDependentType();
497+
if (!CanMangle)
498+
return {};*/
511499

512-
if (!IsDependent)
513-
IsDependent |= ND->getDeclContext()->isDependentContext();
500+
// FIXME: There are likely other contexts in which it makes no sense to ask
501+
// for a mangled name.
502+
if (isa<RequiresExprBodyDecl>(ND->getDeclContext()))
503+
return {};
514504

515-
if (!MC->shouldMangleDeclName(ND) || IsDependent)
516-
return ND->getDeclName().getAsString();
505+
// Do not mangle template deduction guides.
506+
if (isa<CXXDeductionGuideDecl>(ND))
507+
return {};
517508

518-
GlobalDecl GD;
519-
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(ND))
520-
GD = GlobalDecl(CD, Ctor_Base);
521-
else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(ND))
522-
GD = GlobalDecl(DD, Dtor_Base);
523-
else
524-
GD = GlobalDecl(ND);
525-
MC->mangleName(GD, Out);
509+
// Mangled names are not meaningful for locals, and may not be well-defined
510+
// in the case of VLAs.
511+
auto* VD = dyn_cast<VarDecl>(ND);
512+
if (VD && VD->hasLocalStorage())
513+
return {};
526514

527-
Out.flush();
515+
std::string MangledName = NameMangler->GetName(ND);
528516

529517
// Strip away LLVM name marker.
530-
if(!Mangled.empty() && Mangled[0] == '\01')
531-
Mangled = Mangled.substr(1);
518+
if (!MangledName.empty() && MangledName[0] == '\01')
519+
MangledName = MangledName.substr(1);
532520

533-
return Mangled;
521+
return MangledName;
534522
}
535523

536524
//-----------------------------------//
@@ -632,7 +620,7 @@ static clang::SourceLocation GetDeclStartLocation(clang::CompilerInstance* C,
632620
return GetDeclStartLocation(C, prevDecl);
633621
}
634622

635-
std::string Parser::GetTypeName(const clang::Type* Type)
623+
std::string Parser::GetTypeName(const clang::Type* Type) const
636624
{
637625
using namespace clang;
638626

src/CppParser/Parser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ namespace clang {
4747
#define Debug printf
4848

4949
namespace CppSharp { namespace CppParser {
50+
class ASTNameMangler;
5051

5152
class Parser
5253
{
@@ -133,8 +134,8 @@ class Parser
133134
// Clang helpers
134135
SourceLocationKind GetLocationKind(const clang::SourceLocation& Loc);
135136
bool IsValidDeclaration(const clang::SourceLocation& Loc);
136-
std::string GetDeclMangledName(const clang::Decl* D);
137-
std::string GetTypeName(const clang::Type* Type);
137+
std::string GetDeclMangledName(const clang::Decl* D) const;
138+
std::string GetTypeName(const clang::Type* Type) const;
138139
bool CanCheckCodeGenInfo(const clang::Type* Ty);
139140
void CompleteIfSpecializationType(const clang::QualType& QualType);
140141
Parameter* WalkParameter(const clang::ParmVarDecl* PVD,
@@ -181,6 +182,7 @@ class Parser
181182
int index;
182183
std::unique_ptr<clang::CompilerInstance> c;
183184
llvm::LLVMContext LLVMCtx;
185+
std::unique_ptr<ASTNameMangler> NameMangler;
184186
std::unique_ptr<llvm::Module> LLVMModule;
185187
std::unique_ptr<clang::CodeGen::CodeGenModule> CGM;
186188
std::unique_ptr<clang::CodeGen::CodeGenTypes> codeGenTypes;

0 commit comments

Comments
 (0)