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