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