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