Index: llvm/trunk/include/llvm/Demangle/Demangle.h =================================================================== --- llvm/trunk/include/llvm/Demangle/Demangle.h +++ llvm/trunk/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/trunk/lib/Demangle/ItaniumDemangle.cpp =================================================================== --- llvm/trunk/lib/Demangle/ItaniumDemangle.cpp +++ llvm/trunk/lib/Demangle/ItaniumDemangle.cpp @@ -5089,3 +5089,194 @@ *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/trunk/unittests/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/CMakeLists.txt +++ llvm/trunk/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/trunk/unittests/Demangle/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/Demangle/CMakeLists.txt +++ llvm/trunk/unittests/Demangle/CMakeLists.txt @@ -0,0 +1,7 @@ +set(DemangleSources + PartialDemangleTest.cpp +) + +add_llvm_unittest(DemangleTests + ${DemangleSources} +) Index: llvm/trunk/unittests/Demangle/PartialDemangleTest.cpp =================================================================== --- llvm/trunk/unittests/Demangle/PartialDemangleTest.cpp +++ llvm/trunk/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!")); + +}