Index: include/lldb/Symbol/PostfixExpression.h =================================================================== --- include/lldb/Symbol/PostfixExpression.h +++ include/lldb/Symbol/PostfixExpression.h @@ -17,6 +17,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" +#include namespace lldb_private { @@ -211,7 +212,10 @@ /// Parse the given postfix expression. The parsed nodes are placed into the /// provided allocator. -Node *Parse(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc); +Node *ParseOneExpression(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc); + +std::vector> +ParseFPOProgram(llvm::StringRef prog, llvm::BumpPtrAllocator &alloc); /// Serialize the given expression tree as DWARF. The result is written into the /// given stream. The AST should not contain any SymbolNodes. If the expression Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -430,7 +430,7 @@ while (auto rule = GetRule(unwind_rules)) { node_alloc.Reset(); llvm::StringRef lhs = rule->first; - postfix::Node *rhs = postfix::Parse(rule->second, node_alloc); + postfix::Node *rhs = postfix::ParseOneExpression(rule->second, node_alloc); if (!rhs) { LLDB_LOG(log, "Could not parse `{0}` as unwind rhs.", rule->second); return false; Index: source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp =================================================================== --- source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp +++ source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp @@ -51,54 +51,23 @@ return npdb::GetLLDBRegisterNumber(arch_type, reg_id); } -static bool ParseFPOSingleAssignmentProgram(llvm::StringRef program, - llvm::BumpPtrAllocator &alloc, - llvm::StringRef ®ister_name, - Node *&ast) { - // lvalue of assignment is always first token - // rvalue program goes next - std::tie(register_name, program) = getToken(program); - if (register_name.empty()) - return false; - - ast = Parse(program, alloc); - return ast != nullptr; -} - -static Node *ParseFPOProgram(llvm::StringRef program, +static Node *ResolveFPOProgram(llvm::StringRef program, llvm::StringRef register_name, llvm::Triple::ArchType arch_type, llvm::BumpPtrAllocator &alloc) { - llvm::DenseMap dependent_programs; - - size_t cur = 0; - while (true) { - size_t assign_index = program.find('=', cur); - if (assign_index == llvm::StringRef::npos) { - llvm::StringRef tail = program.slice(cur, llvm::StringRef::npos); - if (!tail.trim().empty()) { - // missing assign operator - return nullptr; - } - break; - } - llvm::StringRef assignment_program = program.slice(cur, assign_index); - - llvm::StringRef lvalue_name; - Node *rvalue_ast = nullptr; - if (!ParseFPOSingleAssignmentProgram(assignment_program, alloc, lvalue_name, - rvalue_ast)) { - return nullptr; - } - - lldbassert(rvalue_ast); + std::vector> parsed = + postfix::ParseFPOProgram(program, alloc); + for (auto it = parsed.begin(), end = parsed.end(); it != end; ++it) { // Emplace valid dependent subtrees to make target assignment independent // from predecessors. Resolve all other SymbolNodes as registers. bool success = - ResolveSymbols(rvalue_ast, [&](SymbolNode &symbol) -> Node * { - if (Node *node = dependent_programs.lookup(symbol.GetName())) - return node; + ResolveSymbols(it->second, [&](SymbolNode &symbol) -> Node * { + for (const auto &pair : llvm::make_range(parsed.begin(), it)) { + if (pair.first == symbol.GetName()) + return pair.second; + } + uint32_t reg_num = ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type); @@ -110,13 +79,10 @@ if (!success) return nullptr; - if (lvalue_name == register_name) { + if (it->first == register_name) { // found target assignment program - no need to parse further - return rvalue_ast; + return it->second; } - - dependent_programs[lvalue_name] = rvalue_ast; - cur = assign_index + 1; } return nullptr; @@ -127,7 +93,7 @@ llvm::Triple::ArchType arch_type, Stream &stream) { llvm::BumpPtrAllocator node_alloc; Node *target_program = - ParseFPOProgram(program, register_name, arch_type, node_alloc); + ResolveFPOProgram(program, register_name, arch_type, node_alloc); if (target_program == nullptr) { return false; } Index: source/Symbol/PostfixExpression.cpp =================================================================== --- source/Symbol/PostfixExpression.cpp +++ source/Symbol/PostfixExpression.cpp @@ -41,7 +41,8 @@ return llvm::None; } -Node *postfix::Parse(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc) { +Node *postfix::ParseOneExpression(llvm::StringRef expr, + llvm::BumpPtrAllocator &alloc) { llvm::SmallVector stack; llvm::StringRef token; @@ -83,6 +84,26 @@ return stack.back(); } +std::vector> +postfix::ParseFPOProgram(llvm::StringRef prog, llvm::BumpPtrAllocator &alloc) { + llvm::SmallVector exprs; + prog.split(exprs, '='); + if (exprs.empty() || !exprs.back().trim().empty()) + return {}; + exprs.pop_back(); + + std::vector> result; + for (llvm::StringRef expr : exprs) { + llvm::StringRef lhs; + std::tie(lhs, expr) = getToken(expr); + Node *rhs = ParseOneExpression(expr, alloc); + if (!rhs) + return {}; + result.emplace_back(lhs, rhs); + } + return result; +} + namespace { class SymbolResolver : public Visitor { public: Index: unittests/Symbol/PostfixExpressionTest.cpp =================================================================== --- unittests/Symbol/PostfixExpressionTest.cpp +++ unittests/Symbol/PostfixExpressionTest.cpp @@ -12,6 +12,7 @@ #include "lldb/Utility/StreamString.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using namespace lldb_private; @@ -71,40 +72,68 @@ } }; -static std::string ParseAndStringify(llvm::StringRef expr) { +static std::string ParseOneAndStringify(llvm::StringRef expr) { llvm::BumpPtrAllocator alloc; - return ASTPrinter::Print(Parse(expr, alloc)); + return ASTPrinter::Print(ParseOneExpression(expr, alloc)); } -TEST(PostfixExpression, Parse) { - EXPECT_EQ("int(47)", ParseAndStringify("47")); - EXPECT_EQ("$foo", ParseAndStringify("$foo")); - EXPECT_EQ("+(int(1), int(2))", ParseAndStringify("1 2 +")); - EXPECT_EQ("-(int(1), int(2))", ParseAndStringify("1 2 -")); - EXPECT_EQ("@(int(1), int(2))", ParseAndStringify("1 2 @")); - EXPECT_EQ("+(int(1), +(int(2), int(3)))", ParseAndStringify("1 2 3 + +")); - EXPECT_EQ("+(+(int(1), int(2)), int(3))", ParseAndStringify("1 2 + 3 +")); - EXPECT_EQ("^(int(1))", ParseAndStringify("1 ^")); - EXPECT_EQ("^(^(int(1)))", ParseAndStringify("1 ^ ^")); - EXPECT_EQ("^(+(int(1), ^(int(2))))", ParseAndStringify("1 2 ^ + ^")); - EXPECT_EQ("-($foo, int(47))", ParseAndStringify("$foo 47 -")); - EXPECT_EQ("+(int(47), int(-42))", ParseAndStringify("47 -42 +")); - - EXPECT_EQ("nullptr", ParseAndStringify("+")); - EXPECT_EQ("nullptr", ParseAndStringify("^")); - EXPECT_EQ("nullptr", ParseAndStringify("1 +")); - EXPECT_EQ("nullptr", ParseAndStringify("1 2 ^")); - EXPECT_EQ("nullptr", ParseAndStringify("1 2 3 +")); - EXPECT_EQ("nullptr", ParseAndStringify("^ 1")); - EXPECT_EQ("nullptr", ParseAndStringify("+ 1 2")); - EXPECT_EQ("nullptr", ParseAndStringify("1 + 2")); - EXPECT_EQ("nullptr", ParseAndStringify("1 2")); - EXPECT_EQ("nullptr", ParseAndStringify("")); +TEST(PostfixExpression, ParseOneExpression) { + EXPECT_EQ("int(47)", ParseOneAndStringify("47")); + EXPECT_EQ("$foo", ParseOneAndStringify("$foo")); + EXPECT_EQ("+(int(1), int(2))", ParseOneAndStringify("1 2 +")); + EXPECT_EQ("-(int(1), int(2))", ParseOneAndStringify("1 2 -")); + EXPECT_EQ("@(int(1), int(2))", ParseOneAndStringify("1 2 @")); + EXPECT_EQ("+(int(1), +(int(2), int(3)))", ParseOneAndStringify("1 2 3 + +")); + EXPECT_EQ("+(+(int(1), int(2)), int(3))", ParseOneAndStringify("1 2 + 3 +")); + EXPECT_EQ("^(int(1))", ParseOneAndStringify("1 ^")); + EXPECT_EQ("^(^(int(1)))", ParseOneAndStringify("1 ^ ^")); + EXPECT_EQ("^(+(int(1), ^(int(2))))", ParseOneAndStringify("1 2 ^ + ^")); + EXPECT_EQ("-($foo, int(47))", ParseOneAndStringify("$foo 47 -")); + EXPECT_EQ("+(int(47), int(-42))", ParseOneAndStringify("47 -42 +")); + + EXPECT_EQ("nullptr", ParseOneAndStringify("+")); + EXPECT_EQ("nullptr", ParseOneAndStringify("^")); + EXPECT_EQ("nullptr", ParseOneAndStringify("1 +")); + EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 ^")); + EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 3 +")); + EXPECT_EQ("nullptr", ParseOneAndStringify("^ 1")); + EXPECT_EQ("nullptr", ParseOneAndStringify("+ 1 2")); + EXPECT_EQ("nullptr", ParseOneAndStringify("1 + 2")); + EXPECT_EQ("nullptr", ParseOneAndStringify("1 2")); + EXPECT_EQ("nullptr", ParseOneAndStringify("")); +} + +static std::vector> +ParseFPOAndStringify(llvm::StringRef prog) { + llvm::BumpPtrAllocator alloc; + std::vector> parsed = + ParseFPOProgram(prog, alloc); + auto range = llvm::map_range( + parsed, [](const std::pair &pair) { + return std::make_pair(pair.first, ASTPrinter::Print(pair.second)); + }); + return std::vector>(range.begin(), + range.end()); +} + +TEST(PostfixExpression, ParseFPOProgram) { + EXPECT_THAT(ParseFPOAndStringify("a 1 ="), + testing::ElementsAre(std::make_pair("a", "int(1)"))); + EXPECT_THAT(ParseFPOAndStringify("a 1 = b 2 3 + ="), + testing::ElementsAre(std::make_pair("a", "int(1)"), + std::make_pair("b", "+(int(2), int(3))"))); + + EXPECT_THAT(ParseFPOAndStringify(""), testing::IsEmpty()); + EXPECT_THAT(ParseFPOAndStringify("="), testing::IsEmpty()); + EXPECT_THAT(ParseFPOAndStringify("a 1"), testing::IsEmpty()); + EXPECT_THAT(ParseFPOAndStringify("a 1 = ="), testing::IsEmpty()); + EXPECT_THAT(ParseFPOAndStringify("a 1 + ="), testing::IsEmpty()); + EXPECT_THAT(ParseFPOAndStringify("= a 1 ="), testing::IsEmpty()); } static std::string ParseAndGenerateDWARF(llvm::StringRef expr) { llvm::BumpPtrAllocator alloc; - Node *ast = Parse(expr, alloc); + Node *ast = ParseOneExpression(expr, alloc); if (!ast) return "Parse failed."; if (!ResolveSymbols(ast, [&](SymbolNode &symbol) -> Node * {