1 //===-- StructuredData.cpp ------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
18 using namespace lldb_private
;
21 static StructuredData::ObjectSP
ParseJSONValue(json::Value
&value
);
22 static StructuredData::ObjectSP
ParseJSONObject(json::Object
*object
);
23 static StructuredData::ObjectSP
ParseJSONArray(json::Array
*array
);
25 StructuredData::ObjectSP
StructuredData::ParseJSON(llvm::StringRef json_text
) {
26 llvm::Expected
<json::Value
> value
= json::parse(json_text
);
28 llvm::consumeError(value
.takeError());
31 return ParseJSONValue(*value
);
34 StructuredData::ObjectSP
35 StructuredData::ParseJSONFromFile(const FileSpec
&input_spec
, Status
&error
) {
36 StructuredData::ObjectSP return_sp
;
38 auto buffer_or_error
= llvm::MemoryBuffer::getFile(input_spec
.GetPath());
39 if (!buffer_or_error
) {
40 error
= Status::FromErrorStringWithFormatv(
41 "could not open input file: {0} - {1}.", input_spec
.GetPath(),
42 buffer_or_error
.getError().message());
45 llvm::Expected
<json::Value
> value
=
46 json::parse(buffer_or_error
.get()->getBuffer().str());
48 return ParseJSONValue(*value
);
49 error
= Status::FromError(value
.takeError());
50 return StructuredData::ObjectSP();
53 bool StructuredData::IsRecordType(const ObjectSP object_sp
) {
54 return object_sp
->GetType() == lldb::eStructuredDataTypeArray
||
55 object_sp
->GetType() == lldb::eStructuredDataTypeDictionary
;
58 static StructuredData::ObjectSP
ParseJSONValue(json::Value
&value
) {
59 if (json::Object
*O
= value
.getAsObject())
60 return ParseJSONObject(O
);
62 if (json::Array
*A
= value
.getAsArray())
63 return ParseJSONArray(A
);
65 if (auto s
= value
.getAsString())
66 return std::make_shared
<StructuredData::String
>(*s
);
68 if (auto b
= value
.getAsBoolean())
69 return std::make_shared
<StructuredData::Boolean
>(*b
);
71 if (auto u
= value
.getAsUINT64())
72 return std::make_shared
<StructuredData::UnsignedInteger
>(*u
);
74 if (auto i
= value
.getAsInteger())
75 return std::make_shared
<StructuredData::SignedInteger
>(*i
);
77 if (auto d
= value
.getAsNumber())
78 return std::make_shared
<StructuredData::Float
>(*d
);
80 if (auto n
= value
.getAsNull())
81 return std::make_shared
<StructuredData::Null
>();
83 return StructuredData::ObjectSP();
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
);
94 return std::move(dict_up
);
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
);
103 return std::move(array_up
);
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
);
115 // Do we have additional words to descend? If not, return the value
116 // we're at right now.
117 if (match
.second
.empty())
120 return value
->GetObjectForDotSeparatedPath(match
.second
);
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();
129 if (!llvm::to_integer(match
.second
, val
, /* Base = */ 10))
132 return GetAsArray()->GetItemAtIndex(val
);
135 return shared_from_this();
138 void StructuredData::Object::DumpToStdout(bool pretty_print
) const {
139 json::OStream
stream(llvm::outs(), pretty_print
? 2 : 0);
143 void StructuredData::Array::Serialize(json::OStream
&s
) const {
145 for (const auto &item_sp
: m_items
) {
146 item_sp
->Serialize(s
);
151 void StructuredData::Float::Serialize(json::OStream
&s
) const {
155 void StructuredData::Boolean::Serialize(json::OStream
&s
) const {
159 void StructuredData::String::Serialize(json::OStream
&s
) const {
163 void StructuredData::Dictionary::Serialize(json::OStream
&s
) const {
166 // To ensure the output format is always stable, we sort the dictionary by key
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
});
173 llvm::sort(sorted_entries
);
175 for (const auto &pair
: sorted_entries
) {
176 s
.attributeBegin(pair
.first
);
177 pair
.second
->Serialize(s
);
183 void StructuredData::Null::Serialize(json::OStream
&s
) const {
187 void StructuredData::Generic::Serialize(json::OStream
&s
) const {
188 s
.value(llvm::formatv("{0:X}", m_object
));
191 void StructuredData::Float::GetDescription(lldb_private::Stream
&s
) const {
192 s
.Printf("%f", m_value
);
195 void StructuredData::Boolean::GetDescription(lldb_private::Stream
&s
) const {
196 s
.Printf(m_value
? "True" : "False");
199 void StructuredData::String::GetDescription(lldb_private::Stream
&s
) const {
200 s
.Printf("%s", m_value
.empty() ? "\"\"" : m_value
.c_str());
203 void StructuredData::Array::GetDescription(lldb_private::Stream
&s
) const {
205 size_t indentation_level
= s
.GetIndentLevel();
206 for (const auto &item_sp
: m_items
) {
211 // Reset original indentation level.
212 s
.SetIndentLevel(indentation_level
);
216 s
.Printf("[%zu]:", index
++);
218 // Return to new line and increase indentation if value is record type.
219 // Otherwise add spacing.
220 bool should_indent
= IsRecordType(item_sp
);
228 // Print value and new line if now last pair.
229 item_sp
->GetDescription(s
);
230 if (item_sp
!= *(--m_items
.end()))
233 // Reset indentation level if it was incremented previously.
239 void StructuredData::Dictionary::GetDescription(lldb_private::Stream
&s
) const {
240 size_t indentation_level
= s
.GetIndentLevel();
242 // To ensure the output format is always stable, we sort the dictionary by key
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
});
249 llvm::sort(sorted_entries
);
251 for (auto iter
= sorted_entries
.begin(); iter
!= sorted_entries
.end();
254 if (iter
->first
.empty() || !iter
->second
)
257 // Reset original indentation level.
258 s
.SetIndentLevel(indentation_level
);
262 s
.Format("{0}:", iter
->first
);
264 // Return to new line and increase indentation if value is record type.
265 // Otherwise add spacing.
266 bool should_indent
= IsRecordType(iter
->second
);
274 // Print value and new line if now last pair.
275 iter
->second
->GetDescription(s
);
276 if (std::next(iter
) != sorted_entries
.end())
279 // Reset indentation level if it was incremented previously.
285 void StructuredData::Null::GetDescription(lldb_private::Stream
&s
) const {
289 void StructuredData::Generic::GetDescription(lldb_private::Stream
&s
) const {
290 s
.Printf("%p", m_object
);