292 lines · cpp
1//===-- StructuredData.cpp ------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 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"14#include <cerrno>15#include <cinttypes>16#include <cstdlib>17 18using namespace lldb_private;19using namespace llvm;20 21static StructuredData::ObjectSP ParseJSONValue(json::Value &value);22static StructuredData::ObjectSP ParseJSONObject(json::Object *object);23static StructuredData::ObjectSP ParseJSONArray(json::Array *array);24 25StructuredData::ObjectSP StructuredData::ParseJSON(llvm::StringRef json_text) {26 llvm::Expected<json::Value> value = json::parse(json_text);27 if (!value) {28 llvm::consumeError(value.takeError());29 return nullptr;30 }31 return ParseJSONValue(*value);32}33 34StructuredData::ObjectSP35StructuredData::ParseJSONFromFile(const FileSpec &input_spec, Status &error) {36 StructuredData::ObjectSP return_sp;37 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());43 return return_sp;44 }45 llvm::Expected<json::Value> value =46 json::parse(buffer_or_error.get()->getBuffer().str());47 if (value)48 return ParseJSONValue(*value);49 error = Status::FromError(value.takeError());50 return StructuredData::ObjectSP();51}52 53bool StructuredData::IsRecordType(const ObjectSP object_sp) {54 return object_sp->GetType() == lldb::eStructuredDataTypeArray ||55 object_sp->GetType() == lldb::eStructuredDataTypeDictionary;56}57 58static StructuredData::ObjectSP ParseJSONValue(json::Value &value) {59 if (json::Object *O = value.getAsObject())60 return ParseJSONObject(O);61 62 if (json::Array *A = value.getAsArray())63 return ParseJSONArray(A);64 65 if (auto s = value.getAsString())66 return std::make_shared<StructuredData::String>(*s);67 68 if (auto b = value.getAsBoolean())69 return std::make_shared<StructuredData::Boolean>(*b);70 71 if (auto u = value.getAsUINT64())72 return std::make_shared<StructuredData::UnsignedInteger>(*u);73 74 if (auto i = value.getAsInteger())75 return std::make_shared<StructuredData::SignedInteger>(*i);76 77 if (auto d = value.getAsNumber())78 return std::make_shared<StructuredData::Float>(*d);79 80 if (auto n = value.getAsNull())81 return std::make_shared<StructuredData::Null>();82 83 return StructuredData::ObjectSP();84}85 86static 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);93 }94 return std::move(dict_up);95}96 97static 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);102 }103 return std::move(array_up);104}105 106StructuredData::ObjectSP107StructuredData::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);112 if (!value)113 return {};114 115 // Do we have additional words to descend? If not, return the value116 // we're at right now.117 if (match.second.empty())118 return value;119 120 return value->GetObjectForDotSeparatedPath(match.second);121 }122 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();127 128 uint64_t val = 0;129 if (!llvm::to_integer(match.second, val, /* Base = */ 10))130 return {};131 132 return GetAsArray()->GetItemAtIndex(val);133 }134 135 return shared_from_this();136}137 138void StructuredData::Object::DumpToStdout(bool pretty_print) const {139 json::OStream stream(llvm::outs(), pretty_print ? 2 : 0);140 Serialize(stream);141}142 143void StructuredData::Array::Serialize(json::OStream &s) const {144 s.arrayBegin();145 for (const auto &item_sp : m_items) {146 item_sp->Serialize(s);147 }148 s.arrayEnd();149}150 151void StructuredData::Float::Serialize(json::OStream &s) const {152 s.value(m_value);153}154 155void StructuredData::Boolean::Serialize(json::OStream &s) const {156 s.value(m_value);157}158 159void StructuredData::String::Serialize(json::OStream &s) const {160 s.value(m_value);161}162 163void StructuredData::Dictionary::Serialize(json::OStream &s) const {164 s.objectBegin();165 166 // To ensure the output format is always stable, we sort the dictionary by key167 // first.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});172 173 llvm::sort(sorted_entries);174 175 for (const auto &pair : sorted_entries) {176 s.attributeBegin(pair.first);177 pair.second->Serialize(s);178 s.attributeEnd();179 }180 s.objectEnd();181}182 183void StructuredData::Null::Serialize(json::OStream &s) const {184 s.value(nullptr);185}186 187void StructuredData::Generic::Serialize(json::OStream &s) const {188 s.value(llvm::formatv("{0:X}", m_object));189}190 191void StructuredData::Float::GetDescription(lldb_private::Stream &s) const {192 s.Printf("%f", m_value);193}194 195void StructuredData::Boolean::GetDescription(lldb_private::Stream &s) const {196 s.Printf(m_value ? "True" : "False");197}198 199void StructuredData::String::GetDescription(lldb_private::Stream &s) const {200 s.Printf("%s", m_value.empty() ? "\"\"" : m_value.c_str());201}202 203void StructuredData::Array::GetDescription(lldb_private::Stream &s) const {204 size_t index = 0;205 size_t indentation_level = s.GetIndentLevel();206 for (const auto &item_sp : m_items) {207 // Sanitize.208 if (!item_sp)209 continue;210 211 // Reset original indentation level.212 s.SetIndentLevel(indentation_level);213 s.Indent();214 215 // Print key216 s.Printf("[%zu]:", index++);217 218 // Return to new line and increase indentation if value is record type.219 // Otherwise add spacing.220 bool should_indent = IsRecordType(item_sp);221 if (should_indent) {222 s.EOL();223 s.IndentMore();224 } else {225 s.PutChar(' ');226 }227 228 // Print value and new line if now last pair.229 item_sp->GetDescription(s);230 if (item_sp != *(--m_items.end()))231 s.EOL();232 233 // Reset indentation level if it was incremented previously.234 if (should_indent)235 s.IndentLess();236 }237}238 239void StructuredData::Dictionary::GetDescription(lldb_private::Stream &s) const {240 size_t indentation_level = s.GetIndentLevel();241 242 // To ensure the output format is always stable, we sort the dictionary by key243 // first.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});248 249 llvm::sort(sorted_entries);250 251 for (auto iter = sorted_entries.begin(); iter != sorted_entries.end();252 iter++) {253 // Sanitize.254 if (iter->first.empty() || !iter->second)255 continue;256 257 // Reset original indentation level.258 s.SetIndentLevel(indentation_level);259 s.Indent();260 261 // Print key.262 s.Format("{0}:", iter->first);263 264 // Return to new line and increase indentation if value is record type.265 // Otherwise add spacing.266 bool should_indent = IsRecordType(iter->second);267 if (should_indent) {268 s.EOL();269 s.IndentMore();270 } else {271 s.PutChar(' ');272 }273 274 // Print value and new line if now last pair.275 iter->second->GetDescription(s);276 if (std::next(iter) != sorted_entries.end())277 s.EOL();278 279 // Reset indentation level if it was incremented previously.280 if (should_indent)281 s.IndentLess();282 }283}284 285void StructuredData::Null::GetDescription(lldb_private::Stream &s) const {286 s.Printf("NULL");287}288 289void StructuredData::Generic::GetDescription(lldb_private::Stream &s) const {290 s.Printf("%p", m_object);291}292