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("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"); 386 RegularExpression::Match regex_match(1); 387 if (g_regex.Execute(variable_expr_path, ®ex_match)) { 388 std::string variable_name; 389 if (regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) { 390 variable_list.Clear(); 391 if (callback(baton, variable_name.c_str(), variable_list)) { 392 uint32_t i = 0; 393 while (i < variable_list.GetSize()) { 394 VariableSP var_sp(variable_list.GetVariableAtIndex(i)); 395 ValueObjectSP valobj_sp; 396 if (var_sp) { 397 ValueObjectSP variable_valobj_sp( 398 ValueObjectVariable::Create(scope, var_sp)); 399 if (variable_valobj_sp) { 400 const char *variable_sub_expr_path = 401 variable_expr_path + variable_name.size(); 402 if (*variable_sub_expr_path) { 403 const char *first_unparsed = nullptr; 404 ValueObject::ExpressionPathScanEndReason reason_to_stop; 405 ValueObject::ExpressionPathEndResultType final_value_type; 406 ValueObject::GetValueForExpressionPathOptions options; 407 ValueObject::ExpressionPathAftermath final_task_on_target; 408 409 valobj_sp = variable_valobj_sp->GetValueForExpressionPath( 410 variable_sub_expr_path, &first_unparsed, 411 &reason_to_stop, &final_value_type, options, 412 &final_task_on_target); 413 if (!valobj_sp) { 414 error.SetErrorStringWithFormat( 415 "invalid expression path '%s' for variable '%s'", 416 variable_sub_expr_path, 417 var_sp->GetName().GetCString()); 418 } 419 } else { 420 // Just the name of a variable with no extras 421 valobj_sp = variable_valobj_sp; 422 } 423 } 424 } 425 426 if (!var_sp || !valobj_sp) { 427 variable_list.RemoveVariableAtIndex(i); 428 } else { 429 valobj_list.Append(valobj_sp); 430 ++i; 431 } 432 } 433 434 if (variable_list.GetSize() > 0) { 435 error.Clear(); 436 return error; 437 } 438 } 439 } 440 } 441 error.SetErrorStringWithFormat( 442 "unable to extract a variable name from '%s'", variable_expr_path); 443 } break; 444 } 445 } 446 error.SetErrorString("unknown error"); 447 return error; 448 } 449 450 bool Variable::DumpLocationForAddress(Stream *s, const Address &address) { 451 // Be sure to resolve the address to section offset prior to 452 // calling this function. 453 if (address.IsSectionOffset()) { 454 SymbolContext sc; 455 CalculateSymbolContext(&sc); 456 if (sc.module_sp == address.GetModule()) { 457 ABI *abi = nullptr; 458 if (m_owner_scope) { 459 ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); 460 if (module_sp) 461 abi = ABI::FindPlugin(module_sp->GetArchitecture()).get(); 462 } 463 464 const addr_t file_addr = address.GetFileAddress(); 465 if (sc.function) { 466 if (sc.function->GetAddressRange().ContainsFileAddress(address)) { 467 addr_t loclist_base_file_addr = 468 sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); 469 if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) 470 return false; 471 return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief, 472 loclist_base_file_addr, 473 file_addr, abi); 474 } 475 } 476 return m_location.DumpLocationForAddress( 477 s, eDescriptionLevelBrief, LLDB_INVALID_ADDRESS, file_addr, abi); 478 } 479 } 480 return false; 481 } 482 483 static void PrivateAutoComplete( 484 StackFrame *frame, const std::string &partial_path, 485 const std::string 486 &prefix_path, // Anything that has been resolved already will be in here 487 const CompilerType &compiler_type, 488 StringList &matches, bool &word_complete); 489 490 static void PrivateAutoCompleteMembers( 491 StackFrame *frame, const std::string &partial_member_name, 492 const std::string &partial_path, 493 const std::string 494 &prefix_path, // Anything that has been resolved already will be in here 495 const CompilerType &compiler_type, 496 StringList &matches, bool &word_complete); 497 498 static void PrivateAutoCompleteMembers( 499 StackFrame *frame, const std::string &partial_member_name, 500 const std::string &partial_path, 501 const std::string 502 &prefix_path, // Anything that has been resolved already will be in here 503 const CompilerType &compiler_type, 504 StringList &matches, bool &word_complete) { 505 506 // We are in a type parsing child members 507 const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses(); 508 509 if (num_bases > 0) { 510 for (uint32_t i = 0; i < num_bases; ++i) { 511 CompilerType base_class_type = 512 compiler_type.GetDirectBaseClassAtIndex(i, nullptr); 513 514 PrivateAutoCompleteMembers( 515 frame, partial_member_name, partial_path, prefix_path, 516 base_class_type.GetCanonicalType(), matches, word_complete); 517 } 518 } 519 520 const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses(); 521 522 if (num_vbases > 0) { 523 for (uint32_t i = 0; i < num_vbases; ++i) { 524 CompilerType vbase_class_type = 525 compiler_type.GetVirtualBaseClassAtIndex(i, nullptr); 526 527 PrivateAutoCompleteMembers( 528 frame, partial_member_name, partial_path, prefix_path, 529 vbase_class_type.GetCanonicalType(), matches, word_complete); 530 } 531 } 532 533 // We are in a type parsing child members 534 const uint32_t num_fields = compiler_type.GetNumFields(); 535 536 if (num_fields > 0) { 537 for (uint32_t i = 0; i < num_fields; ++i) { 538 std::string member_name; 539 540 CompilerType member_compiler_type = compiler_type.GetFieldAtIndex( 541 i, member_name, nullptr, nullptr, nullptr); 542 543 if (partial_member_name.empty() || 544 member_name.find(partial_member_name) == 0) { 545 if (member_name == partial_member_name) { 546 PrivateAutoComplete( 547 frame, partial_path, 548 prefix_path + member_name, // Anything that has been resolved 549 // already will be in here 550 member_compiler_type.GetCanonicalType(), matches, word_complete); 551 } else { 552 matches.AppendString(prefix_path + member_name); 553 } 554 } 555 } 556 } 557 } 558 559 static void PrivateAutoComplete( 560 StackFrame *frame, const std::string &partial_path, 561 const std::string 562 &prefix_path, // Anything that has been resolved already will be in here 563 const CompilerType &compiler_type, 564 StringList &matches, bool &word_complete) { 565 // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = 566 // '%s'\n", prefix_path.c_str(), partial_path.c_str()); 567 std::string remaining_partial_path; 568 569 const lldb::TypeClass type_class = compiler_type.GetTypeClass(); 570 if (partial_path.empty()) { 571 if (compiler_type.IsValid()) { 572 switch (type_class) { 573 default: 574 case eTypeClassArray: 575 case eTypeClassBlockPointer: 576 case eTypeClassBuiltin: 577 case eTypeClassComplexFloat: 578 case eTypeClassComplexInteger: 579 case eTypeClassEnumeration: 580 case eTypeClassFunction: 581 case eTypeClassMemberPointer: 582 case eTypeClassReference: 583 case eTypeClassTypedef: 584 case eTypeClassVector: { 585 matches.AppendString(prefix_path); 586 word_complete = matches.GetSize() == 1; 587 } break; 588 589 case eTypeClassClass: 590 case eTypeClassStruct: 591 case eTypeClassUnion: 592 if (prefix_path.back() != '.') 593 matches.AppendString(prefix_path + '.'); 594 break; 595 596 case eTypeClassObjCObject: 597 case eTypeClassObjCInterface: 598 break; 599 case eTypeClassObjCObjectPointer: 600 case eTypeClassPointer: { 601 bool omit_empty_base_classes = true; 602 if (compiler_type.GetNumChildren(omit_empty_base_classes) > 0) 603 matches.AppendString(prefix_path + "->"); 604 else { 605 matches.AppendString(prefix_path); 606 word_complete = true; 607 } 608 } break; 609 } 610 } else { 611 if (frame) { 612 const bool get_file_globals = true; 613 614 VariableList *variable_list = frame->GetVariableList(get_file_globals); 615 616 if (variable_list) { 617 const size_t num_variables = variable_list->GetSize(); 618 for (size_t i = 0; i < num_variables; ++i) { 619 Variable *variable = variable_list->GetVariableAtIndex(i).get(); 620 matches.AppendString(variable->GetName().AsCString()); 621 } 622 } 623 } 624 } 625 } else { 626 const char ch = partial_path[0]; 627 switch (ch) { 628 case '*': 629 if (prefix_path.empty()) { 630 PrivateAutoComplete(frame, partial_path.substr(1), std::string("*"), 631 compiler_type, matches, word_complete); 632 } 633 break; 634 635 case '&': 636 if (prefix_path.empty()) { 637 PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"), 638 compiler_type, matches, word_complete); 639 } 640 break; 641 642 case '-': 643 if (partial_path[1] == '>' && !prefix_path.empty()) { 644 switch (type_class) { 645 case lldb::eTypeClassPointer: { 646 CompilerType pointee_type(compiler_type.GetPointeeType()); 647 if (partial_path[2]) { 648 // If there is more after the "->", then search deeper 649 PrivateAutoComplete( 650 frame, partial_path.substr(2), prefix_path + "->", 651 pointee_type.GetCanonicalType(), matches, word_complete); 652 } else { 653 // Nothing after the "->", so list all members 654 PrivateAutoCompleteMembers( 655 frame, std::string(), std::string(), prefix_path + "->", 656 pointee_type.GetCanonicalType(), matches, word_complete); 657 } 658 } break; 659 default: 660 break; 661 } 662 } 663 break; 664 665 case '.': 666 if (compiler_type.IsValid()) { 667 switch (type_class) { 668 case lldb::eTypeClassUnion: 669 case lldb::eTypeClassStruct: 670 case lldb::eTypeClassClass: 671 if (partial_path[1]) { 672 // If there is more after the ".", then search deeper 673 PrivateAutoComplete(frame, partial_path.substr(1), 674 prefix_path + ".", compiler_type, matches, 675 word_complete); 676 677 } else { 678 // Nothing after the ".", so list all members 679 PrivateAutoCompleteMembers(frame, std::string(), partial_path, 680 prefix_path + ".", compiler_type, 681 matches, word_complete); 682 } 683 break; 684 default: 685 break; 686 } 687 } 688 break; 689 default: 690 if (isalpha(ch) || ch == '_' || ch == '$') { 691 const size_t partial_path_len = partial_path.size(); 692 size_t pos = 1; 693 while (pos < partial_path_len) { 694 const char curr_ch = partial_path[pos]; 695 if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') { 696 ++pos; 697 continue; 698 } 699 break; 700 } 701 702 std::string token(partial_path, 0, pos); 703 remaining_partial_path = partial_path.substr(pos); 704 705 if (compiler_type.IsValid()) { 706 PrivateAutoCompleteMembers(frame, token, remaining_partial_path, 707 prefix_path, compiler_type, matches, 708 word_complete); 709 } else if (frame) { 710 // We haven't found our variable yet 711 const bool get_file_globals = true; 712 713 VariableList *variable_list = 714 frame->GetVariableList(get_file_globals); 715 716 if (!variable_list) 717 break; 718 719 const size_t num_variables = variable_list->GetSize(); 720 for (size_t i = 0; i < num_variables; ++i) { 721 Variable *variable = variable_list->GetVariableAtIndex(i).get(); 722 723 if (!variable) 724 continue; 725 726 const char *variable_name = variable->GetName().AsCString(); 727 if (strstr(variable_name, token.c_str()) == variable_name) { 728 if (strcmp(variable_name, token.c_str()) == 0) { 729 Type *variable_type = variable->GetType(); 730 if (variable_type) { 731 CompilerType variable_compiler_type( 732 variable_type->GetForwardCompilerType()); 733 PrivateAutoComplete( 734 frame, remaining_partial_path, 735 prefix_path + token, // Anything that has been resolved 736 // already will be in here 737 variable_compiler_type.GetCanonicalType(), matches, 738 word_complete); 739 } else { 740 matches.AppendString(prefix_path + variable_name); 741 } 742 } else if (remaining_partial_path.empty()) { 743 matches.AppendString(prefix_path + variable_name); 744 } 745 } 746 } 747 } 748 } 749 break; 750 } 751 } 752 } 753 754 size_t Variable::AutoComplete(const ExecutionContext &exe_ctx, 755 const char *partial_path_cstr, 756 StringList &matches, bool &word_complete) { 757 word_complete = false; 758 std::string partial_path; 759 std::string prefix_path; 760 CompilerType compiler_type; 761 if (partial_path_cstr && partial_path_cstr[0]) 762 partial_path = partial_path_cstr; 763 764 PrivateAutoComplete(exe_ctx.GetFramePtr(), partial_path, prefix_path, 765 compiler_type, matches, word_complete); 766 767 return matches.GetSize(); 768 } 769