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