1 //===-- Variable.cpp --------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Symbol/Variable.h" 11 12 #include "lldb/Core/Module.h" 13 #include "lldb/Core/RegularExpression.h" 14 #include "lldb/Core/Stream.h" 15 #include "lldb/Core/ValueObject.h" 16 #include "lldb/Core/ValueObjectVariable.h" 17 #include "lldb/Symbol/Block.h" 18 #include "lldb/Symbol/CompileUnit.h" 19 #include "lldb/Symbol/CompilerDecl.h" 20 #include "lldb/Symbol/CompilerDeclContext.h" 21 #include "lldb/Symbol/Function.h" 22 #include "lldb/Symbol/SymbolContext.h" 23 #include "lldb/Symbol/SymbolFile.h" 24 #include "lldb/Symbol/Type.h" 25 #include "lldb/Symbol/TypeSystem.h" 26 #include "lldb/Symbol/VariableList.h" 27 #include "lldb/Target/ABI.h" 28 #include "lldb/Target/Process.h" 29 #include "lldb/Target/RegisterContext.h" 30 #include "lldb/Target/StackFrame.h" 31 #include "lldb/Target/Target.h" 32 #include "lldb/Target/Thread.h" 33 34 using namespace lldb; 35 using namespace lldb_private; 36 37 //---------------------------------------------------------------------- 38 // Variable constructor 39 //---------------------------------------------------------------------- 40 Variable::Variable( 41 lldb::user_id_t uid, const char *name, 42 const char *mangled, // The mangled or fully qualified name of the variable. 43 const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope, 44 SymbolContextScope *context, const RangeList &scope_range, 45 Declaration *decl_ptr, const DWARFExpression &location, bool external, 46 bool artificial, bool static_member) 47 : UserID(uid), m_name(name), m_mangled(ConstString(mangled)), 48 m_symfile_type_sp(symfile_type_sp), m_scope(scope), 49 m_owner_scope(context), m_scope_range(scope_range), 50 m_declaration(decl_ptr), m_location(location), m_external(external), 51 m_artificial(artificial), m_static_member(static_member) {} 52 53 //---------------------------------------------------------------------- 54 // Destructor 55 //---------------------------------------------------------------------- 56 Variable::~Variable() {} 57 58 lldb::LanguageType Variable::GetLanguage() const { 59 SymbolContext variable_sc; 60 m_owner_scope->CalculateSymbolContext(&variable_sc); 61 if (variable_sc.comp_unit) 62 return variable_sc.comp_unit->GetLanguage(); 63 return lldb::eLanguageTypeUnknown; 64 } 65 66 ConstString Variable::GetName() const { 67 ConstString name = m_mangled.GetName(GetLanguage()); 68 if (name) 69 return name; 70 return m_name; 71 } 72 73 ConstString Variable::GetUnqualifiedName() const { return m_name; } 74 75 bool Variable::NameMatches(const ConstString &name) const { 76 if (m_name == name) 77 return true; 78 SymbolContext variable_sc; 79 m_owner_scope->CalculateSymbolContext(&variable_sc); 80 81 LanguageType language = eLanguageTypeUnknown; 82 if (variable_sc.comp_unit) 83 language = variable_sc.comp_unit->GetLanguage(); 84 return m_mangled.NameMatches(name, language); 85 } 86 bool Variable::NameMatches(const RegularExpression ®ex) const { 87 if (regex.Execute(m_name.AsCString())) 88 return true; 89 if (m_mangled) 90 return m_mangled.NameMatches(regex, GetLanguage()); 91 return false; 92 } 93 94 Type *Variable::GetType() { 95 if (m_symfile_type_sp) 96 return m_symfile_type_sp->GetType(); 97 return nullptr; 98 } 99 100 void Variable::Dump(Stream *s, bool show_context) const { 101 s->Printf("%p: ", static_cast<const void *>(this)); 102 s->Indent(); 103 *s << "Variable" << (const UserID &)*this; 104 105 if (m_name) 106 *s << ", name = \"" << m_name << "\""; 107 108 if (m_symfile_type_sp) { 109 Type *type = m_symfile_type_sp->GetType(); 110 if (type) { 111 *s << ", type = {" << type->GetID() << "} " << (void *)type << " ("; 112 type->DumpTypeName(s); 113 s->PutChar(')'); 114 } 115 } 116 117 if (m_scope != eValueTypeInvalid) { 118 s->PutCString(", scope = "); 119 switch (m_scope) { 120 case eValueTypeVariableGlobal: 121 s->PutCString(m_external ? "global" : "static"); 122 break; 123 case eValueTypeVariableArgument: 124 s->PutCString("parameter"); 125 break; 126 case eValueTypeVariableLocal: 127 s->PutCString("local"); 128 break; 129 case eValueTypeVariableThreadLocal: 130 s->PutCString("thread local"); 131 break; 132 default: 133 *s << "??? (" << m_scope << ')'; 134 } 135 } 136 137 if (show_context && m_owner_scope != nullptr) { 138 s->PutCString(", context = ( "); 139 m_owner_scope->DumpSymbolContext(s); 140 s->PutCString(" )"); 141 } 142 143 bool show_fullpaths = false; 144 m_declaration.Dump(s, show_fullpaths); 145 146 if (m_location.IsValid()) { 147 s->PutCString(", location = "); 148 lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; 149 if (m_location.IsLocationList()) { 150 SymbolContext variable_sc; 151 m_owner_scope->CalculateSymbolContext(&variable_sc); 152 if (variable_sc.function) 153 loclist_base_addr = variable_sc.function->GetAddressRange() 154 .GetBaseAddress() 155 .GetFileAddress(); 156 } 157 ABI *abi = nullptr; 158 if (m_owner_scope) { 159 ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); 160 if (module_sp) 161 abi = ABI::FindPlugin(module_sp->GetArchitecture()).get(); 162 } 163 m_location.GetDescription(s, lldb::eDescriptionLevelBrief, 164 loclist_base_addr, abi); 165 } 166 167 if (m_external) 168 s->PutCString(", external"); 169 170 if (m_artificial) 171 s->PutCString(", artificial"); 172 173 s->EOL(); 174 } 175 176 bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths, 177 bool show_module) { 178 bool dumped_declaration_info = false; 179 if (m_owner_scope) { 180 SymbolContext sc; 181 m_owner_scope->CalculateSymbolContext(&sc); 182 sc.block = nullptr; 183 sc.line_entry.Clear(); 184 bool show_inlined_frames = false; 185 const bool show_function_arguments = true; 186 const bool show_function_name = true; 187 188 dumped_declaration_info = sc.DumpStopContext( 189 s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames, 190 show_function_arguments, show_function_name); 191 192 if (sc.function) 193 s->PutChar(':'); 194 } 195 if (m_declaration.DumpStopContext(s, false)) 196 dumped_declaration_info = true; 197 return dumped_declaration_info; 198 } 199 200 size_t Variable::MemorySize() const { return sizeof(Variable); } 201 202 CompilerDeclContext Variable::GetDeclContext() { 203 Type *type = GetType(); 204 if (type) 205 return type->GetSymbolFile()->GetDeclContextContainingUID(GetID()); 206 return CompilerDeclContext(); 207 } 208 209 CompilerDecl Variable::GetDecl() { 210 Type *type = GetType(); 211 return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl(); 212 } 213 214 void Variable::CalculateSymbolContext(SymbolContext *sc) { 215 if (m_owner_scope) { 216 m_owner_scope->CalculateSymbolContext(sc); 217 sc->variable = this; 218 } else 219 sc->Clear(false); 220 } 221 222 bool Variable::LocationIsValidForFrame(StackFrame *frame) { 223 // Is the variable is described by a single location? 224 if (!m_location.IsLocationList()) { 225 // Yes it is, the location is valid. 226 return true; 227 } 228 229 if (frame) { 230 Function *function = 231 frame->GetSymbolContext(eSymbolContextFunction).function; 232 if (function) { 233 TargetSP target_sp(frame->CalculateTarget()); 234 235 addr_t loclist_base_load_addr = 236 function->GetAddressRange().GetBaseAddress().GetLoadAddress( 237 target_sp.get()); 238 if (loclist_base_load_addr == LLDB_INVALID_ADDRESS) 239 return false; 240 // It is a location list. We just need to tell if the location 241 // list contains the current address when converted to a load 242 // address 243 return m_location.LocationListContainsAddress( 244 loclist_base_load_addr, 245 frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get())); 246 } 247 } 248 return false; 249 } 250 251 bool Variable::LocationIsValidForAddress(const Address &address) { 252 // Be sure to resolve the address to section offset prior to 253 // calling this function. 254 if (address.IsSectionOffset()) { 255 SymbolContext sc; 256 CalculateSymbolContext(&sc); 257 if (sc.module_sp == address.GetModule()) { 258 // Is the variable is described by a single location? 259 if (!m_location.IsLocationList()) { 260 // Yes it is, the location is valid. 261 return true; 262 } 263 264 if (sc.function) { 265 addr_t loclist_base_file_addr = 266 sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); 267 if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) 268 return false; 269 // It is a location list. We just need to tell if the location 270 // list contains the current address when converted to a load 271 // address 272 return m_location.LocationListContainsAddress(loclist_base_file_addr, 273 address.GetFileAddress()); 274 } 275 } 276 } 277 return false; 278 } 279 280 bool Variable::IsInScope(StackFrame *frame) { 281 switch (m_scope) { 282 case eValueTypeRegister: 283 case eValueTypeRegisterSet: 284 return frame != nullptr; 285 286 case eValueTypeConstResult: 287 case eValueTypeVariableGlobal: 288 case eValueTypeVariableStatic: 289 case eValueTypeVariableThreadLocal: 290 return true; 291 292 case eValueTypeVariableArgument: 293 case eValueTypeVariableLocal: 294 if (frame) { 295 // We don't have a location list, we just need to see if the block 296 // that this variable was defined in is currently 297 Block *deepest_frame_block = 298 frame->GetSymbolContext(eSymbolContextBlock).block; 299 if (deepest_frame_block) { 300 SymbolContext variable_sc; 301 CalculateSymbolContext(&variable_sc); 302 303 // Check for static or global variable defined at the compile unit 304 // level that wasn't defined in a block 305 if (variable_sc.block == nullptr) 306 return true; 307 308 // Check if the variable is valid in the current block 309 if (variable_sc.block != deepest_frame_block && 310 !variable_sc.block->Contains(deepest_frame_block)) 311 return false; 312 313 // If no scope range is specified then it means that the scope is the 314 // same as the 315 // scope of the enclosing lexical block. 316 if (m_scope_range.IsEmpty()) 317 return true; 318 319 addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress(); 320 return m_scope_range.FindEntryThatContains(file_address) != nullptr; 321 } 322 } 323 break; 324 325 default: 326 break; 327 } 328 return false; 329 } 330 331 Error Variable::GetValuesForVariableExpressionPath( 332 const char *variable_expr_path, ExecutionContextScope *scope, 333 GetVariableCallback callback, void *baton, VariableList &variable_list, 334 ValueObjectList &valobj_list) { 335 Error error; 336 if (variable_expr_path && callback) { 337 switch (variable_expr_path[0]) { 338 case '*': { 339 error = Variable::GetValuesForVariableExpressionPath( 340 variable_expr_path + 1, scope, callback, baton, variable_list, 341 valobj_list); 342 if (error.Success()) { 343 for (uint32_t i = 0; i < valobj_list.GetSize();) { 344 Error tmp_error; 345 ValueObjectSP valobj_sp( 346 valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error)); 347 if (tmp_error.Fail()) { 348 variable_list.RemoveVariableAtIndex(i); 349 valobj_list.RemoveValueObjectAtIndex(i); 350 } else { 351 valobj_list.SetValueObjectAtIndex(i, valobj_sp); 352 ++i; 353 } 354 } 355 } else { 356 error.SetErrorString("unknown error"); 357 } 358 return error; 359 } break; 360 361 case '&': { 362 error = Variable::GetValuesForVariableExpressionPath( 363 variable_expr_path + 1, scope, callback, baton, variable_list, 364 valobj_list); 365 if (error.Success()) { 366 for (uint32_t i = 0; i < valobj_list.GetSize();) { 367 Error tmp_error; 368 ValueObjectSP valobj_sp( 369 valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error)); 370 if (tmp_error.Fail()) { 371 variable_list.RemoveVariableAtIndex(i); 372 valobj_list.RemoveValueObjectAtIndex(i); 373 } else { 374 valobj_list.SetValueObjectAtIndex(i, valobj_sp); 375 ++i; 376 } 377 } 378 } else { 379 error.SetErrorString("unknown error"); 380 } 381 return error; 382 } break; 383 384 default: { 385 static RegularExpression g_regex( 386 llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)")); 387 RegularExpression::Match regex_match(1); 388 if (g_regex.Execute(llvm::StringRef::withNullAsEmpty(variable_expr_path), 389 ®ex_match)) { 390 std::string variable_name; 391 if (regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) { 392 variable_list.Clear(); 393 if (callback(baton, variable_name.c_str(), variable_list)) { 394 uint32_t i = 0; 395 while (i < variable_list.GetSize()) { 396 VariableSP var_sp(variable_list.GetVariableAtIndex(i)); 397 ValueObjectSP valobj_sp; 398 if (var_sp) { 399 ValueObjectSP variable_valobj_sp( 400 ValueObjectVariable::Create(scope, var_sp)); 401 if (variable_valobj_sp) { 402 const char *variable_sub_expr_path = 403 variable_expr_path + variable_name.size(); 404 if (*variable_sub_expr_path) { 405 const char *first_unparsed = nullptr; 406 ValueObject::ExpressionPathScanEndReason reason_to_stop; 407 ValueObject::ExpressionPathEndResultType final_value_type; 408 ValueObject::GetValueForExpressionPathOptions options; 409 ValueObject::ExpressionPathAftermath final_task_on_target; 410 411 valobj_sp = variable_valobj_sp->GetValueForExpressionPath( 412 variable_sub_expr_path, &first_unparsed, 413 &reason_to_stop, &final_value_type, options, 414 &final_task_on_target); 415 if (!valobj_sp) { 416 error.SetErrorStringWithFormat( 417 "invalid expression path '%s' for variable '%s'", 418 variable_sub_expr_path, 419 var_sp->GetName().GetCString()); 420 } 421 } else { 422 // Just the name of a variable with no extras 423 valobj_sp = variable_valobj_sp; 424 } 425 } 426 } 427 428 if (!var_sp || !valobj_sp) { 429 variable_list.RemoveVariableAtIndex(i); 430 } else { 431 valobj_list.Append(valobj_sp); 432 ++i; 433 } 434 } 435 436 if (variable_list.GetSize() > 0) { 437 error.Clear(); 438 return error; 439 } 440 } 441 } 442 } 443 error.SetErrorStringWithFormat( 444 "unable to extract a variable name from '%s'", variable_expr_path); 445 } break; 446 } 447 } 448 error.SetErrorString("unknown error"); 449 return error; 450 } 451 452 bool Variable::DumpLocationForAddress(Stream *s, const Address &address) { 453 // Be sure to resolve the address to section offset prior to 454 // calling this function. 455 if (address.IsSectionOffset()) { 456 SymbolContext sc; 457 CalculateSymbolContext(&sc); 458 if (sc.module_sp == address.GetModule()) { 459 ABI *abi = nullptr; 460 if (m_owner_scope) { 461 ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); 462 if (module_sp) 463 abi = ABI::FindPlugin(module_sp->GetArchitecture()).get(); 464 } 465 466 const addr_t file_addr = address.GetFileAddress(); 467 if (sc.function) { 468 if (sc.function->GetAddressRange().ContainsFileAddress(address)) { 469 addr_t loclist_base_file_addr = 470 sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); 471 if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) 472 return false; 473 return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief, 474 loclist_base_file_addr, 475 file_addr, abi); 476 } 477 } 478 return m_location.DumpLocationForAddress( 479 s, eDescriptionLevelBrief, LLDB_INVALID_ADDRESS, file_addr, abi); 480 } 481 } 482 return false; 483 } 484 485 static void PrivateAutoComplete( 486 StackFrame *frame, const std::string &partial_path, 487 const std::string 488 &prefix_path, // Anything that has been resolved already will be in here 489 const CompilerType &compiler_type, 490 StringList &matches, bool &word_complete); 491 492 static void PrivateAutoCompleteMembers( 493 StackFrame *frame, const std::string &partial_member_name, 494 const std::string &partial_path, 495 const std::string 496 &prefix_path, // Anything that has been resolved already will be in here 497 const CompilerType &compiler_type, 498 StringList &matches, bool &word_complete); 499 500 static void PrivateAutoCompleteMembers( 501 StackFrame *frame, const std::string &partial_member_name, 502 const std::string &partial_path, 503 const std::string 504 &prefix_path, // Anything that has been resolved already will be in here 505 const CompilerType &compiler_type, 506 StringList &matches, bool &word_complete) { 507 508 // We are in a type parsing child members 509 const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses(); 510 511 if (num_bases > 0) { 512 for (uint32_t i = 0; i < num_bases; ++i) { 513 CompilerType base_class_type = 514 compiler_type.GetDirectBaseClassAtIndex(i, nullptr); 515 516 PrivateAutoCompleteMembers( 517 frame, partial_member_name, partial_path, prefix_path, 518 base_class_type.GetCanonicalType(), matches, word_complete); 519 } 520 } 521 522 const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses(); 523 524 if (num_vbases > 0) { 525 for (uint32_t i = 0; i < num_vbases; ++i) { 526 CompilerType vbase_class_type = 527 compiler_type.GetVirtualBaseClassAtIndex(i, nullptr); 528 529 PrivateAutoCompleteMembers( 530 frame, partial_member_name, partial_path, prefix_path, 531 vbase_class_type.GetCanonicalType(), matches, word_complete); 532 } 533 } 534 535 // We are in a type parsing child members 536 const uint32_t num_fields = compiler_type.GetNumFields(); 537 538 if (num_fields > 0) { 539 for (uint32_t i = 0; i < num_fields; ++i) { 540 std::string member_name; 541 542 CompilerType member_compiler_type = compiler_type.GetFieldAtIndex( 543 i, member_name, nullptr, nullptr, nullptr); 544 545 if (partial_member_name.empty() || 546 member_name.find(partial_member_name) == 0) { 547 if (member_name == partial_member_name) { 548 PrivateAutoComplete( 549 frame, partial_path, 550 prefix_path + member_name, // Anything that has been resolved 551 // already will be in here 552 member_compiler_type.GetCanonicalType(), matches, word_complete); 553 } else { 554 matches.AppendString(prefix_path + member_name); 555 } 556 } 557 } 558 } 559 } 560 561 static void PrivateAutoComplete( 562 StackFrame *frame, const std::string &partial_path, 563 const std::string 564 &prefix_path, // Anything that has been resolved already will be in here 565 const CompilerType &compiler_type, 566 StringList &matches, bool &word_complete) { 567 // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = 568 // '%s'\n", prefix_path.c_str(), partial_path.c_str()); 569 std::string remaining_partial_path; 570 571 const lldb::TypeClass type_class = compiler_type.GetTypeClass(); 572 if (partial_path.empty()) { 573 if (compiler_type.IsValid()) { 574 switch (type_class) { 575 default: 576 case eTypeClassArray: 577 case eTypeClassBlockPointer: 578 case eTypeClassBuiltin: 579 case eTypeClassComplexFloat: 580 case eTypeClassComplexInteger: 581 case eTypeClassEnumeration: 582 case eTypeClassFunction: 583 case eTypeClassMemberPointer: 584 case eTypeClassReference: 585 case eTypeClassTypedef: 586 case eTypeClassVector: { 587 matches.AppendString(prefix_path); 588 word_complete = matches.GetSize() == 1; 589 } break; 590 591 case eTypeClassClass: 592 case eTypeClassStruct: 593 case eTypeClassUnion: 594 if (prefix_path.back() != '.') 595 matches.AppendString(prefix_path + '.'); 596 break; 597 598 case eTypeClassObjCObject: 599 case eTypeClassObjCInterface: 600 break; 601 case eTypeClassObjCObjectPointer: 602 case eTypeClassPointer: { 603 bool omit_empty_base_classes = true; 604 if (compiler_type.GetNumChildren(omit_empty_base_classes) > 0) 605 matches.AppendString(prefix_path + "->"); 606 else { 607 matches.AppendString(prefix_path); 608 word_complete = true; 609 } 610 } break; 611 } 612 } else { 613 if (frame) { 614 const bool get_file_globals = true; 615 616 VariableList *variable_list = frame->GetVariableList(get_file_globals); 617 618 if (variable_list) { 619 const size_t num_variables = variable_list->GetSize(); 620 for (size_t i = 0; i < num_variables; ++i) { 621 Variable *variable = variable_list->GetVariableAtIndex(i).get(); 622 matches.AppendString(variable->GetName().AsCString()); 623 } 624 } 625 } 626 } 627 } else { 628 const char ch = partial_path[0]; 629 switch (ch) { 630 case '*': 631 if (prefix_path.empty()) { 632 PrivateAutoComplete(frame, partial_path.substr(1), std::string("*"), 633 compiler_type, matches, word_complete); 634 } 635 break; 636 637 case '&': 638 if (prefix_path.empty()) { 639 PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"), 640 compiler_type, matches, word_complete); 641 } 642 break; 643 644 case '-': 645 if (partial_path[1] == '>' && !prefix_path.empty()) { 646 switch (type_class) { 647 case lldb::eTypeClassPointer: { 648 CompilerType pointee_type(compiler_type.GetPointeeType()); 649 if (partial_path[2]) { 650 // If there is more after the "->", then search deeper 651 PrivateAutoComplete( 652 frame, partial_path.substr(2), prefix_path + "->", 653 pointee_type.GetCanonicalType(), matches, word_complete); 654 } else { 655 // Nothing after the "->", so list all members 656 PrivateAutoCompleteMembers( 657 frame, std::string(), std::string(), prefix_path + "->", 658 pointee_type.GetCanonicalType(), matches, word_complete); 659 } 660 } break; 661 default: 662 break; 663 } 664 } 665 break; 666 667 case '.': 668 if (compiler_type.IsValid()) { 669 switch (type_class) { 670 case lldb::eTypeClassUnion: 671 case lldb::eTypeClassStruct: 672 case lldb::eTypeClassClass: 673 if (partial_path[1]) { 674 // If there is more after the ".", then search deeper 675 PrivateAutoComplete(frame, partial_path.substr(1), 676 prefix_path + ".", compiler_type, matches, 677 word_complete); 678 679 } else { 680 // Nothing after the ".", so list all members 681 PrivateAutoCompleteMembers(frame, std::string(), partial_path, 682 prefix_path + ".", compiler_type, 683 matches, word_complete); 684 } 685 break; 686 default: 687 break; 688 } 689 } 690 break; 691 default: 692 if (isalpha(ch) || ch == '_' || ch == '$') { 693 const size_t partial_path_len = partial_path.size(); 694 size_t pos = 1; 695 while (pos < partial_path_len) { 696 const char curr_ch = partial_path[pos]; 697 if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') { 698 ++pos; 699 continue; 700 } 701 break; 702 } 703 704 std::string token(partial_path, 0, pos); 705 remaining_partial_path = partial_path.substr(pos); 706 707 if (compiler_type.IsValid()) { 708 PrivateAutoCompleteMembers(frame, token, remaining_partial_path, 709 prefix_path, compiler_type, matches, 710 word_complete); 711 } else if (frame) { 712 // We haven't found our variable yet 713 const bool get_file_globals = true; 714 715 VariableList *variable_list = 716 frame->GetVariableList(get_file_globals); 717 718 if (!variable_list) 719 break; 720 721 const size_t num_variables = variable_list->GetSize(); 722 for (size_t i = 0; i < num_variables; ++i) { 723 Variable *variable = variable_list->GetVariableAtIndex(i).get(); 724 725 if (!variable) 726 continue; 727 728 const char *variable_name = variable->GetName().AsCString(); 729 if (strstr(variable_name, token.c_str()) == variable_name) { 730 if (strcmp(variable_name, token.c_str()) == 0) { 731 Type *variable_type = variable->GetType(); 732 if (variable_type) { 733 CompilerType variable_compiler_type( 734 variable_type->GetForwardCompilerType()); 735 PrivateAutoComplete( 736 frame, remaining_partial_path, 737 prefix_path + token, // Anything that has been resolved 738 // already will be in here 739 variable_compiler_type.GetCanonicalType(), matches, 740 word_complete); 741 } else { 742 matches.AppendString(prefix_path + variable_name); 743 } 744 } else if (remaining_partial_path.empty()) { 745 matches.AppendString(prefix_path + variable_name); 746 } 747 } 748 } 749 } 750 } 751 break; 752 } 753 } 754 } 755 756 size_t Variable::AutoComplete(const ExecutionContext &exe_ctx, 757 const char *partial_path_cstr, 758 StringList &matches, bool &word_complete) { 759 word_complete = false; 760 std::string partial_path; 761 std::string prefix_path; 762 CompilerType compiler_type; 763 if (partial_path_cstr && partial_path_cstr[0]) 764 partial_path = partial_path_cstr; 765 766 PrivateAutoComplete(exe_ctx.GetFramePtr(), partial_path, prefix_path, 767 compiler_type, matches, word_complete); 768 769 return matches.GetSize(); 770 } 771