1 //===-- OptionValueProperties.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/OptionValueProperties.h" 10 11 #include "lldb/Utility/Flags.h" 12 13 #include "lldb/Core/UserSettingsController.h" 14 #include "lldb/Interpreter/OptionValues.h" 15 #include "lldb/Interpreter/Property.h" 16 #include "lldb/Utility/Args.h" 17 #include "lldb/Utility/Stream.h" 18 #include "lldb/Utility/StringList.h" 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 OptionValueProperties::OptionValueProperties(llvm::StringRef name) 24 : m_name(name.str()) {} 25 26 void OptionValueProperties::Initialize(const PropertyDefinitions &defs) { 27 for (const auto &definition : defs) { 28 Property property(definition); 29 assert(property.IsValid()); 30 m_name_to_index.insert({property.GetName(), m_properties.size()}); 31 property.GetValue()->SetParent(shared_from_this()); 32 m_properties.push_back(property); 33 } 34 } 35 36 void OptionValueProperties::SetValueChangedCallback( 37 size_t property_idx, std::function<void()> callback) { 38 Property *property = ProtectedGetPropertyAtIndex(property_idx); 39 if (property) 40 property->SetValueChangedCallback(std::move(callback)); 41 } 42 43 void OptionValueProperties::AppendProperty(llvm::StringRef name, 44 llvm::StringRef desc, bool is_global, 45 const OptionValueSP &value_sp) { 46 Property property(name, desc, is_global, value_sp); 47 m_name_to_index.insert({name, m_properties.size()}); 48 m_properties.push_back(property); 49 value_sp->SetParent(shared_from_this()); 50 } 51 52 lldb::OptionValueSP 53 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx, 54 llvm::StringRef key) const { 55 auto iter = m_name_to_index.find(key); 56 if (iter == m_name_to_index.end()) 57 return OptionValueSP(); 58 const size_t idx = iter->second; 59 if (idx >= m_properties.size()) 60 return OptionValueSP(); 61 return GetPropertyAtIndex(idx, exe_ctx)->GetValue(); 62 } 63 64 lldb::OptionValueSP 65 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx, 66 llvm::StringRef name, Status &error) const { 67 lldb::OptionValueSP value_sp; 68 if (name.empty()) 69 return OptionValueSP(); 70 71 llvm::StringRef sub_name; 72 llvm::StringRef key; 73 size_t key_len = name.find_first_of(".[{"); 74 if (key_len != llvm::StringRef::npos) { 75 key = name.take_front(key_len); 76 sub_name = name.drop_front(key_len); 77 } else 78 key = name; 79 80 value_sp = GetValueForKey(exe_ctx, key); 81 if (sub_name.empty() || !value_sp) 82 return value_sp; 83 84 switch (sub_name[0]) { 85 case '.': { 86 lldb::OptionValueSP return_val_sp; 87 return_val_sp = 88 value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), error); 89 if (!return_val_sp) { 90 if (Properties::IsSettingExperimental(sub_name.drop_front())) { 91 const size_t experimental_len = 92 Properties::GetExperimentalSettingsName().size(); 93 if (sub_name[experimental_len + 1] == '.') 94 return_val_sp = value_sp->GetSubValue( 95 exe_ctx, sub_name.drop_front(experimental_len + 2), error); 96 // It isn't an error if an experimental setting is not present. 97 if (!return_val_sp) 98 error.Clear(); 99 } 100 } 101 return return_val_sp; 102 } 103 case '[': 104 // Array or dictionary access for subvalues like: "[12]" -- access 105 // 12th array element "['hello']" -- dictionary access of key named hello 106 return value_sp->GetSubValue(exe_ctx, sub_name, error); 107 108 default: 109 value_sp.reset(); 110 break; 111 } 112 return value_sp; 113 } 114 115 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx, 116 VarSetOperationType op, 117 llvm::StringRef name, 118 llvm::StringRef value) { 119 Status error; 120 llvm::SmallVector<llvm::StringRef, 8> components; 121 name.split(components, '.'); 122 bool name_contains_experimental = false; 123 for (const auto &part : components) 124 if (Properties::IsSettingExperimental(part)) 125 name_contains_experimental = true; 126 127 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error)); 128 if (value_sp) 129 error = value_sp->SetValueFromString(value, op); 130 else { 131 // Don't set an error if the path contained .experimental. - those are 132 // allowed to be missing and should silently fail. 133 if (!name_contains_experimental && error.AsCString() == nullptr) { 134 error.SetErrorStringWithFormat("invalid value path '%s'", 135 name.str().c_str()); 136 } 137 } 138 return error; 139 } 140 141 size_t OptionValueProperties::GetPropertyIndex(llvm::StringRef name) const { 142 auto iter = m_name_to_index.find(name); 143 if (iter == m_name_to_index.end()) 144 return SIZE_MAX; 145 return iter->second; 146 } 147 148 const Property * 149 OptionValueProperties::GetProperty(llvm::StringRef name, 150 const ExecutionContext *exe_ctx) const { 151 auto iter = m_name_to_index.find(name); 152 if (iter == m_name_to_index.end()) 153 return nullptr; 154 return GetPropertyAtIndex(iter->second, exe_ctx); 155 } 156 157 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex( 158 size_t idx, const ExecutionContext *exe_ctx) const { 159 const Property *setting = GetPropertyAtIndex(idx, exe_ctx); 160 if (setting) 161 return setting->GetValue(); 162 return OptionValueSP(); 163 } 164 165 OptionValuePathMappings * 166 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings( 167 size_t idx, const ExecutionContext *exe_ctx) const { 168 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); 169 if (value_sp) 170 return value_sp->GetAsPathMappings(); 171 return nullptr; 172 } 173 174 OptionValueFileSpecList * 175 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList( 176 size_t idx, const ExecutionContext *exe_ctx) const { 177 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); 178 if (value_sp) 179 return value_sp->GetAsFileSpecList(); 180 return nullptr; 181 } 182 183 bool OptionValueProperties::GetPropertyAtIndexAsArgs( 184 size_t idx, Args &args, const ExecutionContext *exe_ctx) const { 185 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 186 if (!property) 187 return false; 188 189 OptionValue *value = property->GetValue().get(); 190 if (!value) 191 return false; 192 193 const OptionValueArgs *arguments = value->GetAsArgs(); 194 if (arguments) { 195 arguments->GetArgs(args); 196 return true; 197 } 198 199 const OptionValueArray *array = value->GetAsArray(); 200 if (array) { 201 array->GetArgs(args); 202 return true; 203 } 204 205 const OptionValueDictionary *dict = value->GetAsDictionary(); 206 if (dict) { 207 dict->GetArgs(args); 208 return true; 209 } 210 211 return false; 212 } 213 214 bool OptionValueProperties::SetPropertyAtIndexFromArgs( 215 size_t idx, const Args &args, const ExecutionContext *exe_ctx) { 216 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 217 if (!property) 218 return false; 219 220 OptionValue *value = property->GetValue().get(); 221 if (!value) 222 return false; 223 224 OptionValueArgs *arguments = value->GetAsArgs(); 225 if (arguments) 226 return arguments->SetArgs(args, eVarSetOperationAssign).Success(); 227 228 OptionValueArray *array = value->GetAsArray(); 229 if (array) 230 return array->SetArgs(args, eVarSetOperationAssign).Success(); 231 232 OptionValueDictionary *dict = value->GetAsDictionary(); 233 if (dict) 234 return dict->SetArgs(args, eVarSetOperationAssign).Success(); 235 236 return false; 237 } 238 239 OptionValueDictionary * 240 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary( 241 size_t idx, const ExecutionContext *exe_ctx) const { 242 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 243 if (property) 244 return property->GetValue()->GetAsDictionary(); 245 return nullptr; 246 } 247 248 OptionValueFileSpec * 249 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec( 250 size_t idx, const ExecutionContext *exe_ctx) const { 251 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 252 if (property) { 253 OptionValue *value = property->GetValue().get(); 254 if (value) 255 return value->GetAsFileSpec(); 256 } 257 return nullptr; 258 } 259 260 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64( 261 size_t idx, const ExecutionContext *exe_ctx) const { 262 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 263 if (property) { 264 OptionValue *value = property->GetValue().get(); 265 if (value) 266 return value->GetAsSInt64(); 267 } 268 return nullptr; 269 } 270 271 OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64( 272 size_t idx, const ExecutionContext *exe_ctx) const { 273 const Property *property = GetPropertyAtIndex(idx, exe_ctx); 274 if (property) { 275 OptionValue *value = property->GetValue().get(); 276 if (value) 277 return value->GetAsUInt64(); 278 } 279 return nullptr; 280 } 281 282 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString( 283 size_t idx, const ExecutionContext *exe_ctx) const { 284 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx)); 285 if (value_sp) 286 return value_sp->GetAsString(); 287 return nullptr; 288 } 289 290 void OptionValueProperties::Clear() { 291 const size_t num_properties = m_properties.size(); 292 for (size_t i = 0; i < num_properties; ++i) 293 m_properties[i].GetValue()->Clear(); 294 } 295 296 Status OptionValueProperties::SetValueFromString(llvm::StringRef value, 297 VarSetOperationType op) { 298 Status error; 299 300 // Args args(value_cstr); 301 // const size_t argc = args.GetArgumentCount(); 302 switch (op) { 303 case eVarSetOperationClear: 304 Clear(); 305 break; 306 307 case eVarSetOperationReplace: 308 case eVarSetOperationAssign: 309 case eVarSetOperationRemove: 310 case eVarSetOperationInsertBefore: 311 case eVarSetOperationInsertAfter: 312 case eVarSetOperationAppend: 313 case eVarSetOperationInvalid: 314 error = OptionValue::SetValueFromString(value, op); 315 break; 316 } 317 318 return error; 319 } 320 321 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, 322 Stream &strm, uint32_t dump_mask) { 323 const size_t num_properties = m_properties.size(); 324 for (size_t i = 0; i < num_properties; ++i) { 325 const Property *property = GetPropertyAtIndex(i, exe_ctx); 326 if (property) { 327 OptionValue *option_value = property->GetValue().get(); 328 assert(option_value); 329 const bool transparent_value = option_value->ValueIsTransparent(); 330 property->Dump(exe_ctx, strm, dump_mask); 331 if (!transparent_value) 332 strm.EOL(); 333 } 334 } 335 } 336 337 llvm::json::Value 338 OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) { 339 llvm::json::Object json_properties; 340 const size_t num_properties = m_properties.size(); 341 for (size_t i = 0; i < num_properties; ++i) { 342 const Property *property = GetPropertyAtIndex(i, exe_ctx); 343 if (property) { 344 OptionValue *option_value = property->GetValue().get(); 345 assert(option_value); 346 json_properties.try_emplace(property->GetName(), 347 option_value->ToJSON(exe_ctx)); 348 } 349 } 350 return json_properties; 351 } 352 353 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx, 354 Stream &strm, 355 llvm::StringRef property_path, 356 uint32_t dump_mask, 357 bool is_json) { 358 Status error; 359 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, property_path, error)); 360 if (value_sp) { 361 if (!value_sp->ValueIsTransparent()) { 362 if (dump_mask & eDumpOptionName) 363 strm.PutCString(property_path); 364 if (dump_mask & ~eDumpOptionName) 365 strm.PutChar(' '); 366 } 367 if (is_json) { 368 strm.Printf( 369 "%s", 370 llvm::formatv("{0:2}", value_sp->ToJSON(exe_ctx)).str().c_str()); 371 } else 372 value_sp->DumpValue(exe_ctx, strm, dump_mask); 373 } 374 return error; 375 } 376 377 OptionValuePropertiesSP 378 OptionValueProperties::CreateLocalCopy(const Properties &global_properties) { 379 auto global_props_sp = global_properties.GetValueProperties(); 380 lldbassert(global_props_sp); 381 382 auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent()); 383 return std::static_pointer_cast<OptionValueProperties>(copy_sp); 384 } 385 386 OptionValueSP 387 OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const { 388 auto copy_sp = OptionValue::DeepCopy(new_parent); 389 // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived 390 // types that override GetType returning a different value. 391 auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get()); 392 lldbassert(props_value_ptr); 393 394 for (auto &property : props_value_ptr->m_properties) { 395 // Duplicate any values that are not global when constructing properties 396 // from a global copy. 397 if (!property.IsGlobal()) { 398 auto value_sp = property.GetValue()->DeepCopy(copy_sp); 399 property.SetOptionValue(value_sp); 400 } 401 } 402 return copy_sp; 403 } 404 405 const Property * 406 OptionValueProperties::GetPropertyAtPath(const ExecutionContext *exe_ctx, 407 llvm::StringRef name) const { 408 if (name.empty()) 409 return nullptr; 410 411 const Property *property = nullptr; 412 llvm::StringRef sub_name; 413 llvm::StringRef key; 414 size_t key_len = name.find_first_of(".[{"); 415 416 if (key_len != llvm::StringRef::npos) { 417 key = name.take_front(key_len); 418 sub_name = name.drop_front(key_len); 419 } else 420 key = name; 421 422 property = GetProperty(key, exe_ctx); 423 if (sub_name.empty() || !property) 424 return property; 425 426 if (sub_name[0] == '.') { 427 OptionValueProperties *sub_properties = 428 property->GetValue()->GetAsProperties(); 429 if (sub_properties) 430 return sub_properties->GetPropertyAtPath(exe_ctx, sub_name.drop_front()); 431 } 432 return nullptr; 433 } 434 435 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter, 436 Stream &strm) const { 437 size_t max_name_len = 0; 438 const size_t num_properties = m_properties.size(); 439 for (size_t i = 0; i < num_properties; ++i) { 440 const Property *property = ProtectedGetPropertyAtIndex(i); 441 if (property) 442 max_name_len = std::max<size_t>(property->GetName().size(), max_name_len); 443 } 444 for (size_t i = 0; i < num_properties; ++i) { 445 const Property *property = ProtectedGetPropertyAtIndex(i); 446 if (property) 447 property->DumpDescription(interpreter, strm, max_name_len, false); 448 } 449 } 450 451 void OptionValueProperties::Apropos( 452 llvm::StringRef keyword, 453 std::vector<const Property *> &matching_properties) const { 454 const size_t num_properties = m_properties.size(); 455 StreamString strm; 456 for (size_t i = 0; i < num_properties; ++i) { 457 const Property *property = ProtectedGetPropertyAtIndex(i); 458 if (property) { 459 const OptionValueProperties *properties = 460 property->GetValue()->GetAsProperties(); 461 if (properties) { 462 properties->Apropos(keyword, matching_properties); 463 } else { 464 bool match = false; 465 llvm::StringRef name = property->GetName(); 466 if (name.contains_insensitive(keyword)) 467 match = true; 468 else { 469 llvm::StringRef desc = property->GetDescription(); 470 if (desc.contains_insensitive(keyword)) 471 match = true; 472 } 473 if (match) { 474 matching_properties.push_back(property); 475 } 476 } 477 } 478 } 479 } 480 481 lldb::OptionValuePropertiesSP 482 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx, 483 llvm::StringRef name) { 484 lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name)); 485 if (option_value_sp) { 486 OptionValueProperties *ov_properties = option_value_sp->GetAsProperties(); 487 if (ov_properties) 488 return ov_properties->shared_from_this(); 489 } 490 return lldb::OptionValuePropertiesSP(); 491 } 492