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