std::string &prefix,
std::string &suffix);
+ // When looking up functions, we take a user provided string which may be a
+ // partial match to the full demangled name and compare it to the actual
+ // demangled name to see if it matches as much as the user specified. An
+ // example of this is if the user provided A::my_function, but the
+ // symbol was really B::A::my_function. We want that to be
+ // a match. But we wouldn't want this to match AnotherA::my_function. The
+ // user is specifying a truncated path, not a truncated set of characters.
+ // This function does a language-aware comparison for those purposes.
+ virtual bool DemangledNameContainsPath(llvm::StringRef path,
+ ConstString demangled) const;
+
// if a language has a custom format for printing variable declarations that
// it wants LLDB to honor it should return an appropriate closure here
virtual DumpValueObjectOptions::DeclPrintingHelper GetDeclPrintingHelper();
while (i < sc_list.GetSize()) {
if (!sc_list.GetContextAtIndex(i, sc))
break;
- ConstString full_name(sc.GetFunctionName());
- if (full_name &&
- ::strstr(full_name.GetCString(), m_name.GetCString()) == nullptr) {
- sc_list.RemoveContextAtIndex(i);
- } else {
- ++i;
+
+ llvm::StringRef user_name = m_name.GetStringRef();
+ bool keep_it = true;
+ Language *language = Language::FindPlugin(sc.GetLanguage());
+ // If the symbol has a language, then let the language make the match.
+ // Otherwise just check that the demangled name contains the user name.
+ if (language)
+ keep_it = language->DemangledNameContainsPath(m_name.GetStringRef(),
+ sc.GetFunctionName());
+ else {
+ llvm::StringRef full_name = sc.GetFunctionName().GetStringRef();
+ // We always keep unnamed symbols:
+ if (!full_name.empty())
+ keep_it = full_name.contains(user_name);
}
+ if (keep_it)
+ ++i;
+ else
+ sc_list.RemoveContextAtIndex(i);
}
}
return res;
}
+bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
+ if (!m_parsed)
+ Parse();
+ // If we can't parse the incoming name, then just check that it contains path.
+ if (m_parse_error)
+ return m_full.GetStringRef().contains(path);
+
+ llvm::StringRef identifier;
+ llvm::StringRef context;
+ std::string path_str = path.str();
+ bool success
+ = CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(),
+ context,
+ identifier);
+ if (!success)
+ return m_full.GetStringRef().contains(path);
+
+ if (identifier != GetBasename())
+ return false;
+ // Incoming path only had an identifier, so we match.
+ if (context.empty())
+ return true;
+ // Incoming path has context but this method does not, no match.
+ if (m_context.empty())
+ return false;
+
+ llvm::StringRef haystack = m_context;
+ if (!haystack.consume_back(context))
+ return false;
+ if (haystack.empty() || !isalnum(haystack.back()))
+ return true;
+
+ return false;
+}
+
bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) {
// FIXME!! we should really run through all the known C++ Language plugins
// and ask each one if this is a C++ mangled name
return true;
}
+bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path,
+ ConstString demangled) const {
+ MethodName demangled_name(demangled);
+ return demangled_name.ContainsPath(path);
+}
+
bool CPlusPlusLanguage::ExtractContextAndIdentifier(
const char *name, llvm::StringRef &context, llvm::StringRef &identifier) {
if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name))
llvm::StringRef GetArguments();
llvm::StringRef GetQualifiers();
+
+ bool ContainsPath(llvm::StringRef path);
protected:
void Parse();
static llvm::StringRef GetPluginNameStatic() { return "cplusplus"; }
bool SymbolNameFitsToLanguage(Mangled mangled) const override;
+
+ bool DemangledNameContainsPath(llvm::StringRef path,
+ ConstString demangled) const override;
ConstString
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override;
return false;
}
+bool Language::DemangledNameContainsPath(llvm::StringRef path,
+ ConstString demangled) const {
+ // The base implementation does a simple contains comparision:
+ if (path.empty())
+ return false;
+ return demangled.GetStringRef().contains(path);
+}
+
DumpValueObjectOptions::DeclPrintingHelper Language::GetDeclPrintingHelper() {
return nullptr;
}
self.assertEquals(
bp.GetNumLocations(),
len(names),
- "Make sure we find the right number of breakpoint locations")
+ "Make sure we find the right number of breakpoint locations for {}".format(name))
bp_loc_names = list()
for bp_loc in bp:
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
bp_dicts = [
- {'name': 'func1', 'loc_names': ['a::c::func1()', 'b::c::func1()']},
- {'name': 'func2', 'loc_names': ['a::c::func2()', 'c::d::func2()']},
- {'name': 'func3', 'loc_names': ['a::c::func3()', 'b::c::func3()', 'c::d::func3()']},
+ {'name': 'func1', 'loc_names': ['a::c::func1()', 'aa::cc::func1()', 'b::c::func1()']},
+ {'name': 'func2', 'loc_names': ['a::c::func2()', 'aa::cc::func2()', 'c::d::func2()']},
+ {'name': 'func3', 'loc_names': ['a::c::func3()', 'aa::cc::func3()', 'b::c::func3()', 'c::d::func3()']},
{'name': 'c::func1', 'loc_names': ['a::c::func1()', 'b::c::func1()']},
{'name': 'c::func2', 'loc_names': ['a::c::func2()']},
{'name': 'c::func3', 'loc_names': ['a::c::func3()', 'b::c::func3()']},
c::~c() {}
}
+namespace aa {
+ class cc {
+ public:
+ cc();
+ ~cc();
+ void func1()
+ {
+ puts (__PRETTY_FUNCTION__);
+ }
+ void func2()
+ {
+ puts (__PRETTY_FUNCTION__);
+ }
+ void func3()
+ {
+ puts (__PRETTY_FUNCTION__);
+ }
+ };
+
+ cc::cc() {}
+ cc::~cc() {}
+}
+
namespace b {
class c {
public:
int main (int argc, char const *argv[])
{
a::c ac;
+ aa::cc aac;
b::c bc;
c::d cd;
ac.func1();
ac.func2();
ac.func3();
+ aac.func1();
+ aac.func2();
+ aac.func3();
bc.func1();
bc.func3();
cd.func2();
# Set the breakpoint, run to it, finish out.
bkpt = self.target.BreakpointCreateByName(func_name)
- self.assertTrue(bkpt.GetNumResolvedLocations() > 0)
+ self.assertTrue(bkpt.GetNumResolvedLocations() > 0, "Got wrong number of locations for {0}".format(func_name))
self.process.Continue()
# make sure we are again in out target function.
break_reexported = target.BreakpointCreateByName(
"reexport_to_indirect")
- self.assertTrue(break_reexported, VALID_BREAKPOINT)
+ self.assertEqual(break_reexported.GetNumLocations(), 1, VALID_BREAKPOINT)
# Now continue should take us to the second call through the indirect
# symbol:
}
}
+TEST(CPlusPlusLanguage, ContainsPath) {
+ CPlusPlusLanguage::MethodName
+ reference_1(ConstString("int foo::bar::func01(int a, double b)"));
+ CPlusPlusLanguage::MethodName
+ reference_2(ConstString("int foofoo::bar::func01(std::string a, int b)"));
+ CPlusPlusLanguage::MethodName reference_3(ConstString("int func01()"));
+ CPlusPlusLanguage::MethodName
+ reference_4(ConstString("bar::baz::operator bool()"));
+
+ EXPECT_TRUE(reference_1.ContainsPath("func01"));
+ EXPECT_TRUE(reference_1.ContainsPath("bar::func01"));
+ EXPECT_TRUE(reference_1.ContainsPath("foo::bar::func01"));
+ EXPECT_FALSE(reference_1.ContainsPath("func"));
+ EXPECT_FALSE(reference_1.ContainsPath("baz::func01"));
+ EXPECT_FALSE(reference_1.ContainsPath("::bar::func01"));
+ EXPECT_FALSE(reference_1.ContainsPath("::foo::baz::func01"));
+ EXPECT_FALSE(reference_1.ContainsPath("foo::bar::baz::func01"));
+
+ EXPECT_TRUE(reference_2.ContainsPath("foofoo::bar::func01"));
+ EXPECT_FALSE(reference_2.ContainsPath("foo::bar::func01"));
+
+ EXPECT_TRUE(reference_3.ContainsPath("func01"));
+ EXPECT_FALSE(reference_3.ContainsPath("func"));
+ EXPECT_FALSE(reference_3.ContainsPath("bar::func01"));
+
+ EXPECT_TRUE(reference_4.ContainsPath("operator bool"));
+ EXPECT_TRUE(reference_4.ContainsPath("baz::operator bool"));
+ EXPECT_TRUE(reference_4.ContainsPath("bar::baz::operator bool"));
+ EXPECT_FALSE(reference_4.ContainsPath("az::operator bool"));
+}
+
TEST(CPlusPlusLanguage, ExtractContextAndIdentifier) {
struct TestCase {
std::string input;