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