Implement a simple demangler.
The purpose of this demangler is to avoid crashes for any string.
- It does one pass and should avoid going past the end of the string.
- The code avoids recursion to minimize the amount of stack required.
- It cannot demangle all mangled names, but it should be able to work
on nearly all names in normal stack traces.
- If the mangled name is too large, it will stop demangling and return
as if the name is not a demangled name.
Test: Passes new unit tests.
Change-Id: I596f74a533c0e093d1517c6bd11cced07009d321
diff --git a/demangle/Android.bp b/demangle/Android.bp
new file mode 100644
index 0000000..e80cdc5
--- /dev/null
+++ b/demangle/Android.bp
@@ -0,0 +1,65 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libdemangle_defaults",
+
+ host_supported: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
+cc_library {
+ name: "libdemangle",
+ defaults: ["libdemangle_defaults"],
+
+ srcs: [
+ "Demangler.cpp",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+}
+
+//-------------------------------------------------------------------------
+// Unit Tests
+//-------------------------------------------------------------------------
+cc_test {
+ name: "libdemangle_test",
+ defaults: ["libdemangle_defaults"],
+
+ srcs: [
+ "DemangleTest.cpp",
+ ],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libdemangle",
+ ],
+}
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
new file mode 100644
index 0000000..fb68119
--- /dev/null
+++ b/demangle/DemangleTest.cpp
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+
+#include <demangle.h>
+
+#include "Demangler.h"
+
+TEST(DemangleTest, VoidArgumentTest) {
+ Demangler demangler;
+
+ ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
+ ASSERT_EQ("func(void&)", demangler.Parse("_ZN4funcERv"));
+ ASSERT_EQ("func(void, void)", demangler.Parse("_ZN4funcEvv"));
+ ASSERT_EQ("func(void*)", demangler.Parse("_ZN4funcEPv"));
+ ASSERT_EQ("func(void const)", demangler.Parse("_ZN4funcEKv"));
+ ASSERT_EQ("func(void volatile)", demangler.Parse("_ZN4funcEVv"));
+}
+
+TEST(DemangleTest, ArgumentModifiers) {
+ Demangler demangler;
+
+ ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
+ ASSERT_EQ("func(char*)", demangler.Parse("_ZN4funcEPc"));
+ ASSERT_EQ("func(char**)", demangler.Parse("_ZN4funcEPPc"));
+ ASSERT_EQ("func(char***)", demangler.Parse("_ZN4funcEPPPc"));
+ ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERc"));
+ ASSERT_EQ("func(char*&)", demangler.Parse("_ZN4funcERPc"));
+ ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERRc"));
+ ASSERT_EQ("func(char*&*)", demangler.Parse("_ZN4funcEPRPc"));
+ ASSERT_EQ("func(char**&)", demangler.Parse("_ZN4funcERRPPc"));
+ ASSERT_EQ("func(char const)", demangler.Parse("_ZN4funcEKc"));
+ ASSERT_EQ("func(char volatile)", demangler.Parse("_ZN4funcEVc"));
+ ASSERT_EQ("func(char volatile const)", demangler.Parse("_ZN4funcEKVc"));
+ ASSERT_EQ("func(char const volatile)", demangler.Parse("_ZN4funcEVKc"));
+ ASSERT_EQ("func(char const* volatile&)", demangler.Parse("_ZN4funcERVPKc"));
+ ASSERT_EQ("func(void, char, short)", demangler.Parse("_ZN4funcEvcs"));
+ ASSERT_EQ("func(void*, char&, short&*)", demangler.Parse("_ZN4funcEPvRcPRs"));
+}
+
+TEST(DemangleTest, FunctionModifiers) {
+ Demangler demangler;
+
+ ASSERT_EQ("func() const", demangler.Parse("_ZNK4funcEv"));
+ ASSERT_EQ("func() volatile", demangler.Parse("_ZNV4funcEv"));
+ ASSERT_EQ("func() volatile const", demangler.Parse("_ZNKV4funcEv"));
+ ASSERT_EQ("func() const volatile", demangler.Parse("_ZNVK4funcEv"));
+}
+
+TEST(DemangleTest, MultiplePartsInName) {
+ Demangler demangler;
+
+ ASSERT_EQ("one::two()", demangler.Parse("_ZN3one3twoEv"));
+ ASSERT_EQ("one::two::three()", demangler.Parse("_ZN3one3two5threeEv"));
+ ASSERT_EQ("one::two::three::four()", demangler.Parse("_ZN3one3two5three4fourEv"));
+ ASSERT_EQ("one::two::three::four::five()", demangler.Parse("_ZN3one3two5three4four4fiveEv"));
+ ASSERT_EQ("one(two::three::four::five)", demangler.Parse("_ZN3oneEN3two5three4four4fiveE"));
+}
+
+TEST(DemangleTest, AnonymousNamespace) {
+ Demangler demangler;
+
+ ASSERT_EQ("(anonymous namespace)::two()", demangler.Parse("_ZN12_GLOBAL__N_13twoEv"));
+ ASSERT_EQ("one::two((anonymous namespace))", demangler.Parse("_ZN3one3twoE12_GLOBAL__N_1"));
+}
+
+TEST(DemangleTest, DestructorValues) {
+ Demangler demangler;
+
+ ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD0Ev"));
+ ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD1Ev"));
+ ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD2Ev"));
+ ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD5Ev"));
+ ASSERT_EQ("one::two::three::~three()", demangler.Parse("_ZN3one3two5threeD0Ev"));
+
+ ASSERT_EQ("_ZN3one3twoD3Ev", demangler.Parse("_ZN3one3twoD3Ev"));
+ ASSERT_EQ("_ZN3one3twoD4Ev", demangler.Parse("_ZN3one3twoD4Ev"));
+ ASSERT_EQ("_ZN3one3twoD6Ev", demangler.Parse("_ZN3one3twoD6Ev"));
+ ASSERT_EQ("_ZN3one3twoD7Ev", demangler.Parse("_ZN3one3twoD7Ev"));
+ ASSERT_EQ("_ZN3one3twoD8Ev", demangler.Parse("_ZN3one3twoD8Ev"));
+ ASSERT_EQ("_ZN3one3twoD9Ev", demangler.Parse("_ZN3one3twoD9Ev"));
+
+ ASSERT_EQ("one::two<three::four>::~two()", demangler.Parse("_ZN3one3twoIN5three4fourEED2Ev"));
+}
+
+TEST(DemangleTest, ConstructorValues) {
+ Demangler demangler;
+
+ ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC1Ev"));
+ ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC2Ev"));
+ ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC3Ev"));
+ ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC5Ev"));
+ ASSERT_EQ("one::two::three::three()", demangler.Parse("_ZN3one3two5threeC1Ev"));
+
+ ASSERT_EQ("_ZN3one3twoC0Ev", demangler.Parse("_ZN3one3twoC0Ev"));
+ ASSERT_EQ("_ZN3one3twoC4Ev", demangler.Parse("_ZN3one3twoC4Ev"));
+ ASSERT_EQ("_ZN3one3twoC6Ev", demangler.Parse("_ZN3one3twoC6Ev"));
+ ASSERT_EQ("_ZN3one3twoC7Ev", demangler.Parse("_ZN3one3twoC7Ev"));
+ ASSERT_EQ("_ZN3one3twoC8Ev", demangler.Parse("_ZN3one3twoC8Ev"));
+ ASSERT_EQ("_ZN3one3twoC9Ev", demangler.Parse("_ZN3one3twoC9Ev"));
+
+ ASSERT_EQ("one::two<three::four>::two()", demangler.Parse("_ZN3one3twoIN5three4fourEEC1Ev"));
+}
+
+TEST(DemangleTest, OperatorValues) {
+ Demangler demangler;
+
+ ASSERT_EQ("operator&&()", demangler.Parse("_Zaav"));
+ ASSERT_EQ("operator&()", demangler.Parse("_Zadv"));
+ ASSERT_EQ("operator&()", demangler.Parse("_Zanv"));
+ ASSERT_EQ("operator&=()", demangler.Parse("_ZaNv"));
+ ASSERT_EQ("operator=()", demangler.Parse("_ZaSv"));
+ ASSERT_EQ("operator()()", demangler.Parse("_Zclv"));
+ ASSERT_EQ("operator,()", demangler.Parse("_Zcmv"));
+ ASSERT_EQ("operator~()", demangler.Parse("_Zcov"));
+ ASSERT_EQ("operator delete[]()", demangler.Parse("_Zdav"));
+ ASSERT_EQ("operator*()", demangler.Parse("_Zdev"));
+ ASSERT_EQ("operator delete()", demangler.Parse("_Zdlv"));
+ ASSERT_EQ("operator/()", demangler.Parse("_Zdvv"));
+ ASSERT_EQ("operator/=()", demangler.Parse("_ZdVv"));
+ ASSERT_EQ("operator^()", demangler.Parse("_Zeov"));
+ ASSERT_EQ("operator^=()", demangler.Parse("_ZeOv"));
+ ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
+ ASSERT_EQ("operator>=()", demangler.Parse("_Zgev"));
+ ASSERT_EQ("operator>()", demangler.Parse("_Zgtv"));
+ ASSERT_EQ("operator[]()", demangler.Parse("_Zixv"));
+ ASSERT_EQ("operator<=()", demangler.Parse("_Zlev"));
+ ASSERT_EQ("operator<<()", demangler.Parse("_Zlsv"));
+ ASSERT_EQ("operator<<=()", demangler.Parse("_ZlSv"));
+ ASSERT_EQ("operator<()", demangler.Parse("_Zltv"));
+ ASSERT_EQ("operator-()", demangler.Parse("_Zmiv"));
+ ASSERT_EQ("operator-=()", demangler.Parse("_ZmIv"));
+ ASSERT_EQ("operator*()", demangler.Parse("_Zmlv"));
+ ASSERT_EQ("operator*=()", demangler.Parse("_ZmLv"));
+ ASSERT_EQ("operator--()", demangler.Parse("_Zmmv"));
+ ASSERT_EQ("operator new[]()", demangler.Parse("_Znav"));
+ ASSERT_EQ("operator!=()", demangler.Parse("_Znev"));
+ ASSERT_EQ("operator-()", demangler.Parse("_Zngv"));
+ ASSERT_EQ("operator!()", demangler.Parse("_Zntv"));
+ ASSERT_EQ("operator new()", demangler.Parse("_Znwv"));
+ ASSERT_EQ("operator||()", demangler.Parse("_Zoov"));
+ ASSERT_EQ("operator|()", demangler.Parse("_Zorv"));
+ ASSERT_EQ("operator|=()", demangler.Parse("_ZoRv"));
+ ASSERT_EQ("operator->*()", demangler.Parse("_Zpmv"));
+ ASSERT_EQ("operator+()", demangler.Parse("_Zplv"));
+ ASSERT_EQ("operator+=()", demangler.Parse("_ZpLv"));
+ ASSERT_EQ("operator++()", demangler.Parse("_Zppv"));
+ ASSERT_EQ("operator+()", demangler.Parse("_Zpsv"));
+ ASSERT_EQ("operator->()", demangler.Parse("_Zptv"));
+ ASSERT_EQ("operator?()", demangler.Parse("_Zquv"));
+ ASSERT_EQ("operator%()", demangler.Parse("_Zrmv"));
+ ASSERT_EQ("operator%=()", demangler.Parse("_ZrMv"));
+ ASSERT_EQ("operator>>()", demangler.Parse("_Zrsv"));
+ ASSERT_EQ("operator>>=()", demangler.Parse("_ZrSv"));
+
+ // Spot check using an operator as part of function name.
+ ASSERT_EQ("operator&&()", demangler.Parse("_ZNaaEv"));
+ ASSERT_EQ("operator++()", demangler.Parse("_ZNppEv"));
+ ASSERT_EQ("one::operator++()", demangler.Parse("_ZN3oneppEv"));
+
+ // Spot check using an operator in an argument name.
+ ASSERT_EQ("operator+(operator|=)", demangler.Parse("_ZNpsENoRE"));
+ ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
+ ASSERT_EQ("one(arg1::operator|=, arg2::operator==)",
+ demangler.Parse("_ZN3oneEN4arg1oREN4arg2eqE"));
+}
+
+TEST(DemangleTest, FunctionStartsWithNumber) {
+ Demangler demangler;
+
+ ASSERT_EQ("value(char, int)", demangler.Parse("_Z5valueci"));
+ ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_Z11abcdefjklmna"));
+ ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
+}
+
+TEST(DemangleTest, StdTypes) {
+ Demangler demangler;
+
+ ASSERT_EQ("std::one", demangler.Parse("_ZNSt3oneE"));
+ ASSERT_EQ("std::one(std::two)", demangler.Parse("_ZNSt3oneESt3two"));
+ ASSERT_EQ("std::std::one(std::two)", demangler.Parse("_ZNStSt3oneESt3two"));
+ ASSERT_EQ("std()", demangler.Parse("_ZNStEv"));
+ ASSERT_EQ("one::std::std::two::~two(one::std::std::two)",
+ demangler.Parse("_ZN3oneStSt3twoD0ES0_"));
+
+ ASSERT_EQ("std::allocator", demangler.Parse("_ZNSaE"));
+ ASSERT_EQ("std::basic_string", demangler.Parse("_ZNSbE"));
+ ASSERT_EQ("_ZNScE", demangler.Parse("_ZNScE"));
+ ASSERT_EQ("std::iostream", demangler.Parse("_ZNSdE"));
+ ASSERT_EQ("_ZNSeE", demangler.Parse("_ZNSeE"));
+ ASSERT_EQ("_ZNSfE", demangler.Parse("_ZNSfE"));
+ ASSERT_EQ("_ZNSgE", demangler.Parse("_ZNSgE"));
+ ASSERT_EQ("_ZNShE", demangler.Parse("_ZNShE"));
+ ASSERT_EQ("std::istream", demangler.Parse("_ZNSiE"));
+ ASSERT_EQ("_ZNSjE", demangler.Parse("_ZNSjE"));
+ ASSERT_EQ("_ZNSkE", demangler.Parse("_ZNSkE"));
+ ASSERT_EQ("_ZNSlE", demangler.Parse("_ZNSlE"));
+ ASSERT_EQ("_ZNSmE", demangler.Parse("_ZNSmE"));
+ ASSERT_EQ("_ZNSnE", demangler.Parse("_ZNSnE"));
+ ASSERT_EQ("std::ostream", demangler.Parse("_ZNSoE"));
+ ASSERT_EQ("_ZNSpE", demangler.Parse("_ZNSpE"));
+ ASSERT_EQ("_ZNSqE", demangler.Parse("_ZNSqE"));
+ ASSERT_EQ("_ZNSrE", demangler.Parse("_ZNSrE"));
+ ASSERT_EQ("std::string", demangler.Parse("_ZNSsE"));
+ ASSERT_EQ("_ZNSuE", demangler.Parse("_ZNSuE"));
+ ASSERT_EQ("_ZNSvE", demangler.Parse("_ZNSvE"));
+ ASSERT_EQ("_ZNSwE", demangler.Parse("_ZNSwE"));
+ ASSERT_EQ("_ZNSxE", demangler.Parse("_ZNSxE"));
+ ASSERT_EQ("_ZNSyE", demangler.Parse("_ZNSyE"));
+ ASSERT_EQ("_ZNSzE", demangler.Parse("_ZNSzE"));
+}
+
+TEST(DemangleTest, SingleLetterArguments) {
+ Demangler demangler;
+
+ ASSERT_EQ("func(signed char)", demangler.Parse("_ZN4funcEa"));
+ ASSERT_EQ("func(bool)", demangler.Parse("_ZN4funcEb"));
+ ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
+ ASSERT_EQ("func(double)", demangler.Parse("_ZN4funcEd"));
+ ASSERT_EQ("func(long double)", demangler.Parse("_ZN4funcEe"));
+ ASSERT_EQ("func(float)", demangler.Parse("_ZN4funcEf"));
+ ASSERT_EQ("func(__float128)", demangler.Parse("_ZN4funcEg"));
+ ASSERT_EQ("func(unsigned char)", demangler.Parse("_ZN4funcEh"));
+ ASSERT_EQ("func(int)", demangler.Parse("_ZN4funcEi"));
+ ASSERT_EQ("func(unsigned int)", demangler.Parse("_ZN4funcEj"));
+ ASSERT_EQ("_ZN4funcEk", demangler.Parse("_ZN4funcEk"));
+ ASSERT_EQ("func(long)", demangler.Parse("_ZN4funcEl"));
+ ASSERT_EQ("func(unsigned long)", demangler.Parse("_ZN4funcEm"));
+ ASSERT_EQ("func(__int128)", demangler.Parse("_ZN4funcEn"));
+ ASSERT_EQ("func(unsigned __int128)", demangler.Parse("_ZN4funcEo"));
+ ASSERT_EQ("_ZN4funcEp", demangler.Parse("_ZN4funcEp"));
+ ASSERT_EQ("_ZN4funcEq", demangler.Parse("_ZN4funcEq"));
+ ASSERT_EQ("_ZN4funcEr", demangler.Parse("_ZN4funcEr"));
+ ASSERT_EQ("func(short)", demangler.Parse("_ZN4funcEs"));
+ ASSERT_EQ("func(unsigned short)", demangler.Parse("_ZN4funcEt"));
+ ASSERT_EQ("_ZN4funcEu", demangler.Parse("_ZN4funcEu"));
+ ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
+ ASSERT_EQ("func(wchar_t)", demangler.Parse("_ZN4funcEw"));
+ ASSERT_EQ("func(long long)", demangler.Parse("_ZN4funcEx"));
+ ASSERT_EQ("func(unsigned long long)", demangler.Parse("_ZN4funcEy"));
+ ASSERT_EQ("func(...)", demangler.Parse("_ZN4funcEz"));
+}
+
+TEST(DemangleTest, DArguments) {
+ Demangler demangler;
+
+ ASSERT_EQ("func(auto)", demangler.Parse("_ZN4funcEDa"));
+ ASSERT_EQ("_ZN4funcEDb", demangler.Parse("_ZN4funcEDb"));
+ ASSERT_EQ("_ZN4funcEDc", demangler.Parse("_ZN4funcEDc"));
+ ASSERT_EQ("func(decimal64)", demangler.Parse("_ZN4funcEDd"));
+ ASSERT_EQ("func(decimal128)", demangler.Parse("_ZN4funcEDe"));
+ ASSERT_EQ("func(decimal32)", demangler.Parse("_ZN4funcEDf"));
+ ASSERT_EQ("_ZN4funcEDg", demangler.Parse("_ZN4funcEDg"));
+ ASSERT_EQ("func(half)", demangler.Parse("_ZN4funcEDh"));
+ ASSERT_EQ("func(char32_t)", demangler.Parse("_ZN4funcEDi"));
+ ASSERT_EQ("_ZN4funcEDj", demangler.Parse("_ZN4funcEDj"));
+ ASSERT_EQ("_ZN4funcEDk", demangler.Parse("_ZN4funcEDk"));
+ ASSERT_EQ("_ZN4funcEDl", demangler.Parse("_ZN4funcEDl"));
+ ASSERT_EQ("_ZN4funcEDm", demangler.Parse("_ZN4funcEDm"));
+ ASSERT_EQ("func(decltype(nullptr))", demangler.Parse("_ZN4funcEDn"));
+ ASSERT_EQ("_ZN4funcEDo", demangler.Parse("_ZN4funcEDo"));
+ ASSERT_EQ("_ZN4funcEDp", demangler.Parse("_ZN4funcEDp"));
+ ASSERT_EQ("_ZN4funcEDq", demangler.Parse("_ZN4funcEDq"));
+ ASSERT_EQ("_ZN4funcEDr", demangler.Parse("_ZN4funcEDr"));
+ ASSERT_EQ("func(char16_t)", demangler.Parse("_ZN4funcEDs"));
+ ASSERT_EQ("_ZN4funcEDt", demangler.Parse("_ZN4funcEDt"));
+ ASSERT_EQ("_ZN4funcEDu", demangler.Parse("_ZN4funcEDu"));
+ ASSERT_EQ("_ZN4funcEDv", demangler.Parse("_ZN4funcEDv"));
+ ASSERT_EQ("_ZN4funcEDw", demangler.Parse("_ZN4funcEDw"));
+ ASSERT_EQ("_ZN4funcEDx", demangler.Parse("_ZN4funcEDx"));
+ ASSERT_EQ("_ZN4funcEDy", demangler.Parse("_ZN4funcEDy"));
+ ASSERT_EQ("_ZN4funcEDz", demangler.Parse("_ZN4funcEDz"));
+}
+
+TEST(DemangleTest, FunctionArguments) {
+ Demangler demangler;
+
+ ASSERT_EQ("func(char ())", demangler.Parse("_ZN4funcEFcvE"));
+ ASSERT_EQ("func(char (*)())", demangler.Parse("_ZN4funcEPFcvE"));
+ ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
+ ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
+ ASSERT_EQ("func(char (*&)())", demangler.Parse("_ZN4funcERPFcvE"));
+ ASSERT_EQ("func(char (*)(int) const)", demangler.Parse("_ZN4funcEPKFciE"));
+ ASSERT_EQ("func(char (&)() const)", demangler.Parse("_ZN4funcERKFcvE"));
+ ASSERT_EQ("func(char (&)() volatile)", demangler.Parse("_ZN4funcERVFcvE"));
+ ASSERT_EQ("func(char (&)() volatile const)", demangler.Parse("_ZN4funcERKVFcvE"));
+ ASSERT_EQ("func(char (&)() const volatile)", demangler.Parse("_ZN4funcERVKFcvE"));
+ ASSERT_EQ("func(char (&)(int, signed char) const)", demangler.Parse("_ZN4funcERKFciaE"));
+ ASSERT_EQ("fake(char (&* volatile const)(void, void, signed char), signed char)",
+ demangler.Parse("_ZN4fakeEKVPRFcvvaEa"));
+}
+
+TEST(DemangleTest, TemplateFunction) {
+ Demangler demangler;
+
+ ASSERT_EQ("one<char>", demangler.Parse("_ZN3oneIcEE"));
+ ASSERT_EQ("one<void>", demangler.Parse("_ZN3oneIvEE"));
+ ASSERT_EQ("one<void*>", demangler.Parse("_ZN3oneIPvEE"));
+ ASSERT_EQ("one<void const>", demangler.Parse("_ZN3oneIKvEE"));
+ ASSERT_EQ("one<char, int, bool>", demangler.Parse("_ZN3oneIcibEE"));
+ ASSERT_EQ("one::two<three>", demangler.Parse("_ZN3one3twoIN5threeEEE"));
+ ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_ZN3oneIciN3two5threeEEE"));
+ // Template within templates.
+ ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
+ ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
+}
+
+TEST(DemangleTest, TemplateArguments) {
+ Demangler demangler;
+
+ ASSERT_EQ("one(two<char>)", demangler.Parse("_ZN3oneE3twoIcE"));
+ ASSERT_EQ("one(two<char, void>)", demangler.Parse("_ZN3oneE3twoIcvE"));
+ ASSERT_EQ("one(two<char, void, three<four, int>>)",
+ demangler.Parse("_ZN3oneE3twoIcv5threeI4fouriEE"));
+}
+
+TEST(DemangleTest, SubstitutionUnderscore) {
+ Demangler demangler;
+
+ ASSERT_EQ("a::a", demangler.Parse("_ZN1aS_E"));
+ ASSERT_EQ("one::one", demangler.Parse("_ZN3oneS_E"));
+ ASSERT_EQ("one::two::one", demangler.Parse("_ZN3one3twoS_E"));
+ ASSERT_EQ("one::two::three::one", demangler.Parse("_ZN3one3two5threeS_E"));
+ ASSERT_EQ("one::two(one)", demangler.Parse("_ZN3one3twoES_"));
+ ASSERT_EQ("one::two(three::one)", demangler.Parse("_ZN3one3twoEN5threeS_E"));
+
+ // Special case that St is part of the saved value used in the substitution.
+ ASSERT_EQ("std::one::std::one", demangler.Parse("_ZNSt3oneS_E"));
+
+ // Multiple substitutions in the string.
+ ASSERT_EQ("one::one(one, one)", demangler.Parse("_ZN3oneS_ES_S_"));
+ ASSERT_EQ("std::one::two::std::one(std::one)", demangler.Parse("_ZNSt3one3twoS_ES_"));
+}
+
+TEST(DemangleTest, SubstitutionByNumber) {
+ Demangler demangler;
+
+ // Basic substitution.
+ ASSERT_EQ("a::b::c(a::b)", demangler.Parse("_ZN1a1b1cES0_"));
+ ASSERT_EQ("_ZN1a1b1cES1_", demangler.Parse("_ZN1a1b1cES1_"));
+ ASSERT_EQ("a::b::c::d(a::b::c)", demangler.Parse("_ZN1a1b1c1dES1_"));
+ ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l)",
+ demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESA_"));
+ ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l::m)",
+ demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESB_"));
+
+ // Verify argument modifiers are included in substitution list.
+ ASSERT_EQ("one::two(char&* volatile const, char&)", demangler.Parse("_ZN3one3twoEKVPRcS0_"));
+ ASSERT_EQ("one::two(char&* volatile const, char&*)", demangler.Parse("_ZN3one3twoEKVPRcS1_"));
+ ASSERT_EQ("one::two(char&* volatile const, char&* volatile const)",
+ demangler.Parse("_ZN3one3twoEKVPRcS2_"));
+ ASSERT_EQ("one::two(int&* volatile* const, int&)", demangler.Parse("_ZN3one3twoEKPVPRiS0_"));
+ ASSERT_EQ("one::two(int&* volatile const, int&*)", demangler.Parse("_ZN3one3twoEKVPRiS1_"));
+ ASSERT_EQ("one::two(int&* volatile const, int&* volatile const)",
+ demangler.Parse("_ZN3one3twoEKVPRiS2_"));
+
+ // Verify Constructor/Destructor does properly save from function name.
+ ASSERT_EQ("_ZN1a1bES0_", demangler.Parse("_ZN1a1bES0_"));
+ ASSERT_EQ("a::b::b(a::b)", demangler.Parse("_ZN1a1bC1ES0_"));
+ ASSERT_EQ("a::b::~b(a::b)", demangler.Parse("_ZN1a1bD0ES0_"));
+
+ // Make sure substitution values are not saved.
+ ASSERT_EQ("a::b::b(a::b, char*, char*)", demangler.Parse("_ZN1a1bC1ES0_PcS1_"));
+}
+
+TEST(DemangleTest, ComplexSubstitution) {
+ Demangler demangler;
+
+ ASSERT_EQ("one::two<one::three>::two()", demangler.Parse("_ZN3one3twoINS_5threeEEC1Ev"));
+ ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
+ demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE"));
+ ASSERT_EQ("one::two::three::four<one::five>::~four(one::two*)",
+ demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS0_"));
+ ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three*)",
+ demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS1_"));
+ ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three::four*)",
+ demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS2_"));
+ ASSERT_EQ("one::two::three::four<one::five>::~four(one::five*)",
+ demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
+}
+
+TEST(DemangleTest, StringTooLong) {
+ Demangler demangler;
+
+ ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
+ demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 10));
+ ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
+ demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 30));
+ ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
+ demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 31));
+
+ // Check the length check only occurs after the two letter value
+ // has been processed.
+ ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 15));
+ ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 14));
+ ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 13));
+ ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
+}
+
+TEST(DemangleTest, demangle) {
+ std::string str;
+
+ str = demangle("_ZN1a1b1cES0_");
+ ASSERT_EQ("a::b::c(a::b)", str);
+
+ str = demangle("_");
+ ASSERT_EQ("_", str);
+
+ str = demangle("_Z");
+ ASSERT_EQ("_Z", str);
+
+ str = demangle("_Za");
+ ASSERT_EQ("_Za", str);
+
+ str = demangle("_Zaa");
+ ASSERT_EQ("operator&&", str);
+
+ str = demangle("Xa");
+ ASSERT_EQ("Xa", str);
+}
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
new file mode 100644
index 0000000..77cfd3b
--- /dev/null
+++ b/demangle/Demangler.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <cctype>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "Demangler.h"
+
+constexpr const char* Demangler::kTypes[];
+constexpr const char* Demangler::kDTypes[];
+constexpr const char* Demangler::kSTypes[];
+
+void Demangler::Save(const std::string& str, bool is_name) {
+ saves_.push_back(str);
+ last_save_name_ = is_name;
+}
+
+std::string Demangler::GetArgumentsString() {
+ size_t num_args = cur_state_.args.size();
+ std::string arg_str;
+ if (num_args > 0) {
+ arg_str = cur_state_.args[0];
+ for (size_t i = 1; i < num_args; i++) {
+ arg_str += ", " + cur_state_.args[i];
+ }
+ }
+ return arg_str;
+}
+
+const char* Demangler::AppendOperatorString(const char* name) {
+ const char* oper = nullptr;
+ switch (*name) {
+ case 'a':
+ name++;
+ switch (*name) {
+ case 'a':
+ oper = "operator&&";
+ break;
+ case 'd':
+ case 'n':
+ oper = "operator&";
+ break;
+ case 'N':
+ oper = "operator&=";
+ break;
+ case 'S':
+ oper = "operator=";
+ break;
+ }
+ break;
+ case 'c':
+ name++;
+ switch (*name) {
+ case 'l':
+ oper = "operator()";
+ break;
+ case 'm':
+ oper = "operator,";
+ break;
+ case 'o':
+ oper = "operator~";
+ break;
+ }
+ break;
+ case 'd':
+ name++;
+ switch (*name) {
+ case 'a':
+ oper = "operator delete[]";
+ break;
+ case 'e':
+ oper = "operator*";
+ break;
+ case 'l':
+ oper = "operator delete";
+ break;
+ case 'v':
+ oper = "operator/";
+ break;
+ case 'V':
+ oper = "operator/=";
+ break;
+ }
+ break;
+ case 'e':
+ name++;
+ switch (*name) {
+ case 'o':
+ oper = "operator^";
+ break;
+ case 'O':
+ oper = "operator^=";
+ break;
+ case 'q':
+ oper = "operator==";
+ break;
+ }
+ break;
+ case 'g':
+ name++;
+ switch (*name) {
+ case 'e':
+ oper = "operator>=";
+ break;
+ case 't':
+ oper = "operator>";
+ break;
+ }
+ break;
+ case 'i':
+ name++;
+ switch (*name) {
+ case 'x':
+ oper = "operator[]";
+ break;
+ }
+ break;
+ case 'l':
+ name++;
+ switch (*name) {
+ case 'e':
+ oper = "operator<=";
+ break;
+ case 's':
+ oper = "operator<<";
+ break;
+ case 'S':
+ oper = "operator<<=";
+ break;
+ case 't':
+ oper = "operator<";
+ break;
+ }
+ break;
+ case 'm':
+ name++;
+ switch (*name) {
+ case 'i':
+ oper = "operator-";
+ break;
+ case 'I':
+ oper = "operator-=";
+ break;
+ case 'l':
+ oper = "operator*";
+ break;
+ case 'L':
+ oper = "operator*=";
+ break;
+ case 'm':
+ oper = "operator--";
+ break;
+ }
+ break;
+ case 'n':
+ name++;
+ switch (*name) {
+ case 'a':
+ oper = "operator new[]";
+ break;
+ case 'e':
+ oper = "operator!=";
+ break;
+ case 'g':
+ oper = "operator-";
+ break;
+ case 't':
+ oper = "operator!";
+ break;
+ case 'w':
+ oper = "operator new";
+ break;
+ }
+ break;
+ case 'o':
+ name++;
+ switch (*name) {
+ case 'o':
+ oper = "operator||";
+ break;
+ case 'r':
+ oper = "operator|";
+ break;
+ case 'R':
+ oper = "operator|=";
+ break;
+ }
+ break;
+ case 'p':
+ name++;
+ switch (*name) {
+ case 'm':
+ oper = "operator->*";
+ break;
+ case 'l':
+ oper = "operator+";
+ break;
+ case 'L':
+ oper = "operator+=";
+ break;
+ case 'p':
+ oper = "operator++";
+ break;
+ case 's':
+ oper = "operator+";
+ break;
+ case 't':
+ oper = "operator->";
+ break;
+ }
+ break;
+ case 'q':
+ name++;
+ switch (*name) {
+ case 'u':
+ oper = "operator?";
+ break;
+ }
+ break;
+ case 'r':
+ name++;
+ switch (*name) {
+ case 'm':
+ oper = "operator%";
+ break;
+ case 'M':
+ oper = "operator%=";
+ break;
+ case 's':
+ oper = "operator>>";
+ break;
+ case 'S':
+ oper = "operator>>=";
+ break;
+ }
+ break;
+ }
+ if (oper == nullptr) {
+ return nullptr;
+ }
+ AppendCurrent(oper);
+ cur_state_.last_save = oper;
+ return name + 1;
+}
+
+const char* Demangler::GetStringFromLength(const char* name, std::string* str) {
+ assert(std::isdigit(*name));
+
+ size_t length = *name - '0';
+ name++;
+ while (*name != '\0' && std::isdigit(*name)) {
+ length = length * 10 + *name - '0';
+ name++;
+ }
+
+ std::string read_str;
+ while (*name != '\0' && length != 0) {
+ read_str += *name;
+ name++;
+ length--;
+ }
+ if (length != 0) {
+ return nullptr;
+ }
+ // Special replacement of _GLOBAL__N_1 to (anonymous namespace).
+ if (read_str == "_GLOBAL__N_1") {
+ *str += "(anonymous namespace)";
+ } else {
+ *str += read_str;
+ }
+ return name;
+}
+
+void Demangler::AppendCurrent(const std::string& str) {
+ if (!cur_state_.str.empty()) {
+ cur_state_.str += "::";
+ }
+ cur_state_.str += str;
+}
+
+void Demangler::AppendCurrent(const char* str) {
+ if (!cur_state_.str.empty()) {
+ cur_state_.str += "::";
+ }
+ cur_state_.str += str;
+}
+
+const char* Demangler::ParseS(const char* name) {
+ if (std::islower(*name)) {
+ const char* type = kSTypes[*name - 'a'];
+ if (type == nullptr) {
+ return nullptr;
+ }
+ AppendCurrent(type);
+ return name + 1;
+ }
+
+ if (saves_.empty()) {
+ return nullptr;
+ }
+
+ if (*name == '_') {
+ last_save_name_ = false;
+ AppendCurrent(saves_[0]);
+ return name + 1;
+ }
+
+ bool isdigit = std::isdigit(*name);
+ if (!isdigit && !std::isupper(*name)) {
+ return nullptr;
+ }
+
+ size_t index;
+ if (isdigit) {
+ index = *name - '0' + 1;
+ } else {
+ index = *name - 'A' + 11;
+ }
+ name++;
+ if (*name != '_') {
+ return nullptr;
+ }
+
+ if (index >= saves_.size()) {
+ return nullptr;
+ }
+
+ last_save_name_ = false;
+ AppendCurrent(saves_[index]);
+ return name + 1;
+}
+
+const char* Demangler::ParseFunctionName(const char* name) {
+ if (*name == 'E') {
+ if (parse_funcs_.empty()) {
+ return nullptr;
+ }
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+
+ // Remove the last saved part so that the full function name is not saved.
+ // But only if the last save was not something like a substitution.
+ if (!saves_.empty() && last_save_name_) {
+ saves_.pop_back();
+ }
+
+ function_name_ = cur_state_.str;
+ while (!cur_state_.suffixes.empty()) {
+ function_suffix_ += cur_state_.suffixes.back();
+ cur_state_.suffixes.pop_back();
+ }
+ cur_state_.Clear();
+
+ return name + 1;
+ }
+
+ return ParseComplexString(name);
+}
+
+const char* Demangler::ParseComplexArgument(const char* name) {
+ if (*name == 'E') {
+ if (parse_funcs_.empty()) {
+ return nullptr;
+ }
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+
+ AppendArgument(cur_state_.str);
+ cur_state_.str.clear();
+
+ return name + 1;
+ }
+
+ return ParseComplexString(name);
+}
+
+void Demangler::FinalizeTemplate() {
+ std::string arg_str(GetArgumentsString());
+ cur_state_ = state_stack_.top();
+ state_stack_.pop();
+ cur_state_.str += '<' + arg_str + '>';
+}
+
+const char* Demangler::ParseComplexString(const char* name) {
+ if (*name == 'S') {
+ name++;
+ if (*name == 't') {
+ AppendCurrent("std");
+ return name + 1;
+ }
+ return ParseS(name);
+ }
+ if (*name == 'L') {
+ name++;
+ if (!std::isdigit(*name)) {
+ return nullptr;
+ }
+ }
+ if (std::isdigit(*name)) {
+ std::string str;
+ name = GetStringFromLength(name, &str);
+ if (name == nullptr) {
+ return name;
+ }
+ AppendCurrent(str);
+ Save(cur_state_.str, true);
+ cur_state_.last_save = std::move(str);
+ return name;
+ }
+ if (*name == 'D') {
+ name++;
+ if (saves_.empty() || (*name != '0' && *name != '1' && *name != '2'
+ && *name != '5')) {
+ return nullptr;
+ }
+ last_save_name_ = false;
+ AppendCurrent("~" + cur_state_.last_save);
+ return name + 1;
+ }
+ if (*name == 'C') {
+ name++;
+ if (saves_.empty() || (*name != '1' && *name != '2' && *name != '3'
+ && *name != '5')) {
+ return nullptr;
+ }
+ last_save_name_ = false;
+ AppendCurrent(cur_state_.last_save);
+ return name + 1;
+ }
+ if (*name == 'K') {
+ cur_state_.suffixes.push_back(" const");
+ return name + 1;
+ }
+ if (*name == 'V') {
+ cur_state_.suffixes.push_back(" volatile");
+ return name + 1;
+ }
+ if (*name == 'I') {
+ // Save the current argument state.
+ state_stack_.push(cur_state_);
+ cur_state_.Clear();
+
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseTemplateArgumentsComplex;
+ return name + 1;
+ }
+ name = AppendOperatorString(name);
+ if (name != nullptr) {
+ Save(cur_state_.str, true);
+ }
+ return name;
+}
+
+void Demangler::AppendArgument(const std::string& str) {
+ std::string arg(str);
+ while (!cur_state_.suffixes.empty()) {
+ arg += cur_state_.suffixes.back();
+ cur_state_.suffixes.pop_back();
+ Save(arg, false);
+ }
+ cur_state_.args.push_back(arg);
+}
+
+const char* Demangler::ParseFunctionArgument(const char* name) {
+ if (*name == 'E') {
+ // The first argument is the function modifier.
+ // The second argument is the function type.
+ // The third argument is the return type of the function.
+ // The rest of the arguments are the function arguments.
+ size_t num_args = cur_state_.args.size();
+ if (num_args < 4) {
+ return nullptr;
+ }
+ std::string function_modifier = cur_state_.args[0];
+ std::string function_type = cur_state_.args[1];
+
+ std::string str = cur_state_.args[2] + ' ';
+ if (!cur_state_.args[1].empty()) {
+ str += '(' + cur_state_.args[1] + ')';
+ }
+
+ if (num_args == 4 && cur_state_.args[3] == "void") {
+ str += "()";
+ } else {
+ str += '(' + cur_state_.args[3];
+ for (size_t i = 4; i < num_args; i++) {
+ str += ", " + cur_state_.args[i];
+ }
+ str += ')';
+ }
+ str += cur_state_.args[0];
+
+ cur_state_ = state_stack_.top();
+ state_stack_.pop();
+ cur_state_.args.emplace_back(std::move(str));
+
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+ return name + 1;
+ }
+ return ParseArguments(name);
+}
+
+const char* Demangler::ParseArguments(const char* name) {
+ switch (*name) {
+ case 'P':
+ cur_state_.suffixes.push_back("*");
+ return name + 1;
+
+ case 'R':
+ // This should always be okay because the string is guaranteed to have
+ // at least two characters before this. A mangled string always starts
+ // with _Z.
+ if (name[-1] != 'R') {
+ // Multiple 'R's in a row only add a single &.
+ cur_state_.suffixes.push_back("&");
+ }
+ return name + 1;
+
+ case 'K':
+ case 'V': {
+ const char* suffix;
+ if (*name == 'K') {
+ suffix = " const";
+ } else {
+ suffix = " volatile";
+ }
+ if (name[-1] == 'K' || name[-1] == 'V') {
+ // Special case, const/volatile apply as a single entity.
+ assert(!cur_state_.suffixes.empty());
+ size_t index = cur_state_.suffixes.size();
+ cur_state_.suffixes[index-1].insert(0, suffix);
+ } else {
+ cur_state_.suffixes.push_back(suffix);
+ }
+ return name + 1;
+ }
+
+ case 'F': {
+ std::string function_modifier;
+ std::string function_type;
+ if (!cur_state_.suffixes.empty()) {
+ // If the first element starts with a ' ', then this modifies the
+ // function itself.
+ if (cur_state_.suffixes.back()[0] == ' ') {
+ function_modifier = cur_state_.suffixes.back();
+ cur_state_.suffixes.pop_back();
+ }
+ while (!cur_state_.suffixes.empty()) {
+ function_type += cur_state_.suffixes.back();
+ cur_state_.suffixes.pop_back();
+ }
+ }
+
+ state_stack_.push(cur_state_);
+
+ cur_state_.Clear();
+
+ // The function parameter has this format:
+ // First argument is the function modifier.
+ // Second argument is the function type.
+ // Third argument will be the return function type but has not
+ // been parsed yet.
+ // Any other parameters are the arguments to the function. There
+ // must be at least one or this isn't valid.
+ cur_state_.args.push_back(function_modifier);
+ cur_state_.args.push_back(function_type);
+
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseFunctionArgument;
+ return name + 1;
+ }
+
+ case 'N':
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseComplexArgument;
+ return name + 1;
+
+ case 'S':
+ name++;
+ if (*name == 't') {
+ cur_state_.str = "std::";
+ return name + 1;
+ }
+ name = ParseS(name);
+ if (name == nullptr) {
+ return nullptr;
+ }
+ AppendArgument(cur_state_.str);
+ cur_state_.str.clear();
+ return name;
+
+ case 'D':
+ name++;
+ if (*name >= 'a' && *name <= 'z') {
+ const char* arg = Demangler::kDTypes[*name - 'a'];
+ if (arg == nullptr) {
+ return nullptr;
+ }
+ AppendArgument(arg);
+ return name + 1;
+ }
+ return nullptr;
+
+ case 'I':
+ // Save the current argument state.
+ state_stack_.push(cur_state_);
+ cur_state_.Clear();
+
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseTemplateArguments;
+ return name + 1;
+
+ case 'v':
+ AppendArgument("void");
+ return name + 1;
+
+ default:
+ if (*name >= 'a' && *name <= 'z') {
+ const char* arg = Demangler::kTypes[*name - 'a'];
+ if (arg == nullptr) {
+ return nullptr;
+ }
+ AppendArgument(arg);
+ return name + 1;
+ } else if (std::isdigit(*name)) {
+ std::string arg = cur_state_.str;
+ name = GetStringFromLength(name, &arg);
+ if (name == nullptr) {
+ return nullptr;
+ }
+ Save(arg, true);
+ if (*name == 'I') {
+ // There is one case where this argument is not complete, and that's
+ // where this is a template argument.
+ cur_state_.str = arg;
+ } else {
+ AppendArgument(arg);
+ cur_state_.str.clear();
+ }
+ return name;
+ }
+ }
+ return nullptr;
+}
+
+const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
+ if (*name == 'E') {
+ if (parse_funcs_.empty()) {
+ return nullptr;
+ }
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+ FinalizeTemplate();
+ Save(cur_state_.str, false);
+ return name + 1;
+ }
+ return ParseArguments(name);
+}
+
+const char* Demangler::ParseTemplateArguments(const char* name) {
+ if (*name == 'E') {
+ if (parse_funcs_.empty()) {
+ return nullptr;
+ }
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+ FinalizeTemplate();
+ AppendArgument(cur_state_.str);
+ cur_state_.str.clear();
+ return name + 1;
+ }
+ return ParseArguments(name);
+}
+
+const char* Demangler::FindFunctionName(const char* name) {
+ if (*name == 'N') {
+ parse_funcs_.push_back(&Demangler::ParseArguments);
+ parse_func_ = &Demangler::ParseFunctionName;
+ return name + 1;
+ }
+
+ if (std::isdigit(*name)) {
+ name = GetStringFromLength(name, &function_name_);
+ } else {
+ name = AppendOperatorString(name);
+ function_name_ = cur_state_.str;
+ }
+ parse_func_ = &Demangler::ParseArguments;
+ cur_state_.Clear();
+ return name;
+}
+
+std::string Demangler::Parse(const char* name, size_t max_length) {
+ if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
+ // Name is not mangled.
+ return name;
+ }
+
+ Clear();
+
+ parse_func_ = &Demangler::FindFunctionName;
+ parse_funcs_.push_back(&Demangler::Fail);
+ const char* cur_name = name + 2;
+ while (cur_name != nullptr && *cur_name != '\0'
+ && static_cast<size_t>(cur_name - name) < max_length) {
+ cur_name = (this->*parse_func_)(cur_name);
+ }
+ if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty()) {
+ return name;
+ }
+
+ std::string arg_str;
+ if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
+ // If the only argument is void, then don't print any args.
+ arg_str = "()";
+ } else {
+ arg_str = GetArgumentsString();
+ if (!arg_str.empty()) {
+ arg_str = '(' + arg_str + ')';
+ }
+ }
+ return function_name_ + arg_str + function_suffix_;
+}
+
+std::string demangle(const char* name) {
+ Demangler demangler;
+ return demangler.Parse(name);
+}
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
new file mode 100644
index 0000000..3bd4f3c
--- /dev/null
+++ b/demangle/Demangler.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIB_DEMANGLE_DEMANGLER_H
+#define __LIB_DEMANGLE_DEMANGLER_H
+
+#include <assert.h>
+
+#include <stack>
+#include <string>
+#include <vector>
+
+class Demangler {
+ public:
+ Demangler() = default;
+
+ // NOTE: The max_length is not guaranteed to be the absolute max length
+ // of a string that will be rejected. Under certain circumstances the
+ // length check will not occur until after the second letter of a pair
+ // is checked.
+ std::string Parse(const char* name, size_t max_length = kMaxDefaultLength);
+
+ void AppendCurrent(const std::string& str);
+ void AppendCurrent(const char* str);
+ void AppendArgument(const std::string& str);
+ std::string GetArgumentsString();
+ void FinalizeTemplate();
+ const char* ParseS(const char* name);
+ const char* AppendOperatorString(const char* name);
+ void Save(const std::string& str, bool is_name);
+
+ private:
+ void Clear() {
+ parse_funcs_.clear();
+ function_name_.clear();
+ function_suffix_.clear();
+ first_save_.clear();
+ cur_state_.Clear();
+ saves_.clear();
+ while (!state_stack_.empty()) {
+ state_stack_.pop();
+ }
+ last_save_name_ = false;
+ }
+
+ using parse_func_type = const char* (Demangler::*)(const char*);
+ parse_func_type parse_func_;
+ std::vector<parse_func_type> parse_funcs_;
+ std::vector<std::string> saves_;
+ bool last_save_name_;
+
+ std::string function_name_;
+ std::string function_suffix_;
+
+ struct StateData {
+ void Clear() {
+ str.clear();
+ args.clear();
+ prefix.clear();
+ suffixes.clear();
+ last_save.clear();
+ }
+
+ std::string str;
+ std::vector<std::string> args;
+ std::string prefix;
+ std::vector<std::string> suffixes;
+ std::string last_save;
+ };
+ std::stack<StateData> state_stack_;
+ std::string first_save_;
+ StateData cur_state_;
+
+ static const char* GetStringFromLength(const char* name, std::string* str);
+
+ // Parsing functions.
+ const char* ParseComplexString(const char* name);
+ const char* ParseComplexArgument(const char* name);
+ const char* ParseArguments(const char* name);
+ const char* ParseTemplateArguments(const char* name);
+ const char* ParseTemplateArgumentsComplex(const char* name);
+ const char* ParseFunctionArgument(const char* name);
+ const char* ParseFunctionName(const char* name);
+ const char* FindFunctionName(const char* name);
+ const char* Fail(const char*) { return nullptr; }
+
+ // The default maximum string length string to process.
+ static constexpr size_t kMaxDefaultLength = 2048;
+
+ static constexpr const char* kTypes[] = {
+ "signed char", // a
+ "bool", // b
+ "char", // c
+ "double", // d
+ "long double", // e
+ "float", // f
+ "__float128", // g
+ "unsigned char", // h
+ "int", // i
+ "unsigned int", // j
+ nullptr, // k
+ "long", // l
+ "unsigned long", // m
+ "__int128", // n
+ "unsigned __int128", // o
+ nullptr, // p
+ nullptr, // q
+ nullptr, // r
+ "short", // s
+ "unsigned short", // t
+ nullptr, // u
+ "void", // v
+ "wchar_t", // w
+ "long long", // x
+ "unsigned long long", // y
+ "...", // z
+ };
+
+ static constexpr const char* kDTypes[] = {
+ "auto", // a
+ nullptr, // b
+ nullptr, // c
+ "decimal64", // d
+ "decimal128", // e
+ "decimal32", // f
+ nullptr, // g
+ "half", // h
+ "char32_t", // i
+ nullptr, // j
+ nullptr, // k
+ nullptr, // l
+ nullptr, // m
+ "decltype(nullptr)", // n
+ nullptr, // o
+ nullptr, // p
+ nullptr, // q
+ nullptr, // r
+ "char16_t", // s
+ nullptr, // t
+ nullptr, // u
+ nullptr, // v
+ nullptr, // w
+ nullptr, // x
+ nullptr, // y
+ nullptr, // z
+ };
+
+ static constexpr const char* kSTypes[] = {
+ "std::allocator", // a
+ "std::basic_string", // b
+ nullptr, // c
+ "std::iostream", // d
+ nullptr, // e
+ nullptr, // f
+ nullptr, // g
+ nullptr, // h
+ "std::istream", // i
+ nullptr, // j
+ nullptr, // k
+ nullptr, // l
+ nullptr, // m
+ nullptr, // n
+ "std::ostream", // o
+ nullptr, // p
+ nullptr, // q
+ nullptr, // r
+ "std::string", // s
+ nullptr, // t
+ nullptr, // u
+ nullptr, // v
+ nullptr, // w
+ nullptr, // x
+ nullptr, // y
+ nullptr, // z
+ };
+};
+
+#endif // __LIB_DEMANGLE_DEMANGLER_H
diff --git a/demangle/include/demangle.h b/demangle/include/demangle.h
new file mode 100644
index 0000000..01f1b80
--- /dev/null
+++ b/demangle/include/demangle.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LIB_DEMANGLE_H_
+#define __LIB_DEMANGLE_H_
+
+#include <string>
+
+// If the name cannot be demangled, the original name will be returned as
+// a std::string. If the name can be demangled, then the demangled name
+// will be returned as a std::string.
+std::string demangle(const char* name);
+
+#endif // __LIB_DEMANGLE_H_