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(ConstString name) : m_name(name) {} 24 25 size_t OptionValueProperties::GetNumProperties() const { 26 return m_properties.size(); 27 } 28 29 void OptionValueProperties::Initialize(const PropertyDefinitions &defs) { 30 for (const auto &definition : defs) { 31 Property property(definition); 32 assert(property.IsValid()); 33 m_name_to_index.Append(ConstString(property.GetName()), m_properties.size()); 34 property.GetValue()->SetParent(shared_from_this()); 35 m_properties.push_back(property); 36 } 37 m_name_to_index.Sort(); 38 } 39 40 void OptionValueProperties::SetValueChangedCallback( 41 uint32_t property_idx, std::function<void()> callback) { 42 Property *property = ProtectedGetPropertyAtIndex(property_idx); 43 if (property) 44 property->SetValueChangedCallback(std::move(callback)); 45 } 46 47 void OptionValueProperties::AppendProperty(ConstString name, 48 ConstString desc, 49 bool is_global, 50 const OptionValueSP &value_sp) { 51 Property property(name, desc, is_global, value_sp); 52 m_name_to_index.Append(name, m_properties.size()); 53 m_properties.push_back(property); 54 value_sp->SetParent(shared_from_this()); 55 m_name_to_index.Sort(); 56 } 57 58 // bool 59 // OptionValueProperties::GetQualifiedName (Stream &strm) 60 //{ 61 // bool dumped_something = false; 62 //// lldb::OptionValuePropertiesSP parent_sp(GetParent ()); 63 //// if (parent_sp) 64 //// { 65 //// parent_sp->GetQualifiedName (strm); 66 //// strm.PutChar('.'); 67 //// dumped_something = true; 68 //// } 69 // if (m_name) 70 // { 71 // strm << m_name; 72 // dumped_something = true; 73 // } 74 // return dumped_something; 75 //} 76 // 77 lldb::OptionValueSP 78 OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx, 79 ConstString key, 80 bool will_modify) const { 81 lldb::OptionValueSP value_sp; 82 size_t idx = m_name_to_index.Find(key, SIZE_MAX); 83 if (idx < m_properties.size()) 84 value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue(); 85 return value_sp; 86 } 87 88 lldb::OptionValueSP 89 OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx, 90 llvm::StringRef name, bool will_modify, 91 Status &error) const { 92 lldb::OptionValueSP value_sp; 93 if (name.empty()) 94 return OptionValueSP(); 95 96 llvm::StringRef sub_name; 97 ConstString key; 98 size_t key_len = name.find_first_of(".[{"); 99 if (key_len != llvm::StringRef::npos) { 100 key.SetString(name.take_front(key_len)); 101 sub_name = name.drop_front(key_len); 102 } else 103 key.SetString(name); 104 105 value_sp = GetValueForKey(exe_ctx, key, will_modify); 106 if (sub_name.empty() || !value_sp) 107 return value_sp; 108 109 switch (sub_name[0]) { 110 case '.': { 111 lldb::OptionValueSP return_val_sp; 112 return_val_sp = 113 value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error); 114 if (!return_val_sp) { 115 if (Properties::IsSettingExperimental(sub_name.drop_front())) { 116 size_t experimental_len = 117 strlen(Properties::GetExperimentalSettingsName()); 118 if (sub_name[experimental_len + 1] == '.') 119 return_val_sp = value_sp->GetSubValue( 120 exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error); 121 // It isn't an error if an experimental setting is not present. 122 if (!return_val_sp) 123 error.Clear(); 124 } 125 } 126 return return_val_sp; 127 } 128 case '[': 129 // Array or dictionary access for subvalues like: "[12]" -- access 130 // 12th array element "['hello']" -- dictionary access of key named hello 131 return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error); 132 133 default: 134 value_sp.reset(); 135 break; 136 } 137 return value_sp; 138 } 139 140 Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx, 141 VarSetOperationType op, 142 llvm::StringRef name, 143 llvm::StringRef value) { 144 Status error; 145 const bool will_modify = true; 146 llvm::SmallVector<llvm::StringRef, 8> components; 147 name.split(components, '.'); 148 bool name_contains_experimental = false; 149 for (const auto &part : components) 150 if (Properties::IsSettingExperimental(part)) 151 name_contains_experimental = true; 152 153 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error)); 154 if (value_sp) 155 error = value_sp->SetValueFromString(value, op); 156 else { 157 // Don't set an error if the path contained .experimental. - those are 158 // allowed to be missing and should silently fail. 159 if (!name_contains_experimental && error.AsCString() == nullptr) { 160 error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str()); 161 } 162 } 163 return error; 164 } 165 166 uint32_t 167 OptionValueProperties::GetPropertyIndex(ConstString name) const { 168 return m_name_to_index.Find(name, SIZE_MAX); 169 } 170 171 const Property * 172 OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx, 173 bool will_modify, 174 ConstString name) const { 175 return GetPropertyAtIndex( 176 exe_ctx, will_modify, 177 m_name_to_index.Find(name, SIZE_MAX)); 178 } 179 180 const Property *OptionValueProperties::GetPropertyAtIndex( 181 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 182 return ProtectedGetPropertyAtIndex(idx); 183 } 184 185 lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex( 186 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 187 const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx); 188 if (setting) 189 return setting->GetValue(); 190 return OptionValueSP(); 191 } 192 193 OptionValuePathMappings * 194 OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings( 195 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 196 OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); 197 if (value_sp) 198 return value_sp->GetAsPathMappings(); 199 return nullptr; 200 } 201 202 OptionValueFileSpecList * 203 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList( 204 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 205 OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); 206 if (value_sp) 207 return value_sp->GetAsFileSpecList(); 208 return nullptr; 209 } 210 211 OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch( 212 const ExecutionContext *exe_ctx, uint32_t idx) const { 213 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 214 if (property) 215 return property->GetValue()->GetAsArch(); 216 return nullptr; 217 } 218 219 OptionValueLanguage * 220 OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage( 221 const ExecutionContext *exe_ctx, uint32_t idx) const { 222 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 223 if (property) 224 return property->GetValue()->GetAsLanguage(); 225 return nullptr; 226 } 227 228 bool OptionValueProperties::GetPropertyAtIndexAsArgs( 229 const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const { 230 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 231 if (!property) 232 return false; 233 234 OptionValue *value = property->GetValue().get(); 235 if (!value) 236 return false; 237 238 const OptionValueArgs *arguments = value->GetAsArgs(); 239 if (arguments) 240 return arguments->GetArgs(args); 241 242 const OptionValueArray *array = value->GetAsArray(); 243 if (array) 244 return array->GetArgs(args); 245 246 const OptionValueDictionary *dict = value->GetAsDictionary(); 247 if (dict) 248 return dict->GetArgs(args); 249 250 return false; 251 } 252 253 bool OptionValueProperties::SetPropertyAtIndexFromArgs( 254 const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) { 255 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 256 if (!property) 257 return false; 258 259 OptionValue *value = property->GetValue().get(); 260 if (!value) 261 return false; 262 263 OptionValueArgs *arguments = value->GetAsArgs(); 264 if (arguments) 265 return arguments->SetArgs(args, eVarSetOperationAssign).Success(); 266 267 OptionValueArray *array = value->GetAsArray(); 268 if (array) 269 return array->SetArgs(args, eVarSetOperationAssign).Success(); 270 271 OptionValueDictionary *dict = value->GetAsDictionary(); 272 if (dict) 273 return dict->SetArgs(args, eVarSetOperationAssign).Success(); 274 275 return false; 276 } 277 278 bool OptionValueProperties::GetPropertyAtIndexAsBoolean( 279 const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const { 280 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 281 if (property) { 282 OptionValue *value = property->GetValue().get(); 283 if (value) 284 return value->GetBooleanValue(fail_value); 285 } 286 return fail_value; 287 } 288 289 bool OptionValueProperties::SetPropertyAtIndexAsBoolean( 290 const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) { 291 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 292 if (property) { 293 OptionValue *value = property->GetValue().get(); 294 if (value) { 295 value->SetBooleanValue(new_value); 296 return true; 297 } 298 } 299 return false; 300 } 301 302 OptionValueDictionary * 303 OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary( 304 const ExecutionContext *exe_ctx, uint32_t idx) const { 305 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 306 if (property) 307 return property->GetValue()->GetAsDictionary(); 308 return nullptr; 309 } 310 311 int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration( 312 const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const { 313 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 314 if (property) { 315 OptionValue *value = property->GetValue().get(); 316 if (value) 317 return value->GetEnumerationValue(fail_value); 318 } 319 return fail_value; 320 } 321 322 bool OptionValueProperties::SetPropertyAtIndexAsEnumeration( 323 const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) { 324 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 325 if (property) { 326 OptionValue *value = property->GetValue().get(); 327 if (value) 328 return value->SetEnumerationValue(new_value); 329 } 330 return false; 331 } 332 333 const FormatEntity::Entry * 334 OptionValueProperties::GetPropertyAtIndexAsFormatEntity( 335 const ExecutionContext *exe_ctx, uint32_t idx) { 336 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 337 if (property) { 338 OptionValue *value = property->GetValue().get(); 339 if (value) 340 return value->GetFormatEntity(); 341 } 342 return nullptr; 343 } 344 345 OptionValueFileSpec * 346 OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec( 347 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 348 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 349 if (property) { 350 OptionValue *value = property->GetValue().get(); 351 if (value) 352 return value->GetAsFileSpec(); 353 } 354 return nullptr; 355 } 356 357 FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec( 358 const ExecutionContext *exe_ctx, uint32_t idx) const { 359 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 360 if (property) { 361 OptionValue *value = property->GetValue().get(); 362 if (value) 363 return value->GetFileSpecValue(); 364 } 365 return FileSpec(); 366 } 367 368 bool OptionValueProperties::SetPropertyAtIndexAsFileSpec( 369 const ExecutionContext *exe_ctx, uint32_t idx, 370 const FileSpec &new_file_spec) { 371 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 372 if (property) { 373 OptionValue *value = property->GetValue().get(); 374 if (value) 375 return value->SetFileSpecValue(new_file_spec); 376 } 377 return false; 378 } 379 380 const RegularExpression * 381 OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex( 382 const ExecutionContext *exe_ctx, uint32_t idx) const { 383 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 384 if (property) { 385 OptionValue *value = property->GetValue().get(); 386 if (value) 387 return value->GetRegexValue(); 388 } 389 return nullptr; 390 } 391 392 OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64( 393 const ExecutionContext *exe_ctx, uint32_t idx) const { 394 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 395 if (property) { 396 OptionValue *value = property->GetValue().get(); 397 if (value) 398 return value->GetAsSInt64(); 399 } 400 return nullptr; 401 } 402 403 int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64( 404 const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const { 405 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 406 if (property) { 407 OptionValue *value = property->GetValue().get(); 408 if (value) 409 return value->GetSInt64Value(fail_value); 410 } 411 return fail_value; 412 } 413 414 bool OptionValueProperties::SetPropertyAtIndexAsSInt64( 415 const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) { 416 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 417 if (property) { 418 OptionValue *value = property->GetValue().get(); 419 if (value) 420 return value->SetSInt64Value(new_value); 421 } 422 return false; 423 } 424 425 llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString( 426 const ExecutionContext *exe_ctx, uint32_t idx, 427 llvm::StringRef fail_value) const { 428 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 429 if (property) { 430 OptionValue *value = property->GetValue().get(); 431 if (value) 432 return value->GetStringValue(fail_value); 433 } 434 return fail_value; 435 } 436 437 bool OptionValueProperties::SetPropertyAtIndexAsString( 438 const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) { 439 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 440 if (property) { 441 OptionValue *value = property->GetValue().get(); 442 if (value) 443 return value->SetStringValue(new_value); 444 } 445 return false; 446 } 447 448 OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString( 449 const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { 450 OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); 451 if (value_sp) 452 return value_sp->GetAsString(); 453 return nullptr; 454 } 455 456 uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64( 457 const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const { 458 const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); 459 if (property) { 460 OptionValue *value = property->GetValue().get(); 461 if (value) 462 return value->GetUInt64Value(fail_value); 463 } 464 return fail_value; 465 } 466 467 bool OptionValueProperties::SetPropertyAtIndexAsUInt64( 468 const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) { 469 const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); 470 if (property) { 471 OptionValue *value = property->GetValue().get(); 472 if (value) 473 return value->SetUInt64Value(new_value); 474 } 475 return false; 476 } 477 478 void OptionValueProperties::Clear() { 479 const size_t num_properties = m_properties.size(); 480 for (size_t i = 0; i < num_properties; ++i) 481 m_properties[i].GetValue()->Clear(); 482 } 483 484 Status OptionValueProperties::SetValueFromString(llvm::StringRef value, 485 VarSetOperationType op) { 486 Status error; 487 488 // Args args(value_cstr); 489 // const size_t argc = args.GetArgumentCount(); 490 switch (op) { 491 case eVarSetOperationClear: 492 Clear(); 493 break; 494 495 case eVarSetOperationReplace: 496 case eVarSetOperationAssign: 497 case eVarSetOperationRemove: 498 case eVarSetOperationInsertBefore: 499 case eVarSetOperationInsertAfter: 500 case eVarSetOperationAppend: 501 case eVarSetOperationInvalid: 502 error = OptionValue::SetValueFromString(value, op); 503 break; 504 } 505 506 return error; 507 } 508 509 void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, 510 Stream &strm, uint32_t dump_mask) { 511 const size_t num_properties = m_properties.size(); 512 for (size_t i = 0; i < num_properties; ++i) { 513 const Property *property = GetPropertyAtIndex(exe_ctx, false, i); 514 if (property) { 515 OptionValue *option_value = property->GetValue().get(); 516 assert(option_value); 517 const bool transparent_value = option_value->ValueIsTransparent(); 518 property->Dump(exe_ctx, strm, dump_mask); 519 if (!transparent_value) 520 strm.EOL(); 521 } 522 } 523 } 524 525 Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx, 526 Stream &strm, 527 llvm::StringRef property_path, 528 uint32_t dump_mask) { 529 Status error; 530 const bool will_modify = false; 531 lldb::OptionValueSP value_sp( 532 GetSubValue(exe_ctx, property_path, will_modify, error)); 533 if (value_sp) { 534 if (!value_sp->ValueIsTransparent()) { 535 if (dump_mask & eDumpOptionName) 536 strm.PutCString(property_path); 537 if (dump_mask & ~eDumpOptionName) 538 strm.PutChar(' '); 539 } 540 value_sp->DumpValue(exe_ctx, strm, dump_mask); 541 } 542 return error; 543 } 544 545 OptionValuePropertiesSP 546 OptionValueProperties::CreateLocalCopy(const Properties &global_properties) { 547 auto global_props_sp = global_properties.GetValueProperties(); 548 lldbassert(global_props_sp); 549 550 auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent()); 551 return std::static_pointer_cast<OptionValueProperties>(copy_sp); 552 } 553 554 OptionValueSP 555 OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const { 556 auto copy_sp = OptionValue::DeepCopy(new_parent); 557 // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived 558 // types that override GetType returning a different value. 559 auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get()); 560 lldbassert(props_value_ptr); 561 562 for (auto &property : props_value_ptr->m_properties) { 563 // Duplicate any values that are not global when constructing properties 564 // from a global copy. 565 if (!property.IsGlobal()) { 566 auto value_sp = property.GetValue()->DeepCopy(copy_sp); 567 property.SetOptionValue(value_sp); 568 } 569 } 570 return copy_sp; 571 } 572 573 const Property *OptionValueProperties::GetPropertyAtPath( 574 const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const { 575 const Property *property = nullptr; 576 if (name.empty()) 577 return nullptr; 578 llvm::StringRef sub_name; 579 ConstString key; 580 size_t key_len = name.find_first_of(".[{"); 581 582 if (key_len != llvm::StringRef::npos) { 583 key.SetString(name.take_front(key_len)); 584 sub_name = name.drop_front(key_len); 585 } else 586 key.SetString(name); 587 588 property = GetProperty(exe_ctx, will_modify, key); 589 if (sub_name.empty() || !property) 590 return property; 591 592 if (sub_name[0] == '.') { 593 OptionValueProperties *sub_properties = 594 property->GetValue()->GetAsProperties(); 595 if (sub_properties) 596 return sub_properties->GetPropertyAtPath(exe_ctx, will_modify, 597 sub_name.drop_front()); 598 } 599 return nullptr; 600 } 601 602 void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter, 603 Stream &strm) const { 604 size_t max_name_len = 0; 605 const size_t num_properties = m_properties.size(); 606 for (size_t i = 0; i < num_properties; ++i) { 607 const Property *property = ProtectedGetPropertyAtIndex(i); 608 if (property) 609 max_name_len = std::max<size_t>(property->GetName().size(), max_name_len); 610 } 611 for (size_t i = 0; i < num_properties; ++i) { 612 const Property *property = ProtectedGetPropertyAtIndex(i); 613 if (property) 614 property->DumpDescription(interpreter, strm, max_name_len, false); 615 } 616 } 617 618 void OptionValueProperties::Apropos( 619 llvm::StringRef keyword, 620 std::vector<const Property *> &matching_properties) const { 621 const size_t num_properties = m_properties.size(); 622 StreamString strm; 623 for (size_t i = 0; i < num_properties; ++i) { 624 const Property *property = ProtectedGetPropertyAtIndex(i); 625 if (property) { 626 const OptionValueProperties *properties = 627 property->GetValue()->GetAsProperties(); 628 if (properties) { 629 properties->Apropos(keyword, matching_properties); 630 } else { 631 bool match = false; 632 llvm::StringRef name = property->GetName(); 633 if (name.contains_insensitive(keyword)) 634 match = true; 635 else { 636 llvm::StringRef desc = property->GetDescription(); 637 if (desc.contains_insensitive(keyword)) 638 match = true; 639 } 640 if (match) { 641 matching_properties.push_back(property); 642 } 643 } 644 } 645 } 646 } 647 648 lldb::OptionValuePropertiesSP 649 OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx, 650 ConstString name) { 651 lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false)); 652 if (option_value_sp) { 653 OptionValueProperties *ov_properties = option_value_sp->GetAsProperties(); 654 if (ov_properties) 655 return ov_properties->shared_from_this(); 656 } 657 return lldb::OptionValuePropertiesSP(); 658 } 659