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