1 //===-- OptionValueDictionary.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/Interpreter/OptionValueDictionary.h" 10 11 #include "lldb/DataFormatters/FormatManager.h" 12 #include "lldb/Interpreter/OptionValueEnumeration.h" 13 #include "lldb/Interpreter/OptionValueString.h" 14 #include "lldb/Utility/Args.h" 15 #include "lldb/Utility/State.h" 16 #include "llvm/ADT/StringRef.h" 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, 22 Stream &strm, uint32_t dump_mask) { 23 const Type dict_type = ConvertTypeMaskToType(m_type_mask); 24 if (dump_mask & eDumpOptionType) { 25 if (m_type_mask != eTypeInvalid) 26 strm.Printf("(%s of %ss)", GetTypeAsCString(), 27 GetBuiltinTypeAsCString(dict_type)); 28 else 29 strm.Printf("(%s)", GetTypeAsCString()); 30 } 31 if (dump_mask & eDumpOptionValue) { 32 const bool one_line = dump_mask & eDumpOptionCommand; 33 if (dump_mask & eDumpOptionType) 34 strm.PutCString(" ="); 35 36 collection::iterator pos, end = m_values.end(); 37 38 if (!one_line) 39 strm.IndentMore(); 40 41 for (pos = m_values.begin(); pos != end; ++pos) { 42 OptionValue *option_value = pos->second.get(); 43 44 if (one_line) 45 strm << ' '; 46 else 47 strm.EOL(); 48 49 strm.Indent(pos->first.GetStringRef()); 50 51 const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; 52 switch (dict_type) { 53 default: 54 case eTypeArray: 55 case eTypeDictionary: 56 case eTypeProperties: 57 case eTypeFileSpecList: 58 case eTypePathMap: 59 strm.PutChar(' '); 60 option_value->DumpValue(exe_ctx, strm, dump_mask | extra_dump_options); 61 break; 62 63 case eTypeBoolean: 64 case eTypeChar: 65 case eTypeEnum: 66 case eTypeFileLineColumn: 67 case eTypeFileSpec: 68 case eTypeFormat: 69 case eTypeSInt64: 70 case eTypeString: 71 case eTypeUInt64: 72 case eTypeUUID: 73 // No need to show the type for dictionaries of simple items 74 strm.PutCString("="); 75 option_value->DumpValue(exe_ctx, strm, 76 (dump_mask & (~eDumpOptionType)) | 77 extra_dump_options); 78 break; 79 } 80 } 81 if (!one_line) 82 strm.IndentLess(); 83 } 84 } 85 86 llvm::json::Value 87 OptionValueDictionary::ToJSON(const ExecutionContext *exe_ctx) { 88 llvm::json::Object dict; 89 for (const auto &value : m_values) { 90 dict.try_emplace(value.first.GetCString(), value.second->ToJSON(exe_ctx)); 91 } 92 return dict; 93 } 94 95 size_t OptionValueDictionary::GetArgs(Args &args) const { 96 args.Clear(); 97 collection::const_iterator pos, end = m_values.end(); 98 for (pos = m_values.begin(); pos != end; ++pos) { 99 StreamString strm; 100 strm.Printf("%s=", pos->first.GetCString()); 101 pos->second->DumpValue(nullptr, strm, eDumpOptionValue | eDumpOptionRaw); 102 args.AppendArgument(strm.GetString()); 103 } 104 return args.GetArgumentCount(); 105 } 106 107 Status OptionValueDictionary::SetArgs(const Args &args, 108 VarSetOperationType op) { 109 Status error; 110 const size_t argc = args.GetArgumentCount(); 111 switch (op) { 112 case eVarSetOperationClear: 113 Clear(); 114 break; 115 116 case eVarSetOperationAppend: 117 case eVarSetOperationReplace: 118 case eVarSetOperationAssign: 119 if (argc == 0) { 120 error.SetErrorString( 121 "assign operation takes one or more key=value arguments"); 122 return error; 123 } 124 for (const auto &entry : args) { 125 if (entry.ref().empty()) { 126 error.SetErrorString("empty argument"); 127 return error; 128 } 129 if (!entry.ref().contains('=')) { 130 error.SetErrorString( 131 "assign operation takes one or more key=value arguments"); 132 return error; 133 } 134 135 llvm::StringRef key, value; 136 std::tie(key, value) = entry.ref().split('='); 137 bool key_valid = false; 138 if (key.empty()) { 139 error.SetErrorString("empty dictionary key"); 140 return error; 141 } 142 143 if (key.front() == '[') { 144 // Key name starts with '[', so the key value must be in single or 145 // double quotes like: ['<key>'] ["<key>"] 146 if ((key.size() > 2) && (key.back() == ']')) { 147 // Strip leading '[' and trailing ']' 148 key = key.substr(1, key.size() - 2); 149 const char quote_char = key.front(); 150 if ((quote_char == '\'') || (quote_char == '"')) { 151 if ((key.size() > 2) && (key.back() == quote_char)) { 152 // Strip the quotes 153 key = key.substr(1, key.size() - 2); 154 key_valid = true; 155 } 156 } else { 157 // square brackets, no quotes 158 key_valid = true; 159 } 160 } 161 } else { 162 // No square brackets or quotes 163 key_valid = true; 164 } 165 if (!key_valid) { 166 error.SetErrorStringWithFormat( 167 "invalid key \"%s\", the key must be a bare string or " 168 "surrounded by brackets with optional quotes: [<key>] or " 169 "['<key>'] or [\"<key>\"]", 170 key.str().c_str()); 171 return error; 172 } 173 174 if (m_type_mask == 1u << eTypeEnum) { 175 auto enum_value = 176 std::make_shared<OptionValueEnumeration>(m_enum_values, 0); 177 error = enum_value->SetValueFromString(value); 178 if (error.Fail()) 179 return error; 180 m_value_was_set = true; 181 SetValueForKey(ConstString(key), enum_value, true); 182 } else { 183 lldb::OptionValueSP value_sp(CreateValueFromCStringForTypeMask( 184 value.str().c_str(), m_type_mask, error)); 185 if (value_sp) { 186 if (error.Fail()) 187 return error; 188 m_value_was_set = true; 189 SetValueForKey(ConstString(key), value_sp, true); 190 } else { 191 error.SetErrorString("dictionaries that can contain multiple types " 192 "must subclass OptionValueArray"); 193 } 194 } 195 } 196 break; 197 198 case eVarSetOperationRemove: 199 if (argc > 0) { 200 for (size_t i = 0; i < argc; ++i) { 201 ConstString key(args.GetArgumentAtIndex(i)); 202 if (!DeleteValueForKey(key)) { 203 error.SetErrorStringWithFormat( 204 "no value found named '%s', aborting remove operation", 205 key.GetCString()); 206 break; 207 } 208 } 209 } else { 210 error.SetErrorString("remove operation takes one or more key arguments"); 211 } 212 break; 213 214 case eVarSetOperationInsertBefore: 215 case eVarSetOperationInsertAfter: 216 case eVarSetOperationInvalid: 217 error = OptionValue::SetValueFromString(llvm::StringRef(), op); 218 break; 219 } 220 return error; 221 } 222 223 Status OptionValueDictionary::SetValueFromString(llvm::StringRef value, 224 VarSetOperationType op) { 225 Args args(value.str()); 226 Status error = SetArgs(args, op); 227 if (error.Success()) 228 NotifyValueChanged(); 229 return error; 230 } 231 232 lldb::OptionValueSP 233 OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx, 234 llvm::StringRef name, bool will_modify, 235 Status &error) const { 236 lldb::OptionValueSP value_sp; 237 if (name.empty()) 238 return nullptr; 239 240 llvm::StringRef left, temp; 241 std::tie(left, temp) = name.split('['); 242 if (left.size() == name.size()) { 243 error.SetErrorStringWithFormat("invalid value path '%s', %s values only " 244 "support '[<key>]' subvalues where <key> " 245 "a string value optionally delimited by " 246 "single or double quotes", 247 name.str().c_str(), GetTypeAsCString()); 248 return nullptr; 249 } 250 assert(!temp.empty()); 251 252 llvm::StringRef key, quote_char; 253 254 if (temp[0] == '\"' || temp[0] == '\'') { 255 quote_char = temp.take_front(); 256 temp = temp.drop_front(); 257 } 258 259 llvm::StringRef sub_name; 260 std::tie(key, sub_name) = temp.split(']'); 261 262 if (!key.consume_back(quote_char) || key.empty()) { 263 error.SetErrorStringWithFormat("invalid value path '%s', " 264 "key names must be formatted as ['<key>'] where <key> " 265 "is a string that doesn't contain quotes and the quote" 266 " char is optional", name.str().c_str()); 267 return nullptr; 268 } 269 270 value_sp = GetValueForKey(ConstString(key)); 271 if (!value_sp) { 272 error.SetErrorStringWithFormat( 273 "dictionary does not contain a value for the key name '%s'", 274 key.str().c_str()); 275 return nullptr; 276 } 277 278 if (sub_name.empty()) 279 return value_sp; 280 return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error); 281 } 282 283 Status OptionValueDictionary::SetSubValue(const ExecutionContext *exe_ctx, 284 VarSetOperationType op, 285 llvm::StringRef name, 286 llvm::StringRef value) { 287 Status error; 288 const bool will_modify = true; 289 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error)); 290 if (value_sp) 291 error = value_sp->SetValueFromString(value, op); 292 else { 293 if (error.AsCString() == nullptr) 294 error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str()); 295 } 296 return error; 297 } 298 299 lldb::OptionValueSP 300 OptionValueDictionary::GetValueForKey(ConstString key) const { 301 lldb::OptionValueSP value_sp; 302 collection::const_iterator pos = m_values.find(key); 303 if (pos != m_values.end()) 304 value_sp = pos->second; 305 return value_sp; 306 } 307 308 bool OptionValueDictionary::SetValueForKey(ConstString key, 309 const lldb::OptionValueSP &value_sp, 310 bool can_replace) { 311 // Make sure the value_sp object is allowed to contain values of the type 312 // passed in... 313 if (value_sp && (m_type_mask & value_sp->GetTypeAsMask())) { 314 if (!can_replace) { 315 collection::const_iterator pos = m_values.find(key); 316 if (pos != m_values.end()) 317 return false; 318 } 319 m_values[key] = value_sp; 320 return true; 321 } 322 return false; 323 } 324 325 bool OptionValueDictionary::DeleteValueForKey(ConstString key) { 326 collection::iterator pos = m_values.find(key); 327 if (pos != m_values.end()) { 328 m_values.erase(pos); 329 return true; 330 } 331 return false; 332 } 333 334 OptionValueSP 335 OptionValueDictionary::DeepCopy(const OptionValueSP &new_parent) const { 336 auto copy_sp = OptionValue::DeepCopy(new_parent); 337 // copy_sp->GetAsDictionary cannot be used here as it doesn't work for derived 338 // types that override GetType returning a different value. 339 auto *dict_value_ptr = static_cast<OptionValueDictionary *>(copy_sp.get()); 340 lldbassert(dict_value_ptr); 341 342 for (auto &value : dict_value_ptr->m_values) 343 value.second = value.second->DeepCopy(copy_sp); 344 345 return copy_sp; 346 } 347