1*f2a8bccfSPavel Labath //===---------------------StructuredData.cpp ---------------------*- C++-*-===//
2*f2a8bccfSPavel Labath //
3*f2a8bccfSPavel Labath //                     The LLVM Compiler Infrastructure
4*f2a8bccfSPavel Labath //
5*f2a8bccfSPavel Labath // This file is distributed under the University of Illinois Open Source
6*f2a8bccfSPavel Labath // License. See LICENSE.TXT for details.
7*f2a8bccfSPavel Labath //
8*f2a8bccfSPavel Labath //===----------------------------------------------------------------------===//
9*f2a8bccfSPavel Labath 
10*f2a8bccfSPavel Labath #include "lldb/Utility/StructuredData.h"
11*f2a8bccfSPavel Labath #include "lldb/Utility/DataBuffer.h"
12*f2a8bccfSPavel Labath #include "lldb/Utility/FileSpec.h"
13*f2a8bccfSPavel Labath #include "lldb/Utility/JSON.h"
14*f2a8bccfSPavel Labath #include "lldb/Utility/Status.h"
15*f2a8bccfSPavel Labath #include "lldb/Utility/Stream.h" // for Stream
16*f2a8bccfSPavel Labath #include "lldb/Utility/StreamString.h"
17*f2a8bccfSPavel Labath #include "llvm/ADT/STLExtras.h" // for make_unique
18*f2a8bccfSPavel Labath #include <cerrno>
19*f2a8bccfSPavel Labath #include <cstdlib>
20*f2a8bccfSPavel Labath #include <inttypes.h>
21*f2a8bccfSPavel Labath #include <limits> // for numeric_limits
22*f2a8bccfSPavel Labath 
23*f2a8bccfSPavel Labath using namespace lldb_private;
24*f2a8bccfSPavel Labath 
25*f2a8bccfSPavel Labath //----------------------------------------------------------------------
26*f2a8bccfSPavel Labath // Functions that use a JSONParser to parse JSON into StructuredData
27*f2a8bccfSPavel Labath //----------------------------------------------------------------------
28*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser);
29*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser);
30*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser);
31*f2a8bccfSPavel Labath 
32*f2a8bccfSPavel Labath StructuredData::ObjectSP
33*f2a8bccfSPavel Labath StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {
34*f2a8bccfSPavel Labath   StructuredData::ObjectSP return_sp;
35*f2a8bccfSPavel Labath   if (!input_spec.Exists()) {
36*f2a8bccfSPavel Labath     error.SetErrorStringWithFormatv("input file {0} does not exist.",
37*f2a8bccfSPavel Labath                                     input_spec);
38*f2a8bccfSPavel Labath     return return_sp;
39*f2a8bccfSPavel Labath   }
40*f2a8bccfSPavel Labath 
41*f2a8bccfSPavel Labath   auto buffer_or_error = llvm::MemoryBuffer::getFile(input_spec.GetPath());
42*f2a8bccfSPavel Labath   if (!buffer_or_error) {
43*f2a8bccfSPavel Labath     error.SetErrorStringWithFormatv("could not open input file: {0} - {1}.",
44*f2a8bccfSPavel Labath                                     input_spec.GetPath(),
45*f2a8bccfSPavel Labath                                     buffer_or_error.getError().message());
46*f2a8bccfSPavel Labath     return return_sp;
47*f2a8bccfSPavel Labath   }
48*f2a8bccfSPavel Labath 
49*f2a8bccfSPavel Labath   JSONParser json_parser(buffer_or_error.get()->getBuffer());
50*f2a8bccfSPavel Labath   return_sp = ParseJSONValue(json_parser);
51*f2a8bccfSPavel Labath   return return_sp;
52*f2a8bccfSPavel Labath }
53*f2a8bccfSPavel Labath 
54*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONObject(JSONParser &json_parser) {
55*f2a8bccfSPavel Labath   // The "JSONParser::Token::ObjectStart" token should have already been
56*f2a8bccfSPavel Labath   // consumed by the time this function is called
57*f2a8bccfSPavel Labath   auto dict_up = llvm::make_unique<StructuredData::Dictionary>();
58*f2a8bccfSPavel Labath 
59*f2a8bccfSPavel Labath   std::string value;
60*f2a8bccfSPavel Labath   std::string key;
61*f2a8bccfSPavel Labath   while (1) {
62*f2a8bccfSPavel Labath     JSONParser::Token token = json_parser.GetToken(value);
63*f2a8bccfSPavel Labath 
64*f2a8bccfSPavel Labath     if (token == JSONParser::Token::String) {
65*f2a8bccfSPavel Labath       key.swap(value);
66*f2a8bccfSPavel Labath       token = json_parser.GetToken(value);
67*f2a8bccfSPavel Labath       if (token == JSONParser::Token::Colon) {
68*f2a8bccfSPavel Labath         StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser);
69*f2a8bccfSPavel Labath         if (value_sp)
70*f2a8bccfSPavel Labath           dict_up->AddItem(key, value_sp);
71*f2a8bccfSPavel Labath         else
72*f2a8bccfSPavel Labath           break;
73*f2a8bccfSPavel Labath       }
74*f2a8bccfSPavel Labath     } else if (token == JSONParser::Token::ObjectEnd) {
75*f2a8bccfSPavel Labath       return StructuredData::ObjectSP(dict_up.release());
76*f2a8bccfSPavel Labath     } else if (token == JSONParser::Token::Comma) {
77*f2a8bccfSPavel Labath       continue;
78*f2a8bccfSPavel Labath     } else {
79*f2a8bccfSPavel Labath       break;
80*f2a8bccfSPavel Labath     }
81*f2a8bccfSPavel Labath   }
82*f2a8bccfSPavel Labath   return StructuredData::ObjectSP();
83*f2a8bccfSPavel Labath }
84*f2a8bccfSPavel Labath 
85*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONArray(JSONParser &json_parser) {
86*f2a8bccfSPavel Labath   // The "JSONParser::Token::ObjectStart" token should have already been
87*f2a8bccfSPavel Labath   // consumed
88*f2a8bccfSPavel Labath   // by the time this function is called
89*f2a8bccfSPavel Labath   auto array_up = llvm::make_unique<StructuredData::Array>();
90*f2a8bccfSPavel Labath 
91*f2a8bccfSPavel Labath   std::string value;
92*f2a8bccfSPavel Labath   std::string key;
93*f2a8bccfSPavel Labath   while (1) {
94*f2a8bccfSPavel Labath     StructuredData::ObjectSP value_sp = ParseJSONValue(json_parser);
95*f2a8bccfSPavel Labath     if (value_sp)
96*f2a8bccfSPavel Labath       array_up->AddItem(value_sp);
97*f2a8bccfSPavel Labath     else
98*f2a8bccfSPavel Labath       break;
99*f2a8bccfSPavel Labath 
100*f2a8bccfSPavel Labath     JSONParser::Token token = json_parser.GetToken(value);
101*f2a8bccfSPavel Labath     if (token == JSONParser::Token::Comma) {
102*f2a8bccfSPavel Labath       continue;
103*f2a8bccfSPavel Labath     } else if (token == JSONParser::Token::ArrayEnd) {
104*f2a8bccfSPavel Labath       return StructuredData::ObjectSP(array_up.release());
105*f2a8bccfSPavel Labath     } else {
106*f2a8bccfSPavel Labath       break;
107*f2a8bccfSPavel Labath     }
108*f2a8bccfSPavel Labath   }
109*f2a8bccfSPavel Labath   return StructuredData::ObjectSP();
110*f2a8bccfSPavel Labath }
111*f2a8bccfSPavel Labath 
112*f2a8bccfSPavel Labath static StructuredData::ObjectSP ParseJSONValue(JSONParser &json_parser) {
113*f2a8bccfSPavel Labath   std::string value;
114*f2a8bccfSPavel Labath   const JSONParser::Token token = json_parser.GetToken(value);
115*f2a8bccfSPavel Labath   switch (token) {
116*f2a8bccfSPavel Labath   case JSONParser::Token::ObjectStart:
117*f2a8bccfSPavel Labath     return ParseJSONObject(json_parser);
118*f2a8bccfSPavel Labath 
119*f2a8bccfSPavel Labath   case JSONParser::Token::ArrayStart:
120*f2a8bccfSPavel Labath     return ParseJSONArray(json_parser);
121*f2a8bccfSPavel Labath 
122*f2a8bccfSPavel Labath   case JSONParser::Token::Integer: {
123*f2a8bccfSPavel Labath     uint64_t uval;
124*f2a8bccfSPavel Labath     if (llvm::to_integer(value, uval, 0))
125*f2a8bccfSPavel Labath       return std::make_shared<StructuredData::Integer>(uval);
126*f2a8bccfSPavel Labath   } break;
127*f2a8bccfSPavel Labath 
128*f2a8bccfSPavel Labath   case JSONParser::Token::Float: {
129*f2a8bccfSPavel Labath     double val;
130*f2a8bccfSPavel Labath     if (llvm::to_float(value, val))
131*f2a8bccfSPavel Labath       return std::make_shared<StructuredData::Float>(val);
132*f2a8bccfSPavel Labath   } break;
133*f2a8bccfSPavel Labath 
134*f2a8bccfSPavel Labath   case JSONParser::Token::String:
135*f2a8bccfSPavel Labath     return std::make_shared<StructuredData::String>(value);
136*f2a8bccfSPavel Labath 
137*f2a8bccfSPavel Labath   case JSONParser::Token::True:
138*f2a8bccfSPavel Labath   case JSONParser::Token::False:
139*f2a8bccfSPavel Labath     return std::make_shared<StructuredData::Boolean>(token ==
140*f2a8bccfSPavel Labath                                                      JSONParser::Token::True);
141*f2a8bccfSPavel Labath 
142*f2a8bccfSPavel Labath   case JSONParser::Token::Null:
143*f2a8bccfSPavel Labath     return std::make_shared<StructuredData::Null>();
144*f2a8bccfSPavel Labath 
145*f2a8bccfSPavel Labath   default:
146*f2a8bccfSPavel Labath     break;
147*f2a8bccfSPavel Labath   }
148*f2a8bccfSPavel Labath   return StructuredData::ObjectSP();
149*f2a8bccfSPavel Labath }
150*f2a8bccfSPavel Labath 
151*f2a8bccfSPavel Labath StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) {
152*f2a8bccfSPavel Labath   JSONParser json_parser(json_text.c_str());
153*f2a8bccfSPavel Labath   StructuredData::ObjectSP object_sp = ParseJSONValue(json_parser);
154*f2a8bccfSPavel Labath   return object_sp;
155*f2a8bccfSPavel Labath }
156*f2a8bccfSPavel Labath 
157*f2a8bccfSPavel Labath StructuredData::ObjectSP
158*f2a8bccfSPavel Labath StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) {
159*f2a8bccfSPavel Labath   if (this->GetType() == lldb::eStructuredDataTypeDictionary) {
160*f2a8bccfSPavel Labath     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.');
161*f2a8bccfSPavel Labath     std::string key = match.first.str();
162*f2a8bccfSPavel Labath     ObjectSP value = this->GetAsDictionary()->GetValueForKey(key);
163*f2a8bccfSPavel Labath     if (value.get()) {
164*f2a8bccfSPavel Labath       // Do we have additional words to descend?  If not, return the
165*f2a8bccfSPavel Labath       // value we're at right now.
166*f2a8bccfSPavel Labath       if (match.second.empty()) {
167*f2a8bccfSPavel Labath         return value;
168*f2a8bccfSPavel Labath       } else {
169*f2a8bccfSPavel Labath         return value->GetObjectForDotSeparatedPath(match.second);
170*f2a8bccfSPavel Labath       }
171*f2a8bccfSPavel Labath     }
172*f2a8bccfSPavel Labath     return ObjectSP();
173*f2a8bccfSPavel Labath   }
174*f2a8bccfSPavel Labath 
175*f2a8bccfSPavel Labath   if (this->GetType() == lldb::eStructuredDataTypeArray) {
176*f2a8bccfSPavel Labath     std::pair<llvm::StringRef, llvm::StringRef> match = path.split('[');
177*f2a8bccfSPavel Labath     if (match.second.size() == 0) {
178*f2a8bccfSPavel Labath       return this->shared_from_this();
179*f2a8bccfSPavel Labath     }
180*f2a8bccfSPavel Labath     errno = 0;
181*f2a8bccfSPavel Labath     uint64_t val = strtoul(match.second.str().c_str(), NULL, 10);
182*f2a8bccfSPavel Labath     if (errno == 0) {
183*f2a8bccfSPavel Labath       return this->GetAsArray()->GetItemAtIndex(val);
184*f2a8bccfSPavel Labath     }
185*f2a8bccfSPavel Labath     return ObjectSP();
186*f2a8bccfSPavel Labath   }
187*f2a8bccfSPavel Labath 
188*f2a8bccfSPavel Labath   return this->shared_from_this();
189*f2a8bccfSPavel Labath }
190*f2a8bccfSPavel Labath 
191*f2a8bccfSPavel Labath void StructuredData::Object::DumpToStdout(bool pretty_print) const {
192*f2a8bccfSPavel Labath   StreamString stream;
193*f2a8bccfSPavel Labath   Dump(stream, pretty_print);
194*f2a8bccfSPavel Labath   llvm::outs() << stream.GetString();
195*f2a8bccfSPavel Labath }
196*f2a8bccfSPavel Labath 
197*f2a8bccfSPavel Labath void StructuredData::Array::Dump(Stream &s, bool pretty_print) const {
198*f2a8bccfSPavel Labath   bool first = true;
199*f2a8bccfSPavel Labath   s << "[";
200*f2a8bccfSPavel Labath   if (pretty_print) {
201*f2a8bccfSPavel Labath     s << "\n";
202*f2a8bccfSPavel Labath     s.IndentMore();
203*f2a8bccfSPavel Labath   }
204*f2a8bccfSPavel Labath   for (const auto &item_sp : m_items) {
205*f2a8bccfSPavel Labath     if (first) {
206*f2a8bccfSPavel Labath       first = false;
207*f2a8bccfSPavel Labath     } else {
208*f2a8bccfSPavel Labath       s << ",";
209*f2a8bccfSPavel Labath       if (pretty_print)
210*f2a8bccfSPavel Labath         s << "\n";
211*f2a8bccfSPavel Labath     }
212*f2a8bccfSPavel Labath 
213*f2a8bccfSPavel Labath     if (pretty_print)
214*f2a8bccfSPavel Labath       s.Indent();
215*f2a8bccfSPavel Labath     item_sp->Dump(s, pretty_print);
216*f2a8bccfSPavel Labath   }
217*f2a8bccfSPavel Labath   if (pretty_print) {
218*f2a8bccfSPavel Labath     s.IndentLess();
219*f2a8bccfSPavel Labath     s.EOL();
220*f2a8bccfSPavel Labath     s.Indent();
221*f2a8bccfSPavel Labath   }
222*f2a8bccfSPavel Labath   s << "]";
223*f2a8bccfSPavel Labath }
224*f2a8bccfSPavel Labath 
225*f2a8bccfSPavel Labath void StructuredData::Integer::Dump(Stream &s, bool pretty_print) const {
226*f2a8bccfSPavel Labath   s.Printf("%" PRIu64, m_value);
227*f2a8bccfSPavel Labath }
228*f2a8bccfSPavel Labath 
229*f2a8bccfSPavel Labath void StructuredData::Float::Dump(Stream &s, bool pretty_print) const {
230*f2a8bccfSPavel Labath   s.Printf("%lg", m_value);
231*f2a8bccfSPavel Labath }
232*f2a8bccfSPavel Labath 
233*f2a8bccfSPavel Labath void StructuredData::Boolean::Dump(Stream &s, bool pretty_print) const {
234*f2a8bccfSPavel Labath   if (m_value == true)
235*f2a8bccfSPavel Labath     s.PutCString("true");
236*f2a8bccfSPavel Labath   else
237*f2a8bccfSPavel Labath     s.PutCString("false");
238*f2a8bccfSPavel Labath }
239*f2a8bccfSPavel Labath 
240*f2a8bccfSPavel Labath void StructuredData::String::Dump(Stream &s, bool pretty_print) const {
241*f2a8bccfSPavel Labath   std::string quoted;
242*f2a8bccfSPavel Labath   const size_t strsize = m_value.size();
243*f2a8bccfSPavel Labath   for (size_t i = 0; i < strsize; ++i) {
244*f2a8bccfSPavel Labath     char ch = m_value[i];
245*f2a8bccfSPavel Labath     if (ch == '"' || ch == '\\')
246*f2a8bccfSPavel Labath       quoted.push_back('\\');
247*f2a8bccfSPavel Labath     quoted.push_back(ch);
248*f2a8bccfSPavel Labath   }
249*f2a8bccfSPavel Labath   s.Printf("\"%s\"", quoted.c_str());
250*f2a8bccfSPavel Labath }
251*f2a8bccfSPavel Labath 
252*f2a8bccfSPavel Labath void StructuredData::Dictionary::Dump(Stream &s, bool pretty_print) const {
253*f2a8bccfSPavel Labath   bool first = true;
254*f2a8bccfSPavel Labath   s << "{";
255*f2a8bccfSPavel Labath   if (pretty_print) {
256*f2a8bccfSPavel Labath     s << "\n";
257*f2a8bccfSPavel Labath     s.IndentMore();
258*f2a8bccfSPavel Labath   }
259*f2a8bccfSPavel Labath   for (const auto &pair : m_dict) {
260*f2a8bccfSPavel Labath     if (first)
261*f2a8bccfSPavel Labath       first = false;
262*f2a8bccfSPavel Labath     else {
263*f2a8bccfSPavel Labath       s << ",";
264*f2a8bccfSPavel Labath       if (pretty_print)
265*f2a8bccfSPavel Labath         s << "\n";
266*f2a8bccfSPavel Labath     }
267*f2a8bccfSPavel Labath     if (pretty_print)
268*f2a8bccfSPavel Labath       s.Indent();
269*f2a8bccfSPavel Labath     s << "\"" << pair.first.AsCString() << "\" : ";
270*f2a8bccfSPavel Labath     pair.second->Dump(s, pretty_print);
271*f2a8bccfSPavel Labath   }
272*f2a8bccfSPavel Labath   if (pretty_print) {
273*f2a8bccfSPavel Labath     s.IndentLess();
274*f2a8bccfSPavel Labath     s.EOL();
275*f2a8bccfSPavel Labath     s.Indent();
276*f2a8bccfSPavel Labath   }
277*f2a8bccfSPavel Labath   s << "}";
278*f2a8bccfSPavel Labath }
279*f2a8bccfSPavel Labath 
280*f2a8bccfSPavel Labath void StructuredData::Null::Dump(Stream &s, bool pretty_print) const {
281*f2a8bccfSPavel Labath   s << "null";
282*f2a8bccfSPavel Labath }
283*f2a8bccfSPavel Labath 
284*f2a8bccfSPavel Labath void StructuredData::Generic::Dump(Stream &s, bool pretty_print) const {
285*f2a8bccfSPavel Labath   s << "0x" << m_object;
286*f2a8bccfSPavel Labath }
287