xref: /freebsd/contrib/llvm-project/lldb/source/Utility/StructuredData.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1 //===-- StructuredData.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/Utility/StructuredData.h"
10 #include "lldb/Utility/FileSpec.h"
11 #include "lldb/Utility/Status.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "llvm/Support/MemoryBuffer.h"
14 #include <cerrno>
15 #include <cinttypes>
16 #include <cstdlib>
17 
18 using namespace lldb_private;
19 using namespace llvm;
20 
21 static StructuredData::ObjectSP ParseJSONValue(json::Value &value);
22 static StructuredData::ObjectSP ParseJSONObject(json::Object *object);
23 static StructuredData::ObjectSP ParseJSONArray(json::Array *array);
24 
25 StructuredData::ObjectSP StructuredData::ParseJSON(llvm::StringRef json_text) {
26   llvm::Expected<json::Value> value = json::parse(json_text);
27   if (!value) {
28     llvm::consumeError(value.takeError());
29     return nullptr;
30   }
31   return ParseJSONValue(*value);
32 }
33 
34 StructuredData::ObjectSP
35 StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
36   StructuredData::ObjectSP return_sp;
37 
38   auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath());
39   if (!buffer_or_error) {
40     error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.",
41                                     input_spec.GetPath(),
42                                     buffer_or_error.getError().message());
43     return return_sp;
44   }
45   llvm::Expected<json::Value> value =
46       json::parse(buffer_or_error.get()->getBuffer().str());
47   if (value)
48     return ParseJSONValue(*value);
49   error.SetErrorString(toString(value.takeError()));
50   return StructuredData::ObjectSP();
51 }
52 
53 bool StructuredData::IsRecordType(const ObjectSP object_sp) {
54   return object_sp->GetType() == lldb::eStructuredDataTypeArray ||
55          object_sp->GetType() == lldb::eStructuredDataTypeDictionary;
56 }
57 
58 static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {
59   if (json::Object *O = value.getAsObject())
60     return ParseJSONObject(O);
61 
62   if (json::Array *A = value.getAsArray())
63     return ParseJSONArray(A);
64 
65   if (auto s = value.getAsString())
66     return std::make_shared<StructuredData::String>(*s);
67 
68   if (auto b = value.getAsBoolean())
69     return std::make_shared<StructuredData::Boolean>(*b);
70 
71   if (auto u = value.getAsUINT64())
72     return std::make_shared<StructuredData::UnsignedInteger>(*u);
73 
74   if (auto i = value.getAsInteger())
75     return std::make_shared<StructuredData::SignedInteger>(*i);
76 
77   if (auto d = value.getAsNumber())
78     return std::make_shared<StructuredData::Float>(*d);
79 
80   if (auto n = value.getAsNull())
81     return std::make_shared<StructuredData::Null>();
82 
83   return StructuredData::ObjectSP();
84 }
85 
86 static StructuredData::ObjectSP ParseJSONObject(json::Object *object) {
87   auto dict_up = std::make_unique<StructuredData::Dictionary>();
88   for (auto &KV : *object) {
89     StringRef key = KV.first;
90     json::Value value = KV.second;
91     if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
92       dict_up->AddItem(key, value_sp);
93   }
94   return std::move(dict_up);
95 }
96 
97 static StructuredData::ObjectSP ParseJSONArray(json::Array *array) {
98   auto array_up = std::make_unique<StructuredData::Array>();
99   for (json::Value &value : *array) {
100     if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
101       array_up->AddItem(value_sp);
102   }
103   return std::move(array_up);
104 }
105 
106 StructuredData::ObjectSP
107 StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) {
108   if (GetType() == lldb::eStructuredDataTypeDictionary) {
109     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.');
110     llvm::StringRef key = match.first;
111     ObjectSP value = GetAsDictionary()->GetValueForKey(key);
112     if (!value)
113       return {};
114 
115     // Do we have additional words to descend?  If not, return the value
116     // we're at right now.
117     if (match.second.empty())
118       return value;
119 
120     return value->GetObjectForDotSeparatedPath(match.second);
121   }
122 
123   if (GetType() == lldb::eStructuredDataTypeArray) {
124     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('[');
125     if (match.second.empty())
126       return shared_from_this();
127 
128     uint64_t val = 0;
129     if (!llvm::to_integer(match.second, val, /* Base = */ 10))
130       return {};
131 
132     return GetAsArray()->GetItemAtIndex(val);
133   }
134 
135   return shared_from_this();
136 }
137 
138 void StructuredData::Object::DumpToStdout(bool pretty_print) const {
139   json::OStream stream(llvm::outs(), pretty_print ? 2 : 0);
140   Serialize(stream);
141 }
142 
143 void StructuredData::Array::Serialize(json::OStream &s) const {
144   s.arrayBegin();
145   for (const auto &item_sp : m_items) {
146     item_sp->Serialize(s);
147   }
148   s.arrayEnd();
149 }
150 
151 void StructuredData::Float::Serialize(json::OStream &s) const {
152   s.value(m_value);
153 }
154 
155 void StructuredData::Boolean::Serialize(json::OStream &s) const {
156   s.value(m_value);
157 }
158 
159 void StructuredData::String::Serialize(json::OStream &s) const {
160   s.value(m_value);
161 }
162 
163 void StructuredData::Dictionary::Serialize(json::OStream &s) const {
164   s.objectBegin();
165 
166   // To ensure the output format is always stable, we sort the dictionary by key
167   // first.
168   using Entry = std::pair<llvm::StringRef, ObjectSP>;
169   std::vector<Entry> sorted_entries;
170   for (const auto &pair : m_dict)
171     sorted_entries.push_back({pair.first(), pair.second});
172 
173   llvm::sort(sorted_entries);
174 
175   for (const auto &pair : sorted_entries) {
176     s.attributeBegin(pair.first);
177     pair.second->Serialize(s);
178     s.attributeEnd();
179   }
180   s.objectEnd();
181 }
182 
183 void StructuredData::Null::Serialize(json::OStream &s) const {
184   s.value(nullptr);
185 }
186 
187 void StructuredData::Generic::Serialize(json::OStream &s) const {
188   s.value(llvm::formatv("{0:X}", m_object));
189 }
190 
191 void StructuredData::Float::GetDescription(lldb_private::Stream &s) const {
192   s.Printf("%f", m_value);
193 }
194 
195 void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const {
196   s.Printf(m_value ? "True" : "False");
197 }
198 
199 void StructuredData::String::GetDescription(lldb_private::Stream &s) const {
200   s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str());
201 }
202 
203 void StructuredData::Array::GetDescription(lldb_private::Stream &s) const {
204   size_t index = 0;
205   size_t indentation_level = s.GetIndentLevel();
206   for (const auto &item_sp : m_items) {
207     // Sanitize.
208     if (!item_sp)
209       continue;
210 
211     // Reset original indentation level.
212     s.SetIndentLevel(indentation_level);
213     s.Indent();
214 
215     // Print key
216     s.Printf("[%zu]:", index++);
217 
218     // Return to new line and increase indentation if value is record type.
219     // Otherwise add spacing.
220     bool should_indent = IsRecordType(item_sp);
221     if (should_indent) {
222       s.EOL();
223       s.IndentMore();
224     } else {
225       s.PutChar(' ');
226     }
227 
228     // Print value and new line if now last pair.
229     item_sp->GetDescription(s);
230     if (item_sp != *(--m_items.end()))
231       s.EOL();
232 
233     // Reset indentation level if it was incremented previously.
234     if (should_indent)
235       s.IndentLess();
236   }
237 }
238 
239 void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const {
240   size_t indentation_level = s.GetIndentLevel();
241 
242   // To ensure the output format is always stable, we sort the dictionary by key
243   // first.
244   using Entry = std::pair<llvm::StringRef, ObjectSP>;
245   std::vector<Entry> sorted_entries;
246   for (const auto &pair : m_dict)
247     sorted_entries.push_back({pair.first(), pair.second});
248 
249   llvm::sort(sorted_entries);
250 
251   for (auto iter = sorted_entries.begin(); iter != sorted_entries.end();
252        iter++) {
253     // Sanitize.
254     if (iter->first.empty() || !iter->second)
255       continue;
256 
257     // Reset original indentation level.
258     s.SetIndentLevel(indentation_level);
259     s.Indent();
260 
261     // Print key.
262     s.Format("{0}:", iter->first);
263 
264     // Return to new line and increase indentation if value is record type.
265     // Otherwise add spacing.
266     bool should_indent = IsRecordType(iter->second);
267     if (should_indent) {
268       s.EOL();
269       s.IndentMore();
270     } else {
271       s.PutChar(' ');
272     }
273 
274     // Print value and new line if now last pair.
275     iter->second->GetDescription(s);
276     if (std::next(iter) != sorted_entries.end())
277       s.EOL();
278 
279     // Reset indentation level if it was incremented previously.
280     if (should_indent)
281       s.IndentLess();
282   }
283 }
284 
285 void StructuredData::Null::GetDescription(lldb_private::Stream &s) const {
286   s.Printf("NULL");
287 }
288 
289 void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const {
290   s.Printf("%p", m_object);
291 }
292