1 //===-- PostfixExpressionTest.cpp -------------------------------*- C++ -*-===//
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/Expression/DWARFExpression.h"
11 #include "lldb/Utility/DataExtractor.h"
12 #include "lldb/Utility/StreamString.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 llvm::formatv("{0}({1}, {2})", ToString(binary.GetOpType()),
45                          Dispatch(binary.Left()), Dispatch(binary.Right()));
46   }
47 
48   std::string Visit(InitialValueNode &, Node *&) override { return "InitialValue"; }
49 
50   std::string Visit(IntegerNode &integer, Node *&) override {
51     return llvm::formatv("int({0})", integer.GetValue());
52   }
53 
54   std::string Visit(RegisterNode &reg, Node *&) override {
55     return llvm::formatv("reg({0})", reg.GetRegNum());
56   }
57 
58   std::string Visit(SymbolNode &symbol, Node *&) override {
59     return symbol.GetName();
60   }
61 
62   std::string Visit(UnaryOpNode &unary, Node *&) override {
63     return llvm::formatv("{0}({1})", ToString(unary.GetOpType()),
64                          Dispatch(unary.Operand()));
65   }
66 
67 public:
68   static std::string Print(Node *node) {
69     if (node)
70       return ASTPrinter().Dispatch(node);
71     return "nullptr";
72   }
73 };
74 
75 static std::string ParseOneAndStringify(llvm::StringRef expr) {
76   llvm::BumpPtrAllocator alloc;
77   return ASTPrinter::Print(ParseOneExpression(expr, alloc));
78 }
79 
80 TEST(PostfixExpression, ParseOneExpression) {
81   EXPECT_EQ("int(47)", ParseOneAndStringify("47"));
82   EXPECT_EQ("$foo", ParseOneAndStringify("$foo"));
83   EXPECT_EQ("+(int(1), int(2))", ParseOneAndStringify("1 2 +"));
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), int(3)))", ParseOneAndStringify("1 2 3 + +"));
87   EXPECT_EQ("+(+(int(1), int(2)), int(3))", ParseOneAndStringify("1 2 + 3 +"));
88   EXPECT_EQ("^(int(1))", ParseOneAndStringify("1 ^"));
89   EXPECT_EQ("^(^(int(1)))", ParseOneAndStringify("1 ^ ^"));
90   EXPECT_EQ("^(+(int(1), ^(int(2))))", ParseOneAndStringify("1 2 ^ + ^"));
91   EXPECT_EQ("-($foo, int(47))", ParseOneAndStringify("$foo 47 -"));
92   EXPECT_EQ("+(int(47), int(-42))", ParseOneAndStringify("47 -42 +"));
93 
94   EXPECT_EQ("nullptr", ParseOneAndStringify("+"));
95   EXPECT_EQ("nullptr", ParseOneAndStringify("^"));
96   EXPECT_EQ("nullptr", ParseOneAndStringify("1 +"));
97   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 ^"));
98   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2 3 +"));
99   EXPECT_EQ("nullptr", ParseOneAndStringify("^ 1"));
100   EXPECT_EQ("nullptr", ParseOneAndStringify("+ 1 2"));
101   EXPECT_EQ("nullptr", ParseOneAndStringify("1 + 2"));
102   EXPECT_EQ("nullptr", ParseOneAndStringify("1 2"));
103   EXPECT_EQ("nullptr", ParseOneAndStringify(""));
104 }
105 
106 static std::vector<std::pair<std::string, std::string>>
107 ParseFPOAndStringify(llvm::StringRef prog) {
108   llvm::BumpPtrAllocator alloc;
109   std::vector<std::pair<llvm::StringRef, Node *>> parsed =
110       ParseFPOProgram(prog, alloc);
111   std::vector<std::pair<std::string, std::string>> result;
112   for (const auto &p : parsed)
113     result.emplace_back(p.first, ASTPrinter::Print(p.second));
114   return result;
115 }
116 
117 TEST(PostfixExpression, ParseFPOProgram) {
118   EXPECT_THAT(ParseFPOAndStringify("a 1 ="),
119               testing::ElementsAre(std::make_pair("a", "int(1)")));
120   EXPECT_THAT(ParseFPOAndStringify("a 1 = b 2 3 + ="),
121               testing::ElementsAre(std::make_pair("a", "int(1)"),
122                                    std::make_pair("b", "+(int(2), int(3))")));
123 
124   EXPECT_THAT(ParseFPOAndStringify(""), testing::IsEmpty());
125   EXPECT_THAT(ParseFPOAndStringify("="), testing::IsEmpty());
126   EXPECT_THAT(ParseFPOAndStringify("a 1"), 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 }
131 
132 static std::string ParseAndGenerateDWARF(llvm::StringRef expr) {
133   llvm::BumpPtrAllocator alloc;
134   Node *ast = ParseOneExpression(expr, alloc);
135   if (!ast)
136     return "Parse failed.";
137   if (!ResolveSymbols(ast, [&](SymbolNode &symbol) -> Node * {
138         if (symbol.GetName() == "INIT")
139           return MakeNode<InitialValueNode>(alloc);
140 
141         uint32_t num;
142         if (to_integer(symbol.GetName().drop_front(), num))
143           return MakeNode<RegisterNode>(alloc, num);
144         return nullptr;
145       })) {
146     return "Resolution failed.";
147   }
148 
149   const size_t addr_size = 4;
150   StreamString dwarf(Stream::eBinary, addr_size, lldb::eByteOrderLittle);
151   ToDWARF(*ast, dwarf);
152 
153   // print dwarf expression to comparable textual representation
154   DataExtractor extractor(dwarf.GetData(), dwarf.GetSize(),
155                           lldb::eByteOrderLittle, addr_size);
156 
157   StreamString result;
158   if (!DWARFExpression::PrintDWARFExpression(result, extractor, addr_size,
159                                              /*dwarf_ref_size*/ 4,
160                                              /*location_expression*/ false)) {
161     return "DWARF printing failed.";
162   }
163 
164   return result.GetString();
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 65 0", ParseAndGenerateDWARF("R65"));
173 
174   EXPECT_EQ("DW_OP_pick 0x00", ParseAndGenerateDWARF("INIT"));
175 
176   EXPECT_EQ("DW_OP_pick 0x00, DW_OP_pick 0x01, DW_OP_plus ",
177             ParseAndGenerateDWARF("INIT INIT +"));
178 
179   EXPECT_EQ("DW_OP_breg1 +0, DW_OP_pick 0x01, DW_OP_plus ",
180             ParseAndGenerateDWARF("R1 INIT +"));
181 
182   EXPECT_EQ("DW_OP_consts +1, DW_OP_pick 0x01, 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 "
194             ", DW_OP_minus , DW_OP_not , DW_OP_and ",
195             ParseAndGenerateDWARF("R6 128 @"));
196 }
197