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