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