Index: llvm/include/llvm/Demangle/Demangle.h =================================================================== --- llvm/include/llvm/Demangle/Demangle.h +++ llvm/include/llvm/Demangle/Demangle.h @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include +#include namespace llvm { /// This is a llvm local version of __cxa_demangle. Other than the name and @@ -25,4 +26,51 @@ char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status); -} + +/// "Partial" demangler. This supports demangling a string into an AST +/// (typically an intermediate stage in itaniumDemangle) and querying certain +/// properties or partially printing the demangled name. +struct ItaniumPartialDemangler { + ItaniumPartialDemangler(); + ItaniumPartialDemangler(ItaniumPartialDemangler &&Other); + ItaniumPartialDemangler &operator=(ItaniumPartialDemangler &&Other); + ~ItaniumPartialDemangler(); + + /// Demangle into an AST. Subsequent calls to the rest of the member functions + /// implicitly operate on the AST this produces. + /// \return true on error, false otherwise + bool partialDemangle(const char *MangledName); + + /// Just print the entire mangled name into Buf. Buf and N behave like the + /// second and third parameters to itaniumDemangle. + char *finishDemangle(char *Buf, size_t *N) const; + + /// Get the base name of a function. This doesn't include trailing template + /// arguments, ie for "a::b" this function returns "b". + char *getFunctionBaseName(char *Buf, size_t *N) const; + + /// Get the context name for a function. For "a::b::c", this function returns + /// "a::b". + char *getFunctionContextName(char *Buf, size_t *N) const; + + /// Get the entire name of this function. + char *getFunctionName(char *Buf, size_t *N) const; + + /// Get the parameters for this function. + char *getFunctionParameters(char *Buf, size_t *N) const; + + /// If this function is provably a non-static member function. We can only + /// know this if the function has a cv or reference qualifier. + bool isProvablyMemberFunction() const; + + /// If this name is a function. + bool isFunction() const; + + /// If this name is a special name. + bool isSpecialName() const; + +private: + void *RootNode; + void *Context; +}; +} // namespace llvm Index: llvm/lib/Demangle/ItaniumDemangle.cpp =================================================================== --- llvm/lib/Demangle/ItaniumDemangle.cpp +++ llvm/lib/Demangle/ItaniumDemangle.cpp @@ -459,11 +459,11 @@ } }; -class AbiTagAttr final : public Node { - const Node* Base; +struct AbiTagAttr : Node { + Node* Base; StringView Tag; -public: - AbiTagAttr(const Node* Base_, StringView Tag_) + + AbiTagAttr(Node* Base_, StringView Tag_) : Node(KAbiTagAttr, Base_->ParameterPackSize, Base_->RHSComponentCache, Base_->ArrayCache, Base_->FunctionCache), Base(Base_), Tag(Tag_) {} @@ -819,6 +819,10 @@ ParameterPackSize = std::min(ParameterPackSize, Ret->ParameterPackSize); } + Qualifiers getCVQuals() const { return CVQuals; } + FunctionRefQual getRefQual() const { return RefQual; } + NodeArray getParams() const { return Params; } + bool hasRHSComponentSlow(OutputStream &) const override { return true; } bool hasFunctionSlow(OutputStream &) const override { return true; } @@ -900,18 +904,18 @@ } }; -class QualifiedName final : public Node { +struct QualifiedName : Node { // qualifier::name - const Node *Qualifier; - const Node *Name; + Node *Qualifier; + Node *Name; -public: QualifiedName(Node* Qualifier_, Node* Name_) : Node(KQualifiedName, std::min(Qualifier_->ParameterPackSize, Name_->ParameterPackSize)), Qualifier(Qualifier_), Name(Name_) {} StringView getBaseName() const override { return Name->getBaseName(); } + Node *getQualifier() const { return Qualifier; } void printLeft(OutputStream &S) const override { Qualifier->print(S); @@ -1109,12 +1113,11 @@ } }; -class NameWithTemplateArgs final : public Node { +struct NameWithTemplateArgs : Node { // name Node *Name; Node *TemplateArgs; -public: NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) : Node(KNameWithTemplateArgs, std::min(Name_->ParameterPackSize, TemplateArgs_->ParameterPackSize)), @@ -1128,10 +1131,9 @@ } }; -class GlobalQualifiedName final : public Node { +struct GlobalQualifiedName : Node { Node *Child; -public: GlobalQualifiedName(Node* Child_) : Node(KGlobalQualifiedName, Child_->ParameterPackSize), Child(Child_) {} @@ -1143,10 +1145,9 @@ } }; -class StdQualifiedName final : public Node { +struct StdQualifiedName : Node { Node *Child; -public: StdQualifiedName(Node *Child_) : Node(KStdQualifiedName, Child_->ParameterPackSize), Child(Child_) {} @@ -1265,11 +1266,10 @@ } }; -class CtorDtorName final : public Node { - const Node *Basename; - const bool IsDtor; +struct CtorDtorName : Node { + Node *Basename; + bool IsDtor; -public: CtorDtorName(Node *Basename_, bool IsDtor_) : Node(KCtorDtorName, Basename_->ParameterPackSize), Basename(Basename_), IsDtor(IsDtor_) {} @@ -1856,14 +1856,17 @@ BlockList->Current - N); } - ~BumpPointerAllocator() { + void reset() { while (BlockList) { BlockMeta* Tmp = BlockList; BlockList = BlockList->Next; if (reinterpret_cast(Tmp) != InitialBuffer) delete[] reinterpret_cast(Tmp); } + BlockList = new (InitialBuffer) BlockMeta{nullptr, 0}; } + + ~BumpPointerAllocator() { reset(); } }; template @@ -2009,6 +2012,18 @@ Db(const char *First_, const char *Last_) : First(First_), Last(Last_) {} + void reset(const char *First_, const char *Last_) { + First = First_; + Last = Last_; + Names.clear(); + Subs.clear(); + TemplateParams.clear(); + EncodingDepth = 0; + TagTemplates = FixForwardReferences = ParsingLambdaParams = false; + TryToParseTemplateArgs = true; + ASTAllocator.reset(); + } + template T *make(Args &&... args) { return new (ASTAllocator.allocate(sizeof(T))) T(std::forward(args)...); @@ -4889,3 +4904,219 @@ *Status = InternalStatus; return InternalStatus == success ? Buf : nullptr; } + +namespace llvm { + +ItaniumPartialDemangler::ItaniumPartialDemangler() + : RootNode(nullptr), Context(new Db{nullptr, nullptr}) {} + +ItaniumPartialDemangler::~ItaniumPartialDemangler() { + delete static_cast(Context); +} + +ItaniumPartialDemangler::ItaniumPartialDemangler( + ItaniumPartialDemangler &&Other) { + std::swap(RootNode, Other.RootNode); + std::swap(Context, Other.Context); +} + +ItaniumPartialDemangler &ItaniumPartialDemangler:: +operator=(ItaniumPartialDemangler &&Other) { + std::swap(RootNode, Other.RootNode); + std::swap(Context, Other.Context); + return *this; +} + +// Demangle MangledName into an AST, storing it into this->RootNode. +bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) { + Db *Parser = static_cast(Context); + size_t Len = std::strlen(MangledName); + Parser->reset(MangledName, MangledName + Len); + RootNode = Parser->parse(); + if (RootNode == nullptr) + return true; + + if (Parser->FixForwardReferences && !Parser->TemplateParams.empty()) { + Parser->FixForwardReferences = false; + Parser->TagTemplates = false; + Parser->Names.clear(); + Parser->Subs.clear(); + Parser->First = MangledName; + Parser->Last = MangledName + Len; + RootNode = Parser->parse(); + if (RootNode == nullptr || Parser->FixForwardReferences) + return true; + } + + return static_cast(RootNode)->containsUnexpandedParameterPack(); +} + +static char *printNode(Node *RootNode, char *Buf, size_t *N) { + size_t BufSize; + if (Buf == nullptr) { + Buf = static_cast(std::malloc(1024)); + BufSize = 1024; + if (Buf == nullptr) + return nullptr; + } else { + BufSize = *N; + } + + OutputStream S(Buf, BufSize); + RootNode->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +static char *printStringView(StringView Str, char *Buf, size_t *N) { + if (N == nullptr) + Buf = static_cast(std::malloc(Str.size() + 1)); + else if (*N < Str.size() + 1) { + Buf = static_cast(std::realloc(Buf, Str.size() + 1)); + *N = Str.size() + 1; + } + + std::copy(Str.begin(), Str.end(), Buf); + Buf[Str.size()] = '\0'; + return Buf; +} + +static Node *findBaseName(Node *Name) { + while (true) { + switch (Name->getKind()) { + case Node::KQualifiedName: + Name = static_cast(Name)->Name; + break; + case Node::KStdQualifiedName: + Name = static_cast(Name)->Child; + break; + case Node::KGlobalQualifiedName: + Name = static_cast(Name)->Child; + break; + case Node::KNameWithTemplateArgs: + Name = static_cast(Name)->Name; + break; + case Node::KCtorDtorName: + Name = static_cast(Name)->Basename; + break; + case Node::KAbiTagAttr: + Name = static_cast(Name)->Base; + break; + default: + return Name; + } + } +} + +char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + Node *Name = findBaseName( + static_cast(RootNode)->getName()); + + switch (Name->getKind()) { + case Node::KExpandedSpecialSubstitution: + case Node::KSpecialSubstitution: { + auto View = Name->getBaseName(); + if (N == nullptr) + Buf = static_cast(std::malloc(View.size() + 1)); + else if (*N < View.size() + 1) + Buf = static_cast(std::realloc(Buf, View.size() + 1)); + std::copy(View.begin(), View.end(), Buf); + Buf[View.size()] = '\0'; + return Buf; + } + default: + break; + } + + return printNode(Name, Buf, N); +} + +char *ItaniumPartialDemangler::getFunctionContextName(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + Node *Name = static_cast(RootNode)->getName(); + while (true) { + switch (Name->getKind()) { + case Node::KQualifiedName: + Name = static_cast(Name)->Qualifier; + return printNode(Name, Buf, N); + case Node::KStdQualifiedName: + return printStringView("std", Buf, N); + case Node::KNameWithTemplateArgs: + Name = static_cast(Name)->Name; + break; + case Node::KCtorDtorName: + Name = static_cast(Name)->Basename; + return printNode(Name, Buf, N); + case Node::KAbiTagAttr: + Name = static_cast(Name)->Base; + break; + default: + return nullptr; + } + } +} + +char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + auto *Enc = static_cast(RootNode)->getName(); + return printNode(Enc, Buf, N); +} + +char *ItaniumPartialDemangler::getFunctionParameters(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + auto Params = static_cast(RootNode)->getParams(); + + size_t Size; + if (N == nullptr) { + Size = 128; + Buf = static_cast(std::malloc(Size)); + } else + Size = *N; + + + OutputStream Stream(Buf, Size); + Stream += '('; + Params.printWithComma(Stream); + Stream += ')'; + Stream += '\0'; + + if (N != nullptr) + *N = Stream.getCurrentPosition(); + return Stream.getBuffer(); +} + +char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return printNode(static_cast(RootNode), Buf, N); +} + +bool ItaniumPartialDemangler::isProvablyMemberFunction() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + if (!isFunction()) + return false; + auto *E = static_cast(RootNode); + return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone; +} + +bool ItaniumPartialDemangler::isFunction() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + return static_cast(RootNode)->getKind() == Node::KFunctionEncoding; +} + +bool ItaniumPartialDemangler::isSpecialName() const { + assert(RootNode != nullptr && "must call partialDemangle()"); + auto K = static_cast(RootNode)->getKind(); + return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName; +} + +} Index: llvm/unittests/CMakeLists.txt =================================================================== --- llvm/unittests/CMakeLists.txt +++ llvm/unittests/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Bitcode) add_subdirectory(CodeGen) add_subdirectory(DebugInfo) +add_subdirectory(Demangle) add_subdirectory(ExecutionEngine) add_subdirectory(FuzzMutate) add_subdirectory(IR) Index: llvm/unittests/Demangle/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/Demangle/CMakeLists.txt @@ -0,0 +1,7 @@ +set(DemangleSources + PartialDemangleTest.cpp +) + +add_llvm_unittest(DemangleTests + ${DemangleSources} +) Index: llvm/unittests/Demangle/PartialDemangleTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Demangle/PartialDemangleTest.cpp @@ -0,0 +1,51 @@ +//===----------------------- PartialDemangleTest.cpp ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/Demangle.h" +#include "gtest/gtest.h" + +TEST(PartialDemangleTest, TestNameSplitting) { + llvm::ItaniumPartialDemangler Context; + EXPECT_FALSE(Context.partialDemangle("_Z1fv")); + + size_t Size = 4; + char *Buffer = static_cast(std::malloc(Size)); + + Buffer = Context.getFunctionBaseName(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "f"), 0); + + Buffer = Context.getFunctionParameters(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "()"), 0); + + Buffer = Context.getFunctionName(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "f"), 0); + + EXPECT_FALSE(Context.partialDemangle("_ZN1a1b1cIiiiEEvm")); + + Buffer = Context.getFunctionBaseName(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "c"), 0); + + Buffer = Context.getFunctionContextName(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "a::b"), 0); + + Buffer = Context.getFunctionName(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "a::b::c"), 0); + + Buffer = Context.getFunctionParameters(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "(unsigned long)"), 0); + + Buffer = Context.finishDemangle(Buffer, &Size); + EXPECT_EQ(strcmp(Buffer, "void a::b::c(unsigned long)"), 0); + + EXPECT_FALSE(Context.isProvablyMemberFunction()); + EXPECT_TRUE(Context.isFunction()); + EXPECT_FALSE(Context.isSpecialName()); + + std::free(Buffer); +}