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