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