#include "llvm/Support/ItaniumManglingCanonicalizer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
#include <cstdlib>
#include <map>
#include <vector>
using namespace llvm;
namespace {
using EquivalenceError = llvm::ItaniumManglingCanonicalizer::EquivalenceError;
using FragmentKind = llvm::ItaniumManglingCanonicalizer::FragmentKind;
struct Equivalence {
FragmentKind Kind;
llvm::StringRef First;
llvm::StringRef Second;
};
using EquivalenceClass = std::vector<llvm::StringRef>;
struct Testcase {
std::vector<Equivalence> Equivalences;
std::vector<EquivalenceClass> Classes;
};
static std::vector<Testcase> getTestcases() {
return {
{
{
{FragmentKind::Type, "Ss",
"NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"},
{FragmentKind::Type, "Ss",
"NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
},
{
{"_Z1fv"},
{"_Z1fSs",
"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
"_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
{"_ZNKSs4sizeEv",
"_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4sizeEv",
"_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4sizeEv"},
}
},
{
{
{FragmentKind::Type, "1X", "N1N1XIiEE"},
{FragmentKind::Type, "1TIS_IiiES0_E", "1TIiE"},
{FragmentKind::Name, "N1A1B3fooE", "N2AB3fooE"},
},
{
{"_Z1f1XPS_RS_", "_Z1fN1N1XIiEEPS1_RS1_"},
{"_ZN1A1B3fooE1TIS1_IiiES2_EPS3_RS3_", "_ZN2AB3fooE1TIiEPS1_RS1_"},
}
},
{
{
{FragmentKind::Name,
"NSt3__111char_traitsE",
"NSt7__cxx1111char_traitsE"},
{FragmentKind::Name,
"NSt3__19allocatorE",
"Sa"}, {FragmentKind::Name,
"St6vector",
"NSt3__16vectorE"},
{FragmentKind::Type,
"NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
"NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
{FragmentKind::Type, "1XI1AE", "1XI1BE"},
{FragmentKind::Name, "1X", "1Y"},
},
{
{"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
"_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
{"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
{"_Z1f1XI1AE", "_Z1f1XI1BE", "_Z1f1YI1AE", "_Z1f1YI1BE"},
{"_Z1f1XI1CE", "_Z1f1YI1CE"},
}
},
{
{
{FragmentKind::Name, "St3__1", "St7__cxx11"},
{FragmentKind::Name, "NSt3__19allocatorE", "Sa"},
{FragmentKind::Name, "St6vector", "NSt3__16vectorE"},
{FragmentKind::Name, "NSt7__cxx1111char_traitsE", "St11char_traits"},
},
{
{"_Z1fNSt7__cxx113fooE",
"_Z1fNSt3__13fooE"},
{"_Z1fNSt7__cxx1111char_traitsIcEE",
"_Z1fNSt3__111char_traitsIcEE",
"_Z1fSt11char_traitsIcE"},
{"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
"_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
{"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
}
},
{
{
{FragmentKind::Name, "St3__1", "St"},
{FragmentKind::Name, "St3__1", "St7__cxx11"},
{FragmentKind::Name, "NSt3__19allocatorE", "Sa"},
},
{
{"_Z1fSt3foo", "_Z1fNSt3__13fooE", "_Z1fNSt7__cxx113fooE"},
{"_Z1fNSt3bar3bazE", "_Z1fNSt3__13bar3bazE"},
{"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
"_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"},
{"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"},
}
},
{
{
{FragmentKind::Type, "1A", "1B"},
{FragmentKind::Type, "1A", "1C"},
{FragmentKind::Type, "1D", "1B"},
{FragmentKind::Type, "1C", "1E"},
},
{
{"_Z1f1A", "_Z1f1B", "_Z1f1C", "_Z1f1D", "_Z1f1E"},
{"_Z1f1F"},
}
},
{
{
{FragmentKind::Encoding, "1fv", "1gv"},
},
{
{"_Z1fv", "_Z1gv"},
{"_ZZ1fvE1n", "_ZZ1gvE1n"},
}
},
{
{
{FragmentKind::Type, "1X", "1YI1XE"},
{FragmentKind::Type, "1AI1BE", "1B"},
},
{
{"_Z1f1X", "_Z1f1YI1XE", "_Z1f1YIS_I1XEE", "_Z1f1YIS_IS_I1XEEE"},
{"_Z1f1B", "_Z1f1AI1BE", "_Z1f1AIS_I1BEE", "_Z1f1AIS_IS_I1BEEE"},
}
},
{
{
{FragmentKind::Name, "3std", "St"},
{FragmentKind::Name, "1X", "1Y"},
{FragmentKind::Name, "N1X1ZE", "N1Y1ZE"},
},
{}
},
{
{},
{{"_ZN1XC1Ev"}, {"_ZN1XC2Ev"}, {"_ZN1XD1Ev"}, {"_ZN1XD2Ev"}}
},
{
{
{FragmentKind::Type, "A_i", "A1_f"},
},
{
{"_Z1fRA_i", "_Z1fRA_i", "_Z1fRA1_f"},
{"_Z1fRA1_i"}, {"_Z1fRA_f"},
}
},
{
{
{FragmentKind::Encoding, "3foo", "3bar"},
},
{
{"foo", "bar"},
{"_Z1fIL_Z3fooEEvv", "_Z1fIL_Z3barEEvv"},
}
},
};
}
static std::vector<Testcase> getForwardTemplateReferenceTestcases() {
return {
{
{
{FragmentKind::Encoding, "N1XcvT_I1AEEv", "N1YcvT_I1AEEv"},
{FragmentKind::Name, "1A", "1B"},
},
{
{"_ZN1XcvT_I1AEEv"},
{"_ZN1XcvT_I1BEEv"},
{"_ZN1YcvT_I1AEEv"},
{"_ZN1YcvT_I1BEEv"},
{"_ZN1XcvT_I1AEEv"},
}
},
};
}
template<bool CanonicalizeFirst>
static void testTestcases(ArrayRef<Testcase> Testcases) {
for (const auto &Testcase : Testcases) {
llvm::ItaniumManglingCanonicalizer Canonicalizer;
for (const auto &Equiv : Testcase.Equivalences) {
auto Result =
Canonicalizer.addEquivalence(Equiv.Kind, Equiv.First, Equiv.Second);
EXPECT_EQ(Result, EquivalenceError::Success)
<< "couldn't add equivalence between " << Equiv.First << " and "
<< Equiv.Second;
}
using CanonKey = llvm::ItaniumManglingCanonicalizer::Key;
std::map<const EquivalenceClass*, CanonKey> Keys;
if (CanonicalizeFirst)
for (const auto &Class : Testcase.Classes)
Keys.insert({&Class, Canonicalizer.canonicalize(*Class.begin())});
std::map<CanonKey, llvm::StringRef> Found;
for (const auto &Class : Testcase.Classes) {
CanonKey ClassKey = Keys[&Class];
for (llvm::StringRef Str : Class) {
CanonKey ThisKey = CanonicalizeFirst
? Canonicalizer.lookup(std::string(Str))
: Canonicalizer.canonicalize(Str);
EXPECT_NE(ThisKey, CanonKey()) << "couldn't canonicalize " << Str;
if (ClassKey) {
EXPECT_EQ(ThisKey, ClassKey)
<< Str << " not in the same class as " << *Class.begin();
} else {
ClassKey = ThisKey;
}
}
EXPECT_TRUE(Found.insert({ClassKey, *Class.begin()}).second)
<< *Class.begin() << " is in the same class as " << Found[ClassKey];
}
}
}
TEST(ItaniumManglingCanonicalizerTest, TestCanonicalize) {
testTestcases<false>(getTestcases());
}
TEST(ItaniumManglingCanonicalizerTest, TestLookup) {
testTestcases<true>(getTestcases());
}
TEST(ItaniumManglingCanonicalizerTest, TestForwardTemplateReference) {
testTestcases<false>(getForwardTemplateReferenceTestcases());
}
TEST(ItaniumManglingCanonicalizerTest, TestInvalidManglings) {
llvm::ItaniumManglingCanonicalizer Canonicalizer;
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "", "1X"),
EquivalenceError::InvalidFirstMangling);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1X", "1ab"),
EquivalenceError::InvalidSecondMangling);
EXPECT_EQ(Canonicalizer.canonicalize("_Z3fooE"),
llvm::ItaniumManglingCanonicalizer::Key());
EXPECT_EQ(Canonicalizer.canonicalize("_Zfoo"),
llvm::ItaniumManglingCanonicalizer::Key());
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Name, "N1XcvT_I1AEE",
"1f"),
EquivalenceError::InvalidFirstMangling);
}
TEST(ItaniumManglingCanonicalizerTest, TestBadEquivalenceOrder) {
llvm::ItaniumManglingCanonicalizer Canonicalizer;
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1P1XE", "N1Q1XE"),
EquivalenceError::Success);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1P", "1Q"),
EquivalenceError::ManglingAlreadyUsed);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1C1XE", "N1A1YE"),
EquivalenceError::Success);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1A", "1B"),
EquivalenceError::Success);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1C", "1D"),
EquivalenceError::Success);
EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1B", "1D"),
EquivalenceError::ManglingAlreadyUsed);
}
}