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/FileSpec.h"
11#include "lldb/Utility/Status.h"
12#include "llvm/Support/MemoryBuffer.h"
13#include <cerrno>
14#include <cstdlib>
15#include <inttypes.h>
16
17using namespace lldb_private;
18using namespace llvm;
19
20static StructuredData::ObjectSP ParseJSONValue(json::Value &value);
21static StructuredData::ObjectSP ParseJSONObject(json::Object *object);
22static StructuredData::ObjectSP ParseJSONArray(json::Array *array);
23
24StructuredData::ObjectSP StructuredData::ParseJSON(std::string json_text) {
25  llvm::Expected<json::Value> value = json::parse(json_text);
26  if (!value) {
27    llvm::consumeError(value.takeError());
28    return nullptr;
29  }
30  return ParseJSONValue(*value);
31}
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  return ParseJSON(buffer_or_error.get()->getBuffer().str());
45}
46
47static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {
48  if (json::Object *O = value.getAsObject())
49    return ParseJSONObject(O);
50
51  if (json::Array *A = value.getAsArray())
52    return ParseJSONArray(A);
53
54  std::string s;
55  if (json::fromJSON(value, s))
56    return std::make_shared<StructuredData::String>(s);
57
58  bool b;
59  if (json::fromJSON(value, b))
60    return std::make_shared<StructuredData::Boolean>(b);
61
62  int64_t i;
63  if (json::fromJSON(value, i))
64    return std::make_shared<StructuredData::Integer>(i);
65
66  double d;
67  if (json::fromJSON(value, d))
68    return std::make_shared<StructuredData::Float>(d);
69
70  return StructuredData::ObjectSP();
71}
72
73static StructuredData::ObjectSP ParseJSONObject(json::Object *object) {
74  auto dict_up = std::make_unique<StructuredData::Dictionary>();
75  for (auto &KV : *object) {
76    StringRef key = KV.first;
77    json::Value value = KV.second;
78    if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
79      dict_up->AddItem(key, value_sp);
80  }
81  return std::move(dict_up);
82}
83
84static StructuredData::ObjectSP ParseJSONArray(json::Array *array) {
85  auto array_up = std::make_unique<StructuredData::Array>();
86  for (json::Value &value : *array) {
87    if (StructuredData::ObjectSP value_sp = ParseJSONValue(value))
88      array_up->AddItem(value_sp);
89  }
90  return std::move(array_up);
91}
92
93StructuredData::ObjectSP
94StructuredData::Object::GetObjectForDotSeparatedPath(llvm::StringRef path) {
95  if (this->GetType() == lldb::eStructuredDataTypeDictionary) {
96    std::pair<llvm::StringRef, llvm::StringRef> match = path.split('.');
97    std::string key = match.first.str();
98    ObjectSP value = this->GetAsDictionary()->GetValueForKey(key);
99    if (value.get()) {
100      // Do we have additional words to descend?  If not, return the value
101      // we're at right now.
102      if (match.second.empty()) {
103        return value;
104      } else {
105        return value->GetObjectForDotSeparatedPath(match.second);
106      }
107    }
108    return ObjectSP();
109  }
110
111  if (this->GetType() == lldb::eStructuredDataTypeArray) {
112    std::pair<llvm::StringRef, llvm::StringRef> match = path.split('[');
113    if (match.second.empty()) {
114      return this->shared_from_this();
115    }
116    errno = 0;
117    uint64_t val = strtoul(match.second.str().c_str(), nullptr, 10);
118    if (errno == 0) {
119      return this->GetAsArray()->GetItemAtIndex(val);
120    }
121    return ObjectSP();
122  }
123
124  return this->shared_from_this();
125}
126
127void StructuredData::Object::DumpToStdout(bool pretty_print) const {
128  json::OStream stream(llvm::outs(), pretty_print ? 2 : 0);
129  Serialize(stream);
130}
131
132void StructuredData::Array::Serialize(json::OStream &s) const {
133  s.arrayBegin();
134  for (const auto &item_sp : m_items) {
135    item_sp->Serialize(s);
136  }
137  s.arrayEnd();
138}
139
140void StructuredData::Integer::Serialize(json::OStream &s) const {
141  s.value(static_cast<int64_t>(m_value));
142}
143
144void StructuredData::Float::Serialize(json::OStream &s) const {
145  s.value(m_value);
146}
147
148void StructuredData::Boolean::Serialize(json::OStream &s) const {
149  s.value(m_value);
150}
151
152void StructuredData::String::Serialize(json::OStream &s) const {
153  s.value(m_value);
154}
155
156void StructuredData::Dictionary::Serialize(json::OStream &s) const {
157  s.objectBegin();
158  for (const auto &pair : m_dict) {
159    s.attributeBegin(pair.first.AsCString());
160    pair.second->Serialize(s);
161    s.attributeEnd();
162  }
163  s.objectEnd();
164}
165
166void StructuredData::Null::Serialize(json::OStream &s) const {
167  s.value(nullptr);
168}
169
170void StructuredData::Generic::Serialize(json::OStream &s) const {
171  s.value(llvm::formatv("{0:X}", m_object));
172}
173