Index: llvm/include/llvm/Demangle/Demangle.h =================================================================== --- llvm/include/llvm/Demangle/Demangle.h +++ llvm/include/llvm/Demangle/Demangle.h @@ -25,4 +25,57 @@ 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; + char *getFunctionReturnType(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 @@ -96,6 +96,12 @@ public: OutputStream(char *StartBuf, size_t Size) : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} + OutputStream() = default; + void reset(char *Buffer_, size_t BufferCapacity_) { + CurrentPosition = 0; + Buffer = Buffer_; + BufferCapacity = BufferCapacity_; + } /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. @@ -176,7 +182,8 @@ KSpecialName, KCtorVtableSpecialName, KQualifiedName, - KEmptyName, + KNestedName, + KLocalName, KVectorType, KParameterPack, KTemplateArgumentPack, @@ -462,11 +469,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_) {} @@ -796,8 +803,8 @@ }; class FunctionEncoding final : public Node { - const Node *Ret; - const Node *Name; + Node *Ret; + Node *Name; NodeArray Params; Node *Attrs; Qualifiers CVQuals; @@ -812,6 +819,11 @@ 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; } + Node *getReturnType() const { return Ret; } + bool hasRHSComponentSlow(OutputStream &) const override { return true; } bool hasFunctionSlow(OutputStream &) const override { return true; } @@ -893,6 +905,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; @@ -911,12 +953,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; @@ -1140,12 +1176,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_) {} @@ -1172,10 +1207,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(); } @@ -1879,14 +1913,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 @@ -2034,6 +2071,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)...); @@ -2228,7 +2277,7 @@ if (consumeIf('s')) { First = parse_discriminator(First, Last); - return make(Encoding, make("string literal")); + return make(Encoding, make("string literal")); } if (consumeIf('d')) { @@ -2238,14 +2287,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); } // ::= @@ -2701,7 +2750,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; }; @@ -4983,6 +5032,22 @@ return nullptr; return Ty; } + +bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S, + size_t InitSize) { + size_t BufferSize; + if (Buf == nullptr) { + Buf = static_cast(std::malloc(InitSize)); + if (Buf == nullptr) + return true; + BufferSize = InitSize; + } else + BufferSize = *N; + + S.reset(Buf, BufferSize); + return false; +} + } // unnamed namespace enum { @@ -5001,36 +5066,217 @@ return nullptr; } - size_t BufSize = Buf != nullptr ? *N : 0; int InternalStatus = success; - size_t MangledNameLength = std::strlen(MangledName); + Db Parser(MangledName, MangledName + std::strlen(MangledName)); + OutputStream S; - Db Parser(MangledName, MangledName + MangledNameLength); Node *AST = Parser.parse(); if (AST == nullptr) InternalStatus = invalid_mangled_name; - - if (InternalStatus == success) { + else if (initializeOutputStream(Buf, N, S, 1024)) + InternalStatus = memory_alloc_failure; + else { assert(Parser.ForwardTemplateRefs.empty()); - - if (Buf == nullptr) { - BufSize = 1024; - Buf = static_cast(std::malloc(BufSize)); - } - - if (Buf) { - OutputStream Stream(Buf, BufSize); - AST->print(Stream); - Stream += '\0'; - if (N != nullptr) - *N = Stream.getCurrentPosition(); - Buf = Stream.getBuffer(); - } else - InternalStatus = memory_alloc_failure; + AST->print(S); + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + Buf = S.getBuffer(); } if (Status) *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) + : RootNode(Other.RootNode), Context(Other.Context) { + Other.Context = Other.RootNode = nullptr; +} + +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) { + OutputStream S; + if (initializeOutputStream(Buf, N, S, 128)) + return nullptr; + 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); + } + } +} + +char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + Node *Name = static_cast(RootNode)->getName(); + + OutputStream S; + if (initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + KeepGoingLocalFunction: + 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"; + break; + case Node::KNestedName: + static_cast(Name)->Qual->print(S); + break; + case Node::KLocalName: { + auto *LN = static_cast(Name); + LN->Encoding->print(S); + S += "::"; + Name = LN->Entity; + goto KeepGoingLocalFunction; + } + default: + break; + } + 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 *Name = static_cast(RootNode)->getName(); + return printNode(Name, Buf, N); +} + +char *ItaniumPartialDemangler::getFunctionParameters(char *Buf, + size_t *N) const { + if (!isFunction()) + return nullptr; + NodeArray Params = static_cast(RootNode)->getParams(); + + OutputStream S; + if (initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + S += '('; + Params.printWithComma(S); + S += ')'; + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.getBuffer(); +} + +char *ItaniumPartialDemangler::getFunctionReturnType( + char *Buf, size_t *N) const { + if (!isFunction()) + return nullptr; + + OutputStream S; + if (initializeOutputStream(Buf, N, S, 128)) + return nullptr; + + if (Node *Ret = static_cast(RootNode)->getReturnType()) + Ret->print(S); + + S += '\0'; + if (N != nullptr) + *N = S.getCurrentPosition(); + return S.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,121 @@ +//===----------------------- 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 +#include "llvm/Demangle/Demangle.h" +#include "gtest/gtest.h" + +struct ChoppedName { + const char *Mangled; + const char *ContextName, *BaseName, *ReturnType, *Params; +}; + +static ChoppedName NamesToTest[] = { + {"_Z1fv", "", "f", "", "()"}, + {"_ZN1a1b1cIiiiEEvm", "a::b", "c", "void", "(unsigned long)"}, + {"_ZZ5OuterIiEivEN5Inner12inner_memberEv", + "int Outer()::Inner", "inner_member", "", "()"}, + {"_Z1fIiEPFvvEv", "", "f", "void (*)()", "()"}, + {"_ZN1S1fIiEEvv", "S", "f", "void", "()"}, + + // Call operator for a lambda in f(). + {"_ZZ1fvENK3$_0clEi", "f()::$_0", "operator()", "", "(int)"}, + + // A call operator for a lambda in a lambda in f(). + {"_ZZZ1fvENK3$_0clEvENKUlvE_clEv", + "f()::$_0::operator()() const::'lambda'()", "operator()", "", "()"}, + + {"_ZZN1S1fEiiEd0_NKUlvE_clEv", + "S::f(int, int)::'lambda'()", "operator()", "", "()"}, + + {"_ZN1Scv7MuncherIJDpPT_EEIJFivEA_iEEEv", + "S", "operator Muncher", "", "()"}, + + // Attributes. + {"_ZN5test4IdE1fEUa9enable_ifIXeqfL0p_Li1EEXeqfL0p0_Li2EEEi", + "test4", "f", "", "(int)"}, + {"_ZN1SC2B8ctor_tagEv", "S", "S", "", "()"}, + {"_ZN1S1fB4MERPIiEEvv", "S", "f", "void", "()"}, + + {"_ZNSsC1EmcRKSaIcE", + "std::basic_string, std::allocator >", + "basic_string", "", "(unsigned long, char, std::allocator const&)"}, + {"_ZNSsixEm", "std::string", "operator[]", "", "(unsigned long)"}, + {"_ZSt17__throw_bad_allocv", "std", "__throw_bad_alloc", "", "()"}, + + {"_ZN1AI1BEC2Ev", "A", "A", "", "()"}, + {"_ZN1AI1BED2Ev", "A", "~A", "", "()"}, + {"_ZN1AI1BECI24BaseEi", "A", "A", "", "(int)"}, + {"_ZNKR1AI1BE1fIiEEiv", "A", "f", "int", "()"}, + + {"_ZN1SIJicfEE3mfnIJjcdEEEvicfDpT_", "S", + "mfn", "void", "(int, char, float, unsigned int, char, double)"}, +}; + +TEST(PartialDemangleTest, TestNameChopping) { + size_t Size = 1; + char *Buf = static_cast(std::malloc(Size)); + + llvm::ItaniumPartialDemangler D; + + for (ChoppedName &N : NamesToTest) { + EXPECT_FALSE(D.partialDemangle(N.Mangled)); + EXPECT_TRUE(D.isFunction()); + EXPECT_FALSE(D.isData()); + EXPECT_FALSE(D.isSpecialName()); + + Buf = D.getFunctionDeclContextName(Buf, &Size); + EXPECT_STREQ(Buf, N.ContextName); + + Buf = D.getFunctionBaseName(Buf, &Size); + EXPECT_STREQ(Buf, N.BaseName); + + Buf = D.getFunctionReturnType(Buf, &Size); + EXPECT_STREQ(Buf, N.ReturnType); + + Buf = D.getFunctionParameters(Buf, &Size); + EXPECT_STREQ(Buf, N.Params); + } +} + +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()); + + EXPECT_FALSE(Demangler.partialDemangle("_ZN1aDC1a1b1cEE")); + EXPECT_FALSE(Demangler.isFunction()); + EXPECT_FALSE(Demangler.isSpecialName()); + EXPECT_TRUE(Demangler.isData()); +} + +TEST(PartialDemanglerTest, TestMisc) { + llvm::ItaniumPartialDemangler D1, D2; + + EXPECT_FALSE(D1.partialDemangle("_Z1fv")); + EXPECT_FALSE(D2.partialDemangle("_Z1g")); + std::swap(D1, D2); + EXPECT_FALSE(D1.isFunction()); + EXPECT_TRUE(D2.isFunction()); + + EXPECT_TRUE(D1.partialDemangle("Not a mangled name!")); + +}