Index: llvm/include/llvm/Demangle/Demangle.h =================================================================== --- llvm/include/llvm/Demangle/Demangle.h +++ llvm/include/llvm/Demangle/Demangle.h @@ -25,4 +25,56 @@ 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); + + /// 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 *getFunctionDeclContextName(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 has any any cv or reference qualifiers. These imply that + /// the function is a non-static member function. + bool hasFunctionQualifiers() const; + + /// If this symbol describes a function. + bool isFunction() const; + + /// If this symbol describes a variable. + bool isData() const; + + /// If this symbol is a . These are generally implicitly + /// generated by the implementation, such as vtables and typeinfo names. + bool isSpecialName() const; + + ~ItaniumPartialDemangler(); +private: + void *RootNode; + void *Context; +}; +} // namespace llvm Index: llvm/lib/Demangle/ItaniumDemangle.cpp =================================================================== --- llvm/lib/Demangle/ItaniumDemangle.cpp +++ llvm/lib/Demangle/ItaniumDemangle.cpp @@ -176,7 +176,8 @@ KSpecialName, KCtorVtableSpecialName, KQualifiedName, - KEmptyName, + KNestedName, + KLocalName, KVectorType, KParameterPack, KTemplateArgumentPack, @@ -454,11 +455,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_->RHSComponentCache, Base_->ArrayCache, Base_->FunctionCache), Base(Base_), Tag(Tag_) {} @@ -804,6 +805,10 @@ Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_), CVQuals(CVQuals_), RefQual(RefQual_) {} + 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; } @@ -885,6 +890,36 @@ } }; +struct NestedName : Node { + Node *Qual; + Node *Name; + + NestedName(Node *Qual_, Node *Name_) + : Node(KNestedName), Qual(Qual_), Name(Name_) {} + + StringView getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputStream &S) const override { + Qual->print(S); + S += "::"; + Name->print(S); + } +}; + +struct LocalName : Node { + Node *Encoding; + Node *Entity; + + LocalName(Node *Encoding_, Node *Entity_) + : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {} + + void printLeft(OutputStream &S) const override { + Encoding->print(S); + S += "::"; + Entity->print(S); + } +}; + class QualifiedName final : public Node { // qualifier::name const Node *Qualifier; @@ -903,12 +938,6 @@ } }; -class EmptyName : public Node { -public: - EmptyName() : Node(KEmptyName) {} - void printLeft(OutputStream &) const override {} -}; - class VectorType final : public Node { const Node *BaseType; const NodeOrString Dimension; @@ -1132,12 +1161,11 @@ } }; -class NameWithTemplateArgs final : public Node { +struct NameWithTemplateArgs : Node { // name Node *Name; Node *TemplateArgs; -public: NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_) : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {} @@ -1164,10 +1192,9 @@ } }; -class StdQualifiedName final : public Node { +struct StdQualifiedName : Node { Node *Child; -public: StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} StringView getBaseName() const override { return Child->getBaseName(); } @@ -1822,14 +1849,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 @@ -1977,6 +2007,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(); + ParsingLambdaParams = false; + TryToParseTemplateArgs = true; + PermitForwardTemplateReferences = false; + ASTAllocator.reset(); + } + template T *make(Args &&... args) { return new (ASTAllocator.allocate(sizeof(T))) T(std::forward(args)...); @@ -2170,7 +2212,7 @@ if (consumeIf('s')) { First = parse_discriminator(First, Last); - return make(Encoding, make("string literal")); + return make(Encoding, make("string literal")); } if (consumeIf('d')) { @@ -2180,14 +2222,14 @@ Node *N = parseName(State); if (N == nullptr) return nullptr; - return make(Encoding, N); + return make(Encoding, N); } Node *Entity = parseName(State); if (Entity == nullptr) return nullptr; First = parse_discriminator(First, Last); - return make(Encoding, Entity); + return make(Encoding, Entity); } // ::= @@ -2641,7 +2683,7 @@ Node *SoFar = nullptr; auto PushComponent = [&](Node *Comp) { - if (SoFar) SoFar = make(SoFar, Comp); + if (SoFar) SoFar = make(SoFar, Comp); else SoFar = Comp; if (State) State->EndsWithTemplateArgs = false; }; @@ -4879,3 +4921,199 @@ *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(); + return RootNode == nullptr; +} + +static char *printNode(Node *RootNode, char *Buf, size_t *N) { + size_t BufSize; + if (Buf == nullptr) { + BufSize = 1024; + Buf = static_cast(std::malloc(BufSize)); + 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(); +} + +char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + Node *Name = static_cast(RootNode)->getName(); + + while (true) { + switch (Name->getKind()) { + case Node::KAbiTagAttr: + Name = static_cast(Name)->Base; + continue; + case Node::KStdQualifiedName: + Name = static_cast(Name)->Child; + continue; + case Node::KNestedName: + Name = static_cast(Name)->Name; + continue; + case Node::KLocalName: + Name = static_cast(Name)->Entity; + continue; + case Node::KNameWithTemplateArgs: + Name = static_cast(Name)->Name; + continue; + default: + return printNode(Name, Buf, N); + } + } +} + +static bool printContextName(Node *Name, OutputStream &S) { + while (true) { + if (Name->getKind() == Node::KAbiTagAttr) { + Name = static_cast(Name)->Base; + continue; + } + if (Name->getKind() == Node::KNameWithTemplateArgs) { + Name = static_cast(Name)->Name; + continue; + } + break; + } + + switch (Name->getKind()) { + case Node::KStdQualifiedName: + S += "std"; + return false; + case Node::KNestedName: + static_cast(Name)->Qual->print(S); + return false; + case Node::KLocalName: { + auto *LN = static_cast(Name); + LN->Encoding->print(S); + S += "::"; + return printContextName(LN->Entity, S); + } + default: + return true; + } +} + +char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + Node *Name = static_cast(RootNode)->getName(); + + size_t BufSize; + if (Buf == nullptr) { + Buf = static_cast(std::malloc(128)); + BufSize = 128; + if (Buf == nullptr) + return nullptr; + } else + BufSize = *N; + OutputStream S(Buf, BufSize); + if (printContextName(Name, S)) + return nullptr; + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +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 (Buf == nullptr) { + Size = 128; + Buf = static_cast(std::malloc(Size)); + if (Buf == nullptr) + return nullptr; + } 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::hasFunctionQualifiers() 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; +} + +bool ItaniumPartialDemangler::isData() const { + return !isFunction() && !isSpecialName(); +} + +} 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,109 @@ +//===----------------------- 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_STREQ(Buffer, "f"); + + Buffer = Context.getFunctionParameters(Buffer, &Size); + EXPECT_STREQ(Buffer, "()"); + + Buffer = Context.getFunctionName(Buffer, &Size); + EXPECT_STREQ(Buffer, "f"); + + EXPECT_FALSE(Context.partialDemangle("_ZN1a1b1cIiiiEEvm")); + + Buffer = Context.getFunctionBaseName(Buffer, &Size); + EXPECT_STREQ(Buffer, "c"); + + Buffer = Context.getFunctionDeclContextName(Buffer, &Size); + EXPECT_STREQ(Buffer, "a::b"); + + Buffer = Context.getFunctionName(Buffer, &Size); + EXPECT_STREQ(Buffer, "a::b::c"); + + Buffer = Context.getFunctionParameters(Buffer, &Size); + EXPECT_STREQ(Buffer, "(unsigned long)"); + + Buffer = Context.finishDemangle(Buffer, &Size); + EXPECT_STREQ(Buffer, "void a::b::c(unsigned long)"); + + EXPECT_FALSE(Context.hasFunctionQualifiers()); + EXPECT_TRUE(Context.isFunction()); + EXPECT_FALSE(Context.isSpecialName()); + + std::free(Buffer); +} + +TEST(PartialDemangleTest, TestFindBaseNames) { + llvm::ItaniumPartialDemangler Context; + size_t Size = 4; + char *Buffer = static_cast(std::malloc(Size)); + + auto CheckBaseName = [&](const char *Mangled, const char *ExpectedBaseName) { + EXPECT_FALSE(Context.partialDemangle(Mangled)); + EXPECT_TRUE(Context.isFunction()); + Buffer = Context.getFunctionBaseName(Buffer, &Size); + EXPECT_STREQ(ExpectedBaseName, Buffer); + }; + + CheckBaseName("_ZN1S1fEv", "f"); + // Strip away template params. + CheckBaseName("_ZN1S1fIiEEvv", "f"); + // Ignore abi_tag. + CheckBaseName("_ZN1S1fB4MERPIiEEvv", "f"); + // Function local member function. + CheckBaseName("_ZZ5OuterIiEivEN5Inner12inner_memberEv", "inner_member"); +} + +TEST(PartialDemangleTest, TestFindContextNames) { + llvm::ItaniumPartialDemangler Context; + size_t Size = 4; + char *Buffer = static_cast(std::malloc(Size)); + + auto CheckContext = [&](const char *Mangled, const char *Expected) { + EXPECT_FALSE(Context.partialDemangle(Mangled)); + EXPECT_TRUE(Context.isFunction()); + Buffer = Context.getFunctionDeclContextName(Buffer, &Size); + EXPECT_STREQ(Expected, Buffer); + }; + + CheckContext("_ZN1S1fEv", "S"); + // Function local member function. + CheckContext("_ZZ5OuterIiEivEN5Inner12inner_memberEv", "int Outer()::Inner"); + CheckContext("_ZN1fIiE1gEv", "f"); +} + +TEST(PartialDemangleTest, TestNameMeta) { + llvm::ItaniumPartialDemangler Demangler; + + EXPECT_FALSE(Demangler.partialDemangle("_ZNK1f1gEv")); + EXPECT_TRUE(Demangler.isFunction()); + EXPECT_TRUE(Demangler.hasFunctionQualifiers()); + EXPECT_FALSE(Demangler.isSpecialName()); + EXPECT_FALSE(Demangler.isData()); + + EXPECT_FALSE(Demangler.partialDemangle("_Z1fv")); + EXPECT_FALSE(Demangler.hasFunctionQualifiers()); + + EXPECT_FALSE(Demangler.partialDemangle("_ZTV1S")); + EXPECT_TRUE(Demangler.isSpecialName()); + EXPECT_FALSE(Demangler.isData()); + EXPECT_FALSE(Demangler.isFunction()); +}