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