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