xref: /llvm-project/lldb/unittests/Symbol/PostfixExpressionTest.cpp (revision d7796855b87911b8ae6c726ab5df4949f173dbd2)
1 //===-- PostfixExpressionTest.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Symbol/PostfixExpression.h"
10 #include "lldb/Utility/DataExtractor.h"
11 #include "lldb/Utility/StreamString.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/DebugInfo/DIContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
15 #include "llvm/Support/FormatVariadic.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 
20 using namespace lldb_private;
21 using namespace lldb_private::postfix;
22 
23 static std::string ToString(BinaryOpNode::OpType type) {
24   switch (type) {
25   case BinaryOpNode::Align:
26     return "@";
27   case BinaryOpNode::Minus:
28     return "-";
29   case BinaryOpNode::Plus:
30     return "+";
31   }
32   llvm_unreachable("Fully covered switch!");
33 }
34 
35 static std::string ToString(UnaryOpNode::OpType type) {
36   switch (type) {
37   case UnaryOpNode::Deref:
38     return "^";
39   }
40   llvm_unreachable("Fully covered switch!");
41 }
42 
43 struct ASTPrinter : public Visitor<std::string> {
44 protected:
45   std::string Visit(BinaryOpNode &binary, Node *&) override {
46     return std::string(
47         llvm::formatv("{0}({1}, {2})", ToString(binary.GetOpType()),
48                       Dispatch(binary.Left()), Dispatch(binary.Right())));
49   }
50 
51   std::string Visit(InitialValueNode &, Node *&) override { return "InitialValue"; }
52 
53   std::string Visit(IntegerNode &integer, Node *&) override {
54     return std::string(llvm::formatv("int({0})", integer.GetValue()));
55   }
56 
57   std::string Visit(RegisterNode &reg, Node *&) override {
58     return std::string(llvm::formatv("reg({0})", reg.GetRegNum()));
59   }
60 
61   std::string Visit(SymbolNode &symbol, Node *&) override {
62     return std::string(symbol.GetName());
63   }
64 
65   std::string Visit(UnaryOpNode &unary, Node *&) override {
66     return std::string(llvm::formatv("{0}({1})", ToString(unary.GetOpType()),
67                                      Dispatch(unary.Operand())));
68   }
69 
70 public:
71   static std::string Print(Node *node) {
72     if (node)
73       return ASTPrinter().Dispatch(node);
74     return "nullptr";
75   }
76 };
77 
78 static std::string ParseOneAndStringify(llvm::StringRef expr) {
79   llvm::BumpPtrAllocator alloc;
80   return ASTPrinter::Print(ParseOneExpression(expr, alloc));
81 }
82 
83 TEST(PostfixExpression, ParseOneExpression) {
84   EXPECT_EQ("int(47)", ParseOneAndStringify("47"));
85   EXPECT_EQ("$foo", ParseOneAndStringify("$foo"));
86   EXPECT_EQ("+(int(1), int(2))", ParseOneAndStringify("1 2 +"));
87   EXPECT_EQ("-(int(1), int(2))", ParseOneAndStringify("1 2 -"));
88   EXPECT_EQ("@(int(1), int(2))", ParseOneAndStringify("1 2 @"));
89   EXPECT_EQ("+(int(1), +(int(2), int(3)))", ParseOneAndStringify("1 2 3 + +"));
90   EXPECT_EQ("+(+(int(1), int(2)), int(3))", ParseOneAndStringify("1 2 + 3 +"));
91   EXPECT_EQ("^(int(1))", ParseOneAndStringify("1 ^"));
92   EXPECT_EQ("^(^(int(1)))", ParseOneAndStringify("1 ^ ^"));
93   EXPECT_EQ("^(+(int(1), ^(int(2))))", ParseOneAndStringify("1 2 ^ + ^"));
94   EXPECT_EQ("-($foo, int(47))", ParseOneAndStringify("$foo 47 -"));
95   EXPECT_EQ("+(int(47), int(-42))", ParseOneAndStringify("47 -42 +"));
96 
97   EXPECT_EQ("nullptr", ParseOneAndStringify("+"));
98   EXPECT_EQ("nullptr", ParseOneAndStringify("^"));
99   EXPECT_EQ("nullptr", ParseOneAndStringify("1 +"));
100   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 ^"));
101   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 3 +"));
102   EXPECT_EQ("nullptr", ParseOneAndStringify("^ 1"));
103   EXPECT_EQ("nullptr", ParseOneAndStringify("+ 1 2"));
104   EXPECT_EQ("nullptr", ParseOneAndStringify("1 + 2"));
105   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2"));
106   EXPECT_EQ("nullptr", ParseOneAndStringify(""));
107 }
108 
109 static std::vector<std::pair<std::string, std::string>>
110 ParseFPOAndStringify(llvm::StringRef prog) {
111   llvm::BumpPtrAllocator alloc;
112   std::vector<std::pair<llvm::StringRef, Node *>> parsed =
113       ParseFPOProgram(prog, alloc);
114   std::vector<std::pair<std::string, std::string>> result;
115   for (const auto &p : parsed)
116     result.emplace_back(p.first.str(), ASTPrinter::Print(p.second));
117   return result;
118 }
119 
120 TEST(PostfixExpression, ParseFPOProgram) {
121   EXPECT_THAT(ParseFPOAndStringify("a 1 ="),
122               testing::ElementsAre(std::make_pair("a", "int(1)")));
123   EXPECT_THAT(ParseFPOAndStringify("a 1 = b 2 3 + ="),
124               testing::ElementsAre(std::make_pair("a", "int(1)"),
125                                    std::make_pair("b", "+(int(2), int(3))")));
126 
127   EXPECT_THAT(ParseFPOAndStringify(""), testing::IsEmpty());
128   EXPECT_THAT(ParseFPOAndStringify("="), testing::IsEmpty());
129   EXPECT_THAT(ParseFPOAndStringify("a 1"), testing::IsEmpty());
130   EXPECT_THAT(ParseFPOAndStringify("a 1 = ="), testing::IsEmpty());
131   EXPECT_THAT(ParseFPOAndStringify("a 1 + ="), testing::IsEmpty());
132   EXPECT_THAT(ParseFPOAndStringify("= a 1 ="), testing::IsEmpty());
133 }
134 
135 static std::string ParseAndGenerateDWARF(llvm::StringRef expr) {
136   llvm::BumpPtrAllocator alloc;
137   Node *ast = ParseOneExpression(expr, alloc);
138   if (!ast)
139     return "Parse failed.";
140   if (!ResolveSymbols(ast, [&](SymbolNode &symbol) -> Node * {
141         if (symbol.GetName() == "INIT")
142           return MakeNode<InitialValueNode>(alloc);
143 
144         uint32_t num;
145         if (to_integer(symbol.GetName().drop_front(), num))
146           return MakeNode<RegisterNode>(alloc, num);
147         return nullptr;
148       })) {
149     return "Resolution failed.";
150   }
151 
152   const size_t addr_size = 4;
153   StreamString dwarf(Stream::eBinary, addr_size, lldb::eByteOrderLittle);
154   ToDWARF(*ast, dwarf);
155 
156   // print dwarf expression to comparable textual representation
157   llvm::DataExtractor extractor(dwarf.GetString(), /*IsLittleEndian=*/true,
158                                 addr_size);
159 
160   std::string result;
161   llvm::raw_string_ostream os(result);
162   llvm::DWARFExpression(extractor, addr_size, llvm::dwarf::DWARF32)
163       .print(os, llvm::DIDumpOptions(), nullptr);
164   return result;
165 }
166 
167 TEST(PostfixExpression, ToDWARF) {
168   EXPECT_EQ("DW_OP_consts +0", ParseAndGenerateDWARF("0"));
169 
170   EXPECT_EQ("DW_OP_breg1 +0", ParseAndGenerateDWARF("R1"));
171 
172   EXPECT_EQ("DW_OP_bregx 0x41 +0", ParseAndGenerateDWARF("R65"));
173 
174   EXPECT_EQ("DW_OP_pick 0x0", ParseAndGenerateDWARF("INIT"));
175 
176   EXPECT_EQ("DW_OP_pick 0x0, DW_OP_pick 0x1, DW_OP_plus",
177             ParseAndGenerateDWARF("INIT INIT +"));
178 
179   EXPECT_EQ("DW_OP_breg1 +0, DW_OP_pick 0x1, DW_OP_plus",
180             ParseAndGenerateDWARF("R1 INIT +"));
181 
182   EXPECT_EQ("DW_OP_consts +1, DW_OP_pick 0x1, DW_OP_deref, DW_OP_plus",
183             ParseAndGenerateDWARF("1 INIT ^ +"));
184 
185   EXPECT_EQ("DW_OP_consts +4, DW_OP_consts +5, DW_OP_plus",
186             ParseAndGenerateDWARF("4 5 +"));
187 
188   EXPECT_EQ("DW_OP_consts +4, DW_OP_consts +5, DW_OP_minus",
189             ParseAndGenerateDWARF("4 5 -"));
190 
191   EXPECT_EQ("DW_OP_consts +4, DW_OP_deref", ParseAndGenerateDWARF("4 ^"));
192 
193   EXPECT_EQ("DW_OP_breg6 +0, DW_OP_consts +128, DW_OP_lit1, DW_OP_minus, "
194             "DW_OP_not, DW_OP_and",
195             ParseAndGenerateDWARF("R6 128 @"));
196 }
197