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