1 /*
2     Copyright (C) 2014 Volker Krause <vkrause@kde.org>
3 
4     This program is free software; you can redistribute it and/or modify it
5     under the terms of the GNU Library General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or (at your
7     option) any later version.
8 
9     This program is distributed in the hope that it will be useful, but WITHOUT
10     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
12     License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 #include <config-elf-dissector.h>
19 
20 #include <QtTest/qtest.h>
21 #include <QObject>
22 #include <QDebug>
23 
24 #include <demangle/demangler.h>
25 
26 #define VB QVector<QByteArray>()
27 
28 class DemanglerTest : public QObject
29 {
30     Q_OBJECT
31 private slots:
testDemangler_data()32     void testDemangler_data()
33     {
34         QTest::addColumn<QString>("mangled"); // should be QByteArray, but then we have to explicitly cast it below
35         QTest::addColumn<QVector<QByteArray>>("expectedDemangled");
36 
37         QTest::newRow("empty") << "" << (VB << "");
38         QTest::newRow("C 1") << "malloc" << (VB << "malloc");
39         QTest::newRow("member func 1") << "_ZN10QArrayData4dataEv" << (VB << "QArrayData" << "data()");
40         QTest::newRow("member func 2") << "_ZN10QByteArray6appendERKS_" << (VB << "QByteArray" << "append(QByteArray const&)");
41         QTest::newRow("member func 3") << "_ZN10QByteArray6numberEii" << (VB << "QByteArray" << "number(int, int)");
42         QTest::newRow("ctor 1") << "_ZN10QByteArrayC1EPKci" << (VB << "QByteArray" << "QByteArray(char const*, int)");
43         QTest::newRow("copy ctor 1") << "_ZN10QByteArrayC1ERKS_" << (VB << "QByteArray" << "QByteArray(QByteArray const&)");
44         QTest::newRow("copy ctor 2") << "_ZN10QByteArrayC2ERKS_" << (VB << "QByteArray" << "QByteArray(QByteArray const&)");
45         QTest::newRow("dtor 1") << "_ZN10QByteArrayD1Ev" << (VB << "QByteArray" << "~QByteArray()");
46         QTest::newRow("dtor 2") << "_ZN10QByteArrayD2Ev" << (VB << "QByteArray" << "~QByteArray()");
47 
48         QTest::newRow("operator 1") << "_ZN10QByteArraypLERKS_" << (VB << "QByteArray" << "operator+=(QByteArray const&)");
49         QTest::newRow("operator 2") << "_ZNK10QByteArraycvPKcEv" << (VB << "QByteArray" << "operator char const*() const");
50         QTest::newRow("operator 3") << "_ZN7QStringaSEOS_" << (VB << "QString" << "operator=(QString&&)");
51 
52 #if BINUTILS_VERSION >= BINUTILS_VERSION_CHECK(2, 24)
53         QTest::newRow("rvalue ref on this") << "_ZNO7QString11toLocal8BitEv" << (VB << "QString" << "toLocal8Bit() &&");
54 #endif
55 
56         QTest::newRow("template func 1") <<  "_Z13qGetPtrHelperI14QScopedPointerI11QObjectData21QScopedPointerDeleterIS1_EEENT_7pointerERKS5_" << (VB << "qGetPtrHelper" << "qGetPtrHelper<QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>>>(QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData>> const&)");
57         QTest::newRow("template func 2") << "_ZN23QXmlStreamWriterPrivate5writeILi4EEEvRAT__Kc" << (VB << "QXmlStreamWriterPrivate" << "write" << "write<4>(char const (&) [4])");
58         QTest::newRow("template func 3") << "_ZSt4moveIRP11TreeMapItemEONSt16remove_referenceIT_E4typeEOS4_" << (VB << "std" << "move" << "move<TreeMapItem*&>(TreeMapItem*&)");
59 
60         QTest::newRow("function pointer template") << "_ZN20QGlobalStaticDeleterI5QListIPFP7QObjectvEEED1Ev" << (VB << "QGlobalStaticDeleter" << "QGlobalStaticDeleter<QList<QObject* (*)()>>" << "~QGlobalStaticDeleter()");
61 
62 #if BINUTILS_VERSION < BINUTILS_VERSION_CHECK(2, 36)
63         QTest::newRow("lambda 1") << "_ZSt7find_ifIPKSt10shared_ptrI7ElfFileEZN10ElfFileSet7addFileERK7QStringEUlRS3_E_ET_SB_SB_T0_" << (VB << "std" << "find_if" << "find_if<std::shared_ptr<ElfFile> const*, ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1}>(ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1}, ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1}, ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1})");
64 #else
65         QTest::newRow("lambda 1") << "_ZSt7find_ifIPKSt10shared_ptrI7ElfFileEZN10ElfFileSet7addFileERK7QStringEUlRS3_E_ET_SB_SB_T0_" << (VB << "std" << "find_if" << "find_if<std::shared_ptr<ElfFile> const*, ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1}>(std::shared_ptr<ElfFile> const*, std::shared_ptr<ElfFile> const*, ElfFileSet::addFile(QString const&)::{lambda(std::shared_ptr<ElfFile> const&)#1})");
66 #endif
67         QTest::newRow("lambda 2") << "_ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE5_clEv" << (VB << "Ui_MainWindow" << "setupUi(QMainWindow*)" << "{lambda()#7}" << "operator()() const");
68 
69         QTest::newRow("tmp 1") << "_Z27qRegisterNormalizedMetaTypeI7QVectorI10QByteArrayEEiRKS1_PT_N9QtPrivate21MetaTypeDefinedHelperIS5_Xaasr12QMetaTypeId2IS5_E7DefinedntsrSA_9IsBuiltInEE11DefinedTypeE" << (VB << "qRegisterNormalizedMetaType" << /*"qRegisterNormalizedMetaType<QVector<QByteArray>>(QByteArray const&, QVector<QByteArray>*, QtPrivate::MetaTypeDefinedHelper<QVector<QByteArray>, QMetaTypeId2<QVector<QByteArray>>::Defined&&(!QMetaTypeId2<QVector<QByteArray>>::IsBuiltIn)>::DefinedType)"*/ "qRegisterNormalizedMetaType<QVector<QByteArray>>(QByteArray const&, QVector<QByteArray>*, QtPrivate::MetaTypeDefinedHelper<QVector<QByteArray>, QMetaTypeId2<QVector<QByteArray>>::Defined&&!QMetaTypeId2<QVector<QByteArray>>::IsBuiltIn>::DefinedType)"); // TODO find a way to fix the parenthesis in unary/binary expressions
70 #if BINUTILS_VERSION < BINUTILS_VERSION_CHECK(2, 36)
71         QTest::newRow("tmp 2") << "_ZN7QObject7connectIM9QLineEditFvRK7QStringEZN10MainWindowC1EP7QWidgetEUlS4_E_EEN9QtPrivate9QEnableIfIXeqsrNSB_15FunctionPointerIT0_EE13ArgumentCountngLi1EEN11QMetaObject10ConnectionEE4TypeEPKNSD_IT_E6ObjectESK_PKS_SE_N2Qt14ConnectionTypeE" << (VB << "QObject" << "connect" << /*"connect<void (QLineEdit::*)(QString const&), MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}>(QtPrivate::QEnableIf<void (QLineEdit::*)(QString const&)>::Object const*, QtPrivate::QEnableIf<MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}::FunctionPointer<MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}>::ArgumentCount==(-(1)), QMetaObject::Connection>::Type, QObject const*, MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}::FunctionPointer, Qt::ConnectionType)"*/ "connect<void (QLineEdit::*)(QString const&), MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}>(QtPrivate::QEnableIf<void (QLineEdit::*)(QString const&)>::Object const*, QtPrivate::QEnableIf<MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}::FunctionPointer<MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}>::ArgumentCount==-1, QMetaObject::Connection>::Type, QObject const*, MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}::FunctionPointer, Qt::ConnectionType)"); // TODO fix parenthesis on -1
72 #else
73         QTest::newRow("tmp 2") << "_ZN7QObject7connectIM9QLineEditFvRK7QStringEZN10MainWindowC1EP7QWidgetEUlS4_E_EEN9QtPrivate9QEnableIfIXeqsrNSB_15FunctionPointerIT0_EE13ArgumentCountngLi1EEN11QMetaObject10ConnectionEE4TypeEPKNSD_IT_E6ObjectESK_PKS_SE_N2Qt14ConnectionTypeE" << (VB << "QObject" << "connect" << "connect<void (QLineEdit::*)(QString const&), MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}>(QtPrivate::FunctionPointer<void (QLineEdit::*)(QString const&)>::Object const*, void (QLineEdit::*)(QString const&), QObject const*, MainWindow::MainWindow(QWidget*)::{lambda(QString const&)#1}, Qt::ConnectionType)");
74 #endif
75 
76         QTest::newRow("pack 1") << "_ZSt12__get_helperILm0EPN2Ui10MainWindowEISt14default_deleteIS1_EEENSt9__add_refIT0_E4typeERSt11_Tuple_implIXT_EIS6_DpT1_EE" << (VB << "std" << "__get_helper" << "__get_helper<0ul, Ui::MainWindow*, std::default_delete<Ui::MainWindow>>(std::_Tuple_impl<0ul, Ui::MainWindow*, std::default_delete<Ui::MainWindow>>&)");
77         QTest::newRow("pack 2") << "_ZN3WTF14NeverDestroyedINS_12_GLOBAL__N_125ARC4RandomNumberGeneratorEEC1IIEEEDpOT_" << (VB << "WTF" << "NeverDestroyed" << "NeverDestroyed<WTF::(anonymous namespace)::ARC4RandomNumberGenerator>" << "NeverDestroyed" << "NeverDestroyed<>()");
78 
79         QTest::newRow("vendor 1") << "_ZL18mergeQuestionMarksU8__vectorx" << (VB << "mergeQuestionMarks(long long __vector)");
80 
81         QTest::newRow("array no size") << "_ZN5boost6detail21sp_assert_convertibleIA_NS_18default_color_typeES3_EEvv" << (VB << "boost" << "detail" << "sp_assert_convertible" << "sp_assert_convertible<boost::default_color_type [], boost::default_color_type []>()");
82         QTest::newRow("vector type") << "_ZN9QSimdSse25v_mulEDv4_fS0_" << (VB << "QSimdSse2" << "v_mul(float __vector(4), float __vector(4))");
83 
84         QTest::newRow("decltype in return") << "_ZNSt16allocator_traitsISaIiEE9constructIiJRKiEEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS0_PT_DpOS5_" << (VB << "std" << "allocator_traits" << "allocator_traits<std::allocator<int>>" << "construct" << "construct<int, int const&>(std::allocator<int>&, int*, int const&)");
85         QTest::newRow("decltype with binary op") << "_ZSt5beginI7QVectorIP7ElfFileEEDTcldtfp_5beginEERT_" << (VB << "std" << "begin" << "begin<QVector<ElfFile*>>(QVector<ElfFile*>&)");
86 
87         QTest::newRow("typeinfo") << "_ZTI14ElfNodeVisitorIiE" << (VB << "ElfNodeVisitor" << "ElfNodeVisitor<int>" << "typeinfo");
88         QTest::newRow("typeinfo name") << "_ZTS26KRecursiveFilterProxyModel" << (VB << "KRecursiveFilterProxyModel" << "typeinfo name");
89         QTest::newRow("vtable") << "_ZTV17ElfDynamicSection" << (VB << "ElfDynamicSection" << "vtable");
90         QTest::newRow("thunk") << "_ZThn16_N13TreeMapWidgetD0Ev" << (VB << "TreeMapWidget" << "~TreeMapWidget()" << "thunk");
91         QTest::newRow("virtual thunk") << "_ZTv0_n24_N5Solid6Ifaces11OpticalDiscD1Ev" << (VB << "Solid" << "Ifaces" << "OpticalDisc" << "~OpticalDisc()" << "virtual thunk");
92         QTest::newRow("covariant thunk") << "_ZTch0_h16_NK12ThreadWeaver15WeaverImplState6weaverEv" << (VB << "ThreadWeaver" << "WeaverImplState" << "weaver() const" << "covariant return thunk");
93         QTest::newRow("vtt") << "_ZTTN5Solid6Ifaces13StorageVolumeE" << (VB << "Solid" << "Ifaces" << "StorageVolume" << "vtt");
94         QTest::newRow("construction vtable") << "_ZTCN5Solid8Backends7UDisks211OpticalDiscE0_NS1_5BlockE" << (VB << "Solid" << "Backends" << "UDisks2" << "Block" << "construction vtable in Solid::Backends::UDisks2::OpticalDisc");
95 
96         QTest::newRow("guard variable") << "_ZGVZN12_GLOBAL__N_119Q_QGS_s_parsingData13innerFunctionEvE6holder" << (VB << "(anonymous namespace)" << "Q_QGS_s_parsingData" << "innerFunction()" << "holder" << "guard variable");
97         QTest::newRow("reference temporary") << "_ZGRZNK16KateHighlighting10canBreakAtE5QChariE2sq0" << (VB << "KateHighlighting" << "canBreakAt(QChar, int) const" << "sq" << "reference temporary #0");
98         QTest::newRow("unnamed type") << "_ZN24QVariantAnimationPrivateUt_D1Ev" << (VB << "QVariantAnimationPrivate" <<  "{unnamed type#1}" << "~QVariantAnimationPrivate()");
99 
100         QTest::newRow("literal bool") << "_ZNSt10_Iter_baseIPN3QV45ValueELb0EE7_S_baseES2_" << (VB << "std" << "_Iter_base" << "_Iter_base<QV4::Value*, false>" << "_S_base(QV4::Value*)");
101         QTest::newRow("literal custom") << "_ZSt10_ConstructIN3JSC4Yarr13YarrGeneratorILNS1_18YarrJITCompileModeE0EE6YarrOpEIS5_EEvPT_DpOT0_" << (VB << "std" << "_Construct" << "_Construct<JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)0>::YarrOp, JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)0>::YarrOp>(JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)0>::YarrOp*, JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)0>::YarrOp&&)");
102         QTest::newRow("literal neg int") << "_ZNK7WebCore17CSSPrimitiveValue15convertToLengthILin1EEENS_6LengthEPKNS_11RenderStyleES5_db" << (VB << "WebCore" << "CSSPrimitiveValue" << "convertToLength" << "convertToLength<-1>(WebCore::RenderStyle const*, WebCore::RenderStyle const*, double, bool) const");
103 
104         QTest::newRow("cloned component") << "_ZN7QVectorIjE16defaultConstructEPjS1_.isra.2" << (VB << "QVector" << "QVector<unsigned int>" << "defaultConstruct(unsigned int*, unsigned int*) [clone .isra.2]");
105 
106         QTest::newRow("nested types") << "_ZN7QVectorIPZN10MainWindow8loadFileERK7QStringE10SymbolNodeE4dataEv" << (VB << "QVector" << "QVector<MainWindow::loadFile(QString const&)::SymbolNode*>" << "data()");
107 
108         QTest::newRow("default argument") << "_ZZN8KDevelop18SourceFileTemplate22setTemplateDescriptionERK7QStringS3_Ed_NKUlvE_clEv" << (VB << "KDevelop" << "SourceFileTemplate" << "setTemplateDescription(QString const&, QString const&)" << "{default arg#1}" << "{lambda()#1}" << "operator()() const");
109 
110         QTest::newRow("abi tag") << "_ZNK5ZXing6Result4textB5cxx11Ev" << (VB << "ZXing" << "Result" << "text[abi:cxx11]() const");
111 
112         QTest::newRow("transaction clone") << "_ZGTtNSt11logic_errorC1EPKc.cold.14" << (VB << "std" << "logic_error" << "transaction clone for logic_error(char const*) [clone .cold.14]");
113 
114 	QTest::newRow("initializer list") << "_Z1fP1BIXtl1ALi1EEEE" << (VB << "f(B<A{1}>*)");
115 	QTest::newRow("template parameter object") << "_ZTAXtl1ALi1EEE" << (VB << "template parameter object for A{1}");
116 
117         QTest::newRow("noexcept") << "_ZSt9__find_ifIPKcN9__gnu_cxx5__ops12_Iter_negateIPDoFiiEEEET_S8_S8_T0_St26random_access_iterator_tag.isra.0"
118             << (VB << "std" << "__find_if" << "__find_if<char const*, __gnu_cxx::__ops::_Iter_negate<int (*)(int) noexcept>>(char const*, char const*, __gnu_cxx::__ops::_Iter_negate<int (*)(int) noexcept>, std::random_access_iterator_tag) [clone .isra.0]");
119     }
120 
testDemangler()121     void testDemangler()
122     {
123         QFETCH(QString, mangled);
124         QFETCH(QVector<QByteArray>, expectedDemangled);
125 
126         Demangler d;
127         auto actualDemangled = d.demangle(mangled.toLatin1());
128         if (actualDemangled != expectedDemangled) {
129             qDebug() << actualDemangled;
130             qDebug() << expectedDemangled;
131         }
132         QEXPECT_FAIL("nested types", "bug in pointer handling", Continue);
133         QCOMPARE(actualDemangled, expectedDemangled);
134     }
135 
testSymbolType_data()136     void testSymbolType_data()
137     {
138         QTest::addColumn<QByteArray>("symbol");
139         QTest::addColumn<Demangler::SymbolType>("type");
140 
141         QTest::newRow("empty") << QByteArray("") << Demangler::SymbolType::Normal;
142         QTest::newRow("C") << QByteArray("malloc") << Demangler::SymbolType::Normal;
143         QTest::newRow("C++") << QByteArray("_ZN10QArrayData4dataEv") << Demangler::SymbolType::Normal;
144 
145         QTest::newRow("vtable") << QByteArray("_ZTV17ElfDynamicSection") << Demangler::SymbolType::VTable;
146         QTest::newRow("typeinfo") << QByteArray("_ZTI14ElfNodeVisitorIiE") << Demangler::SymbolType::TypeInfo;
147         QTest::newRow("typeinfo name") << QByteArray("_ZTS26KRecursiveFilterProxyModel") << Demangler::SymbolType::TypeInfoName;
148 //         QTest::newRow("thunk") << QByteArray("_ZThn16_N13TreeMapWidgetD0Ev") << Demangler::SymbolType::Thunk;
149 //         QTest::newRow("virtual thunk") << QByteArray("_ZTv0_n24_N5Solid6Ifaces11OpticalDiscD1Ev") << Demangler::SymbolType::VirtualThunk;
150 //         QTest::newRow("covariant thunk") << QByteArray("_ZTch0_h16_NK12ThreadWeaver15WeaverImplState6weaverEv") << Demangler::SymbolType::CovariantThunk;
151         QTest::newRow("vtt") << QByteArray("_ZTTN5Solid6Ifaces13StorageVolumeE") << Demangler::SymbolType::VTT;
152         QTest::newRow("construction vtable") << QByteArray("_ZTCN5Solid8Backends7UDisks211OpticalDiscE0_NS1_5BlockE") << Demangler::SymbolType::ConstructionVTable;
153     }
154 
testSymbolType()155     void testSymbolType()
156     {
157         QFETCH(QByteArray, symbol);
158         QFETCH(Demangler::SymbolType, type);
159 
160         QCOMPARE(Demangler::symbolType(symbol), type);
161     }
162 };
163 
164 QTEST_MAIN(DemanglerTest)
165 
166 #include "demangler_test.moc"
167