15ffd83dbSDimitry Andric //===-- StructuredData.cpp ------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Utility/StructuredData.h"
100b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h"
110b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
1206c3fb27SDimitry Andric #include "llvm/ADT/StringExtras.h"
130b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
140b57cec5SDimitry Andric #include <cerrno>
15fe6060f1SDimitry Andric #include <cinttypes>
160b57cec5SDimitry Andric #include <cstdlib>
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric using namespace lldb_private;
199dba64beSDimitry Andric using namespace llvm;
200b57cec5SDimitry Andric 
219dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONValue(json::Value &value);
229dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONObject(json::Object *object);
239dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONArray(json::Array *array);
249dba64beSDimitry Andric 
ParseJSON(llvm::StringRef json_text)2506c3fb27SDimitry Andric StructuredData::ObjectSP StructuredData::ParseJSON(llvm::StringRef json_text) {
269dba64beSDimitry Andric   llvm::Expected<json::Value> value = json::parse(json_text);
279dba64beSDimitry Andric   if (!value) {
289dba64beSDimitry Andric     llvm::consumeError(value.takeError());
299dba64beSDimitry Andric     return nullptr;
309dba64beSDimitry Andric   }
319dba64beSDimitry Andric   return ParseJSONValue(*value);
329dba64beSDimitry Andric }
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric StructuredData::ObjectSP
ParseJSONFromFile(const FileSpec & input_spec,Status & error)350b57cec5SDimitry Andric StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
360b57cec5SDimitry Andric   StructuredData::ObjectSP return_sp;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric   auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath());
390b57cec5SDimitry Andric   if (!buffer_or_error) {
400b57cec5SDimitry Andric     error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.",
410b57cec5SDimitry Andric                                     input_spec.GetPath(),
420b57cec5SDimitry Andric                                     buffer_or_error.getError().message());
430b57cec5SDimitry Andric     return return_sp;
440b57cec5SDimitry Andric   }
45e8d8bef9SDimitry Andric   llvm::Expected<json::Value> value =
46e8d8bef9SDimitry Andric       json::parse(buffer_or_error.get()->getBuffer().str());
47e8d8bef9SDimitry Andric   if (value)
48e8d8bef9SDimitry Andric     return ParseJSONValue(*value);
49e8d8bef9SDimitry Andric   error.SetErrorString(toString(value.takeError()));
50e8d8bef9SDimitry Andric   return StructuredData::ObjectSP();
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric 
IsRecordType(const ObjectSP object_sp)53bdd1243dSDimitry Andric bool StructuredData::IsRecordType(const ObjectSP object_sp) {
54bdd1243dSDimitry Andric   return object_sp->GetType() == lldb::eStructuredDataTypeArray ||
55bdd1243dSDimitry Andric          object_sp->GetType() == lldb::eStructuredDataTypeDictionary;
56bdd1243dSDimitry Andric }
57bdd1243dSDimitry Andric 
ParseJSONValue(json::Value & value)589dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {
599dba64beSDimitry Andric   if (json::Object *O = value.getAsObject())
609dba64beSDimitry Andric     return ParseJSONObject(O);
610b57cec5SDimitry Andric 
629dba64beSDimitry Andric   if (json::Array *A = value.getAsArray())
639dba64beSDimitry Andric     return ParseJSONArray(A);
640b57cec5SDimitry Andric 
65e8d8bef9SDimitry Andric   if (auto s = value.getAsString())
66e8d8bef9SDimitry Andric     return std::make_shared<StructuredData::String>(*s);
679dba64beSDimitry Andric 
68e8d8bef9SDimitry Andric   if (auto b = value.getAsBoolean())
69e8d8bef9SDimitry Andric     return std::make_shared<StructuredData::Boolean>(*b);
709dba64beSDimitry Andric 
7106c3fb27SDimitry Andric   if (auto u = value.getAsUINT64())
7206c3fb27SDimitry Andric     return std::make_shared<StructuredData::UnsignedInteger>(*u);
7306c3fb27SDimitry Andric 
74e8d8bef9SDimitry Andric   if (auto i = value.getAsInteger())
7506c3fb27SDimitry Andric     return std::make_shared<StructuredData::SignedInteger>(*i);
769dba64beSDimitry Andric 
77e8d8bef9SDimitry Andric   if (auto d = value.getAsNumber())
78e8d8bef9SDimitry Andric     return std::make_shared<StructuredData::Float>(*d);
799dba64beSDimitry Andric 
80bdd1243dSDimitry Andric   if (auto n = value.getAsNull())
81bdd1243dSDimitry Andric     return std::make_shared<StructuredData::Null>();
82bdd1243dSDimitry Andric 
839dba64beSDimitry Andric   return StructuredData::ObjectSP();
849dba64beSDimitry Andric }
859dba64beSDimitry Andric 
ParseJSONObject(json::Object * object)869dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONObject(json::Object *object) {
879dba64beSDimitry Andric   auto dict_up = std::make_unique<StructuredData::Dictionary>();
889dba64beSDimitry Andric   for (auto &KV : *object) {
899dba64beSDimitry Andric     StringRef key = KV.first;
909dba64beSDimitry Andric     json::Value value = KV.second;
919dba64beSDimitry Andric     if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
920b57cec5SDimitry Andric       dict_up->AddItem(key, value_sp);
930b57cec5SDimitry Andric   }
94480093f4SDimitry Andric   return std::move(dict_up);
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
ParseJSONArray(json::Array * array)979dba64beSDimitry Andric static StructuredData::ObjectSP ParseJSONArray(json::Array *array) {
989dba64beSDimitry Andric   auto array_up = std::make_unique<StructuredData::Array>();
999dba64beSDimitry Andric   for (json::Value &value : *array) {
1009dba64beSDimitry Andric     if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
1010b57cec5SDimitry Andric       array_up->AddItem(value_sp);
1020b57cec5SDimitry Andric   }
103480093f4SDimitry Andric   return std::move(array_up);
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric StructuredData::ObjectSP
GetObjectForDotSeparatedPath(llvm::StringRef path)1070b57cec5SDimitry Andric StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) {
10806c3fb27SDimitry Andric   if (GetType() == lldb::eStructuredDataTypeDictionary) {
1090b57cec5SDimitry Andric     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.');
11006c3fb27SDimitry Andric     llvm::StringRef key = match.first;
11106c3fb27SDimitry Andric     ObjectSP value = GetAsDictionary()->GetValueForKey(key);
11206c3fb27SDimitry Andric     if (!value)
11306c3fb27SDimitry Andric       return {};
11406c3fb27SDimitry Andric 
1150b57cec5SDimitry Andric     // Do we have additional words to descend?  If not, return the value
1160b57cec5SDimitry Andric     // we're at right now.
11706c3fb27SDimitry Andric     if (match.second.empty())
1180b57cec5SDimitry Andric       return value;
11906c3fb27SDimitry Andric 
1200b57cec5SDimitry Andric     return value->GetObjectForDotSeparatedPath(match.second);
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric 
12306c3fb27SDimitry Andric   if (GetType() == lldb::eStructuredDataTypeArray) {
1240b57cec5SDimitry Andric     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('[');
12506c3fb27SDimitry Andric     if (match.second.empty())
12606c3fb27SDimitry Andric       return shared_from_this();
12706c3fb27SDimitry Andric 
12806c3fb27SDimitry Andric     uint64_t val = 0;
12906c3fb27SDimitry Andric     if (!llvm::to_integer(match.second, val, /* Base = */ 10))
13006c3fb27SDimitry Andric       return {};
13106c3fb27SDimitry Andric 
13206c3fb27SDimitry Andric     return GetAsArray()->GetItemAtIndex(val);
1330b57cec5SDimitry Andric   }
1340b57cec5SDimitry Andric 
13506c3fb27SDimitry Andric   return shared_from_this();
1360b57cec5SDimitry Andric }
1370b57cec5SDimitry Andric 
DumpToStdout(bool pretty_print) const1380b57cec5SDimitry Andric void StructuredData::Object::DumpToStdout(bool pretty_print) const {
1399dba64beSDimitry Andric   json::OStream stream(llvm::outs(), pretty_print ? 2 : 0);
1409dba64beSDimitry Andric   Serialize(stream);
1410b57cec5SDimitry Andric }
1420b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1439dba64beSDimitry Andric void StructuredData::Array::Serialize(json::OStream &s) const {
1449dba64beSDimitry Andric   s.arrayBegin();
1450b57cec5SDimitry Andric   for (const auto &item_sp : m_items) {
1469dba64beSDimitry Andric     item_sp->Serialize(s);
1479dba64beSDimitry Andric   }
1489dba64beSDimitry Andric   s.arrayEnd();
1490b57cec5SDimitry Andric }
1500b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1519dba64beSDimitry Andric void StructuredData::Float::Serialize(json::OStream &s) const {
1529dba64beSDimitry Andric   s.value(m_value);
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1559dba64beSDimitry Andric void StructuredData::Boolean::Serialize(json::OStream &s) const {
1569dba64beSDimitry Andric   s.value(m_value);
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1599dba64beSDimitry Andric void StructuredData::String::Serialize(json::OStream &s) const {
1609dba64beSDimitry Andric   s.value(m_value);
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1639dba64beSDimitry Andric void StructuredData::Dictionary::Serialize(json::OStream &s) const {
1649dba64beSDimitry Andric   s.objectBegin();
1655f757f3fSDimitry Andric 
1665f757f3fSDimitry Andric   // To ensure the output format is always stable, we sort the dictionary by key
1675f757f3fSDimitry Andric   // first.
1685f757f3fSDimitry Andric   using Entry = std::pair<llvm::StringRef, ObjectSP>;
1695f757f3fSDimitry Andric   std::vector<Entry> sorted_entries;
1705f757f3fSDimitry Andric   for (const auto &pair : m_dict)
1715f757f3fSDimitry Andric     sorted_entries.push_back({pair.first(), pair.second});
1725f757f3fSDimitry Andric 
1735f757f3fSDimitry Andric   llvm::sort(sorted_entries);
1745f757f3fSDimitry Andric 
1755f757f3fSDimitry Andric   for (const auto &pair : sorted_entries) {
1765f757f3fSDimitry Andric     s.attributeBegin(pair.first);
1779dba64beSDimitry Andric     pair.second->Serialize(s);
1789dba64beSDimitry Andric     s.attributeEnd();
1790b57cec5SDimitry Andric   }
1809dba64beSDimitry Andric   s.objectEnd();
1810b57cec5SDimitry Andric }
1820b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1839dba64beSDimitry Andric void StructuredData::Null::Serialize(json::OStream &s) const {
1849dba64beSDimitry Andric   s.value(nullptr);
1850b57cec5SDimitry Andric }
1860b57cec5SDimitry Andric 
Serialize(json::OStream & s) const1879dba64beSDimitry Andric void StructuredData::Generic::Serialize(json::OStream &s) const {
1889dba64beSDimitry Andric   s.value(llvm::formatv("{0:X}", m_object));
1890b57cec5SDimitry Andric }
190bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const191bdd1243dSDimitry Andric void StructuredData::Float::GetDescription(lldb_private::Stream &s) const {
192bdd1243dSDimitry Andric   s.Printf("%f", m_value);
193bdd1243dSDimitry Andric }
194bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const195bdd1243dSDimitry Andric void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const {
196bdd1243dSDimitry Andric   s.Printf(m_value ? "True" : "False");
197bdd1243dSDimitry Andric }
198bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const199bdd1243dSDimitry Andric void StructuredData::String::GetDescription(lldb_private::Stream &s) const {
200bdd1243dSDimitry Andric   s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str());
201bdd1243dSDimitry Andric }
202bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const203bdd1243dSDimitry Andric void StructuredData::Array::GetDescription(lldb_private::Stream &s) const {
204bdd1243dSDimitry Andric   size_t index = 0;
205bdd1243dSDimitry Andric   size_t indentation_level = s.GetIndentLevel();
206bdd1243dSDimitry Andric   for (const auto &item_sp : m_items) {
207bdd1243dSDimitry Andric     // Sanitize.
208bdd1243dSDimitry Andric     if (!item_sp)
209bdd1243dSDimitry Andric       continue;
210bdd1243dSDimitry Andric 
211bdd1243dSDimitry Andric     // Reset original indentation level.
212bdd1243dSDimitry Andric     s.SetIndentLevel(indentation_level);
213bdd1243dSDimitry Andric     s.Indent();
214bdd1243dSDimitry Andric 
215bdd1243dSDimitry Andric     // Print key
216bdd1243dSDimitry Andric     s.Printf("[%zu]:", index++);
217bdd1243dSDimitry Andric 
218bdd1243dSDimitry Andric     // Return to new line and increase indentation if value is record type.
219bdd1243dSDimitry Andric     // Otherwise add spacing.
220bdd1243dSDimitry Andric     bool should_indent = IsRecordType(item_sp);
221bdd1243dSDimitry Andric     if (should_indent) {
222bdd1243dSDimitry Andric       s.EOL();
223bdd1243dSDimitry Andric       s.IndentMore();
224bdd1243dSDimitry Andric     } else {
225bdd1243dSDimitry Andric       s.PutChar(' ');
226bdd1243dSDimitry Andric     }
227bdd1243dSDimitry Andric 
228bdd1243dSDimitry Andric     // Print value and new line if now last pair.
229bdd1243dSDimitry Andric     item_sp->GetDescription(s);
230bdd1243dSDimitry Andric     if (item_sp != *(--m_items.end()))
231bdd1243dSDimitry Andric       s.EOL();
232bdd1243dSDimitry Andric 
233bdd1243dSDimitry Andric     // Reset indentation level if it was incremented previously.
234bdd1243dSDimitry Andric     if (should_indent)
235bdd1243dSDimitry Andric       s.IndentLess();
236bdd1243dSDimitry Andric   }
237bdd1243dSDimitry Andric }
238bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const239bdd1243dSDimitry Andric void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const {
240bdd1243dSDimitry Andric   size_t indentation_level = s.GetIndentLevel();
2415f757f3fSDimitry Andric 
2425f757f3fSDimitry Andric   // To ensure the output format is always stable, we sort the dictionary by key
2435f757f3fSDimitry Andric   // first.
2445f757f3fSDimitry Andric   using Entry = std::pair<llvm::StringRef, ObjectSP>;
2455f757f3fSDimitry Andric   std::vector<Entry> sorted_entries;
2465f757f3fSDimitry Andric   for (const auto &pair : m_dict)
2475f757f3fSDimitry Andric     sorted_entries.push_back({pair.first(), pair.second});
2485f757f3fSDimitry Andric 
2495f757f3fSDimitry Andric   llvm::sort(sorted_entries);
2505f757f3fSDimitry Andric 
2515f757f3fSDimitry Andric   for (auto iter = sorted_entries.begin(); iter != sorted_entries.end();
2525f757f3fSDimitry Andric        iter++) {
253bdd1243dSDimitry Andric     // Sanitize.
2545f757f3fSDimitry Andric     if (iter->first.empty() || !iter->second)
255bdd1243dSDimitry Andric       continue;
256bdd1243dSDimitry Andric 
257bdd1243dSDimitry Andric     // Reset original indentation level.
258bdd1243dSDimitry Andric     s.SetIndentLevel(indentation_level);
259bdd1243dSDimitry Andric     s.Indent();
260bdd1243dSDimitry Andric 
261bdd1243dSDimitry Andric     // Print key.
2625f757f3fSDimitry Andric     s.Format("{0}:", iter->first);
263bdd1243dSDimitry Andric 
264bdd1243dSDimitry Andric     // Return to new line and increase indentation if value is record type.
265bdd1243dSDimitry Andric     // Otherwise add spacing.
2665f757f3fSDimitry Andric     bool should_indent = IsRecordType(iter->second);
267bdd1243dSDimitry Andric     if (should_indent) {
268bdd1243dSDimitry Andric       s.EOL();
269bdd1243dSDimitry Andric       s.IndentMore();
270bdd1243dSDimitry Andric     } else {
271bdd1243dSDimitry Andric       s.PutChar(' ');
272bdd1243dSDimitry Andric     }
273bdd1243dSDimitry Andric 
274bdd1243dSDimitry Andric     // Print value and new line if now last pair.
2755f757f3fSDimitry Andric     iter->second->GetDescription(s);
2765f757f3fSDimitry Andric     if (std::next(iter) != sorted_entries.end())
277bdd1243dSDimitry Andric       s.EOL();
278bdd1243dSDimitry Andric 
279bdd1243dSDimitry Andric     // Reset indentation level if it was incremented previously.
280bdd1243dSDimitry Andric     if (should_indent)
281bdd1243dSDimitry Andric       s.IndentLess();
282bdd1243dSDimitry Andric   }
283bdd1243dSDimitry Andric }
284bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const285bdd1243dSDimitry Andric void StructuredData::Null::GetDescription(lldb_private::Stream &s) const {
286bdd1243dSDimitry Andric   s.Printf("NULL");
287bdd1243dSDimitry Andric }
288bdd1243dSDimitry Andric 
GetDescription(lldb_private::Stream & s) const289bdd1243dSDimitry Andric void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const {
290bdd1243dSDimitry Andric   s.Printf("%p", m_object);
291bdd1243dSDimitry Andric }
292