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