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