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 "gtest/gtest.h"
16 
17 using namespace lldb_private;
18 using namespace lldb_private::postfix;
19 
20 static std::string ToString(BinaryOpNode::OpType type) {
21   switch (type) {
22   case BinaryOpNode::Align:
23     return "@";
24   case BinaryOpNode::Minus:
25     return "-";
26   case BinaryOpNode::Plus:
27     return "+";
28   }
29   llvm_unreachable("Fully covered switch!");
30 }
31 
32 static std::string ToString(UnaryOpNode::OpType type) {
33   switch (type) {
34   case UnaryOpNode::Deref:
35     return "^";
36   }
37   llvm_unreachable("Fully covered switch!");
38 }
39 
40 struct ASTPrinter : public Visitor<std::string> {
41 protected:
42   std::string Visit(BinaryOpNode &binary, Node *&) override {
43     return llvm::formatv("{0}({1}, {2})", ToString(binary.GetOpType()),
44                          Dispatch(binary.Left()), Dispatch(binary.Right()));
45   }
46 
47   std::string Visit(InitialValueNode &, Node *&) override { return "InitialValue"; }
48 
49   std::string Visit(IntegerNode &integer, Node *&) override {
50     return llvm::formatv("int({0})", integer.GetValue());
51   }
52 
53   std::string Visit(RegisterNode &reg, Node *&) override {
54     return llvm::formatv("reg({0})", reg.GetRegNum());
55   }
56 
57   std::string Visit(SymbolNode &symbol, Node *&) override {
58     return symbol.GetName();
59   }
60 
61   std::string Visit(UnaryOpNode &unary, Node *&) override {
62     return llvm::formatv("{0}({1})", ToString(unary.GetOpType()),
63                          Dispatch(unary.Operand()));
64   }
65 
66 public:
67   static std::string Print(Node *node) {
68     if (node)
69       return ASTPrinter().Dispatch(node);
70     return "nullptr";
71   }
72 };
73 
74 static std::string ParseAndStringify(llvm::StringRef expr) {
75   llvm::BumpPtrAllocator alloc;
76   return ASTPrinter::Print(Parse(expr, alloc));
77 }
78 
79 TEST(PostfixExpression, Parse) {
80   EXPECT_EQ("int(47)", ParseAndStringify("47"));
81   EXPECT_EQ("$foo", ParseAndStringify("$foo"));
82   EXPECT_EQ("+(int(1), int(2))", ParseAndStringify("1 2 +"));
83   EXPECT_EQ("-(int(1), int(2))", ParseAndStringify("1 2 -"));
84   EXPECT_EQ("@(int(1), int(2))", ParseAndStringify("1 2 @"));
85   EXPECT_EQ("+(int(1), +(int(2), int(3)))", ParseAndStringify("1 2 3 + +"));
86   EXPECT_EQ("+(+(int(1), int(2)), int(3))", ParseAndStringify("1 2 + 3 +"));
87   EXPECT_EQ("^(int(1))", ParseAndStringify("1 ^"));
88   EXPECT_EQ("^(^(int(1)))", ParseAndStringify("1 ^ ^"));
89   EXPECT_EQ("^(+(int(1), ^(int(2))))", ParseAndStringify("1 2 ^ + ^"));
90   EXPECT_EQ("-($foo, int(47))", ParseAndStringify("$foo 47 -"));
91   EXPECT_EQ("+(int(47), int(-42))", ParseAndStringify("47 -42 +"));
92 
93   EXPECT_EQ("nullptr", ParseAndStringify("+"));
94   EXPECT_EQ("nullptr", ParseAndStringify("^"));
95   EXPECT_EQ("nullptr", ParseAndStringify("1 +"));
96   EXPECT_EQ("nullptr", ParseAndStringify("1 2 ^"));
97   EXPECT_EQ("nullptr", ParseAndStringify("1 2 3 +"));
98   EXPECT_EQ("nullptr", ParseAndStringify("^ 1"));
99   EXPECT_EQ("nullptr", ParseAndStringify("+ 1 2"));
100   EXPECT_EQ("nullptr", ParseAndStringify("1 + 2"));
101   EXPECT_EQ("nullptr", ParseAndStringify("1 2"));
102   EXPECT_EQ("nullptr", ParseAndStringify(""));
103 }
104 
105 static std::string ParseAndGenerateDWARF(llvm::StringRef expr) {
106   llvm::BumpPtrAllocator alloc;
107   Node *ast = Parse(expr, alloc);
108   if (!ast)
109     return "Parse failed.";
110   if (!ResolveSymbols(ast, [&](SymbolNode &symbol) -> Node * {
111         if (symbol.GetName() == "INIT")
112           return MakeNode<InitialValueNode>(alloc);
113 
114         uint32_t num;
115         if (to_integer(symbol.GetName().drop_front(), num))
116           return MakeNode<RegisterNode>(alloc, num);
117         return nullptr;
118       })) {
119     return "Resolution failed.";
120   }
121 
122   const size_t addr_size = 4;
123   StreamString dwarf(Stream::eBinary, addr_size, lldb::eByteOrderLittle);
124   ToDWARF(*ast, dwarf);
125 
126   // print dwarf expression to comparable textual representation
127   DataExtractor extractor(dwarf.GetData(), dwarf.GetSize(),
128                           lldb::eByteOrderLittle, addr_size);
129 
130   StreamString result;
131   if (!DWARFExpression::PrintDWARFExpression(result, extractor, addr_size,
132                                              /*dwarf_ref_size*/ 4,
133                                              /*location_expression*/ false)) {
134     return "DWARF printing failed.";
135   }
136 
137   return result.GetString();
138 }
139 
140 TEST(PostfixExpression, ToDWARF) {
141   EXPECT_EQ("DW_OP_consts +0", ParseAndGenerateDWARF("0"));
142 
143   EXPECT_EQ("DW_OP_breg1 +0", ParseAndGenerateDWARF("R1"));
144 
145   EXPECT_EQ("DW_OP_bregx 65 0", ParseAndGenerateDWARF("R65"));
146 
147   EXPECT_EQ("DW_OP_pick 0x00", ParseAndGenerateDWARF("INIT"));
148 
149   EXPECT_EQ("DW_OP_pick 0x00, DW_OP_pick 0x01, DW_OP_plus ",
150             ParseAndGenerateDWARF("INIT INIT +"));
151 
152   EXPECT_EQ("DW_OP_breg1 +0, DW_OP_pick 0x01, DW_OP_plus ",
153             ParseAndGenerateDWARF("R1 INIT +"));
154 
155   EXPECT_EQ("DW_OP_consts +1, DW_OP_pick 0x01, DW_OP_deref , DW_OP_plus ",
156             ParseAndGenerateDWARF("1 INIT ^ +"));
157 
158   EXPECT_EQ("DW_OP_consts +4, DW_OP_consts +5, DW_OP_plus ",
159             ParseAndGenerateDWARF("4 5 +"));
160 
161   EXPECT_EQ("DW_OP_consts +4, DW_OP_consts +5, DW_OP_minus ",
162             ParseAndGenerateDWARF("4 5 -"));
163 
164   EXPECT_EQ("DW_OP_consts +4, DW_OP_deref ", ParseAndGenerateDWARF("4 ^"));
165 
166   EXPECT_EQ("DW_OP_breg6 +0, DW_OP_consts +128, DW_OP_lit1 "
167             ", DW_OP_minus , DW_OP_not , DW_OP_and ",
168             ParseAndGenerateDWARF("R6 128 @"));
169 }
170