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 
92   EXPECT_EQ("nullptr", ParseAndStringify("+"));
93   EXPECT_EQ("nullptr", ParseAndStringify("^"));
94   EXPECT_EQ("nullptr", ParseAndStringify("1 +"));
95   EXPECT_EQ("nullptr", ParseAndStringify("1 2 ^"));
96   EXPECT_EQ("nullptr", ParseAndStringify("1 2 3 +"));
97   EXPECT_EQ("nullptr", ParseAndStringify("^ 1"));
98   EXPECT_EQ("nullptr", ParseAndStringify("+ 1 2"));
99   EXPECT_EQ("nullptr", ParseAndStringify("1 + 2"));
100   EXPECT_EQ("nullptr", ParseAndStringify("1 2"));
101   EXPECT_EQ("nullptr", ParseAndStringify(""));
102 }
103 
104 static std::string ParseAndGenerateDWARF(llvm::StringRef expr) {
105   llvm::BumpPtrAllocator alloc;
106   Node *ast = Parse(expr, alloc);
107   if (!ast)
108     return "Parse failed.";
109   if (!ResolveSymbols(ast, [&](SymbolNode &symbol) -> Node * {
110         if (symbol.GetName() == "INIT")
111           return MakeNode<InitialValueNode>(alloc);
112 
113         uint32_t num;
114         if (to_integer(symbol.GetName().drop_front(), num))
115           return MakeNode<RegisterNode>(alloc, num);
116         return nullptr;
117       })) {
118     return "Resolution failed.";
119   }
120 
121   const size_t addr_size = 4;
122   StreamString dwarf(Stream::eBinary, addr_size, lldb::eByteOrderLittle);
123   ToDWARF(*ast, dwarf);
124 
125   // print dwarf expression to comparable textual representation
126   DataExtractor extractor(dwarf.GetData(), dwarf.GetSize(),
127                           lldb::eByteOrderLittle, addr_size);
128 
129   StreamString result;
130   if (!DWARFExpression::PrintDWARFExpression(result, extractor, addr_size,
131                                              /*dwarf_ref_size*/ 4,
132                                              /*location_expression*/ false)) {
133     return "DWARF printing failed.";
134   }
135 
136   return result.GetString();
137 }
138 
139 TEST(PostfixExpression, ToDWARF) {
140   EXPECT_EQ("DW_OP_constu 0x0", ParseAndGenerateDWARF("0"));
141 
142   EXPECT_EQ("DW_OP_breg1 +0", ParseAndGenerateDWARF("R1"));
143 
144   EXPECT_EQ("DW_OP_bregx 65 0", ParseAndGenerateDWARF("R65"));
145 
146   EXPECT_EQ("DW_OP_pick 0x00", ParseAndGenerateDWARF("INIT"));
147 
148   EXPECT_EQ("DW_OP_pick 0x00, DW_OP_pick 0x01, DW_OP_plus ",
149             ParseAndGenerateDWARF("INIT INIT +"));
150 
151   EXPECT_EQ("DW_OP_breg1 +0, DW_OP_pick 0x01, DW_OP_plus ",
152             ParseAndGenerateDWARF("R1 INIT +"));
153 
154   EXPECT_EQ("DW_OP_constu 0x1, DW_OP_pick 0x01, DW_OP_deref , DW_OP_plus ",
155             ParseAndGenerateDWARF("1 INIT ^ +"));
156 
157   EXPECT_EQ("DW_OP_constu 0x4, DW_OP_constu 0x5, DW_OP_plus ",
158             ParseAndGenerateDWARF("4 5 +"));
159 
160   EXPECT_EQ("DW_OP_constu 0x4, DW_OP_constu 0x5, DW_OP_minus ",
161             ParseAndGenerateDWARF("4 5 -"));
162 
163   EXPECT_EQ("DW_OP_constu 0x4, DW_OP_deref ", ParseAndGenerateDWARF("4 ^"));
164 
165   EXPECT_EQ("DW_OP_breg6 +0, DW_OP_constu 0x80, DW_OP_lit1 "
166             ", DW_OP_minus , DW_OP_not , DW_OP_and ",
167             ParseAndGenerateDWARF("R6 128 @"));
168 }
169