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

source code of lldb/unittests/Symbol/PostfixExpressionTest.cpp