Variable.cpp revision 341825
1254721Semaste//===-- Variable.cpp --------------------------------------------*- C++ -*-===//
2254721Semaste//
3254721Semaste//                     The LLVM Compiler Infrastructure
4254721Semaste//
5254721Semaste// This file is distributed under the University of Illinois Open Source
6254721Semaste// License. See LICENSE.TXT for details.
7254721Semaste//
8254721Semaste//===----------------------------------------------------------------------===//
9254721Semaste
10254721Semaste#include "lldb/Symbol/Variable.h"
11254721Semaste
12254721Semaste#include "lldb/Core/Module.h"
13254721Semaste#include "lldb/Core/ValueObject.h"
14254721Semaste#include "lldb/Core/ValueObjectVariable.h"
15254721Semaste#include "lldb/Symbol/Block.h"
16314564Sdim#include "lldb/Symbol/CompileUnit.h"
17296417Sdim#include "lldb/Symbol/CompilerDecl.h"
18296417Sdim#include "lldb/Symbol/CompilerDeclContext.h"
19254721Semaste#include "lldb/Symbol/Function.h"
20254721Semaste#include "lldb/Symbol/SymbolContext.h"
21296417Sdim#include "lldb/Symbol/SymbolFile.h"
22254721Semaste#include "lldb/Symbol/Type.h"
23296417Sdim#include "lldb/Symbol/TypeSystem.h"
24254721Semaste#include "lldb/Symbol/VariableList.h"
25254721Semaste#include "lldb/Target/ABI.h"
26254721Semaste#include "lldb/Target/Process.h"
27254721Semaste#include "lldb/Target/RegisterContext.h"
28254721Semaste#include "lldb/Target/StackFrame.h"
29314564Sdim#include "lldb/Target/Target.h"
30254721Semaste#include "lldb/Target/Thread.h"
31321369Sdim#include "lldb/Utility/RegularExpression.h"
32321369Sdim#include "lldb/Utility/Stream.h"
33254721Semaste
34314564Sdim#include "llvm/ADT/Twine.h"
35314564Sdim
36254721Semasteusing namespace lldb;
37254721Semasteusing namespace lldb_private;
38254721Semaste
39254721Semaste//----------------------------------------------------------------------
40254721Semaste// Variable constructor
41254721Semaste//----------------------------------------------------------------------
42314564SdimVariable::Variable(
43314564Sdim    lldb::user_id_t uid, const char *name,
44314564Sdim    const char *mangled, // The mangled or fully qualified name of the variable.
45314564Sdim    const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope,
46314564Sdim    SymbolContextScope *context, const RangeList &scope_range,
47314564Sdim    Declaration *decl_ptr, const DWARFExpression &location, bool external,
48314564Sdim    bool artificial, bool static_member)
49314564Sdim    : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
50314564Sdim      m_symfile_type_sp(symfile_type_sp), m_scope(scope),
51314564Sdim      m_owner_scope(context), m_scope_range(scope_range),
52314564Sdim      m_declaration(decl_ptr), m_location(location), m_external(external),
53314564Sdim      m_artificial(artificial), m_static_member(static_member) {}
54254721Semaste
55254721Semaste//----------------------------------------------------------------------
56254721Semaste// Destructor
57254721Semaste//----------------------------------------------------------------------
58314564SdimVariable::~Variable() {}
59254721Semaste
60314564Sdimlldb::LanguageType Variable::GetLanguage() const {
61314564Sdim  SymbolContext variable_sc;
62314564Sdim  m_owner_scope->CalculateSymbolContext(&variable_sc);
63314564Sdim  if (variable_sc.comp_unit)
64314564Sdim    return variable_sc.comp_unit->GetLanguage();
65314564Sdim  return lldb::eLanguageTypeUnknown;
66288943Sdim}
67254721Semaste
68314564SdimConstString Variable::GetName() const {
69314564Sdim  ConstString name = m_mangled.GetName(GetLanguage());
70314564Sdim  if (name)
71314564Sdim    return name;
72314564Sdim  return m_name;
73254721Semaste}
74254721Semaste
75314564SdimConstString Variable::GetUnqualifiedName() const { return m_name; }
76296417Sdim
77314564Sdimbool Variable::NameMatches(const ConstString &name) const {
78314564Sdim  if (m_name == name)
79314564Sdim    return true;
80314564Sdim  SymbolContext variable_sc;
81314564Sdim  m_owner_scope->CalculateSymbolContext(&variable_sc);
82296417Sdim
83314564Sdim  LanguageType language = eLanguageTypeUnknown;
84314564Sdim  if (variable_sc.comp_unit)
85314564Sdim    language = variable_sc.comp_unit->GetLanguage();
86314564Sdim  return m_mangled.NameMatches(name, language);
87288943Sdim}
88314564Sdimbool Variable::NameMatches(const RegularExpression &regex) const {
89314564Sdim  if (regex.Execute(m_name.AsCString()))
90314564Sdim    return true;
91314564Sdim  if (m_mangled)
92314564Sdim    return m_mangled.NameMatches(regex, GetLanguage());
93314564Sdim  return false;
94254721Semaste}
95254721Semaste
96314564SdimType *Variable::GetType() {
97314564Sdim  if (m_symfile_type_sp)
98314564Sdim    return m_symfile_type_sp->GetType();
99314564Sdim  return nullptr;
100254721Semaste}
101254721Semaste
102314564Sdimvoid Variable::Dump(Stream *s, bool show_context) const {
103314564Sdim  s->Printf("%p: ", static_cast<const void *>(this));
104314564Sdim  s->Indent();
105314564Sdim  *s << "Variable" << (const UserID &)*this;
106254721Semaste
107314564Sdim  if (m_name)
108314564Sdim    *s << ", name = \"" << m_name << "\"";
109254721Semaste
110314564Sdim  if (m_symfile_type_sp) {
111314564Sdim    Type *type = m_symfile_type_sp->GetType();
112314564Sdim    if (type) {
113314564Sdim      *s << ", type = {" << type->GetID() << "} " << (void *)type << " (";
114314564Sdim      type->DumpTypeName(s);
115314564Sdim      s->PutChar(')');
116254721Semaste    }
117314564Sdim  }
118254721Semaste
119314564Sdim  if (m_scope != eValueTypeInvalid) {
120314564Sdim    s->PutCString(", scope = ");
121314564Sdim    switch (m_scope) {
122314564Sdim    case eValueTypeVariableGlobal:
123314564Sdim      s->PutCString(m_external ? "global" : "static");
124314564Sdim      break;
125314564Sdim    case eValueTypeVariableArgument:
126314564Sdim      s->PutCString("parameter");
127314564Sdim      break;
128314564Sdim    case eValueTypeVariableLocal:
129314564Sdim      s->PutCString("local");
130314564Sdim      break;
131314564Sdim    case eValueTypeVariableThreadLocal:
132314564Sdim      s->PutCString("thread local");
133314564Sdim      break;
134314564Sdim    default:
135314564Sdim      *s << "??? (" << m_scope << ')';
136254721Semaste    }
137314564Sdim  }
138254721Semaste
139314564Sdim  if (show_context && m_owner_scope != nullptr) {
140314564Sdim    s->PutCString(", context = ( ");
141314564Sdim    m_owner_scope->DumpSymbolContext(s);
142314564Sdim    s->PutCString(" )");
143314564Sdim  }
144254721Semaste
145314564Sdim  bool show_fullpaths = false;
146314564Sdim  m_declaration.Dump(s, show_fullpaths);
147254721Semaste
148314564Sdim  if (m_location.IsValid()) {
149314564Sdim    s->PutCString(", location = ");
150314564Sdim    lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
151314564Sdim    if (m_location.IsLocationList()) {
152314564Sdim      SymbolContext variable_sc;
153314564Sdim      m_owner_scope->CalculateSymbolContext(&variable_sc);
154314564Sdim      if (variable_sc.function)
155314564Sdim        loclist_base_addr = variable_sc.function->GetAddressRange()
156314564Sdim                                .GetBaseAddress()
157314564Sdim                                .GetFileAddress();
158254721Semaste    }
159314564Sdim    ABI *abi = nullptr;
160314564Sdim    if (m_owner_scope) {
161314564Sdim      ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
162314564Sdim      if (module_sp)
163321369Sdim        abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture()).get();
164314564Sdim    }
165314564Sdim    m_location.GetDescription(s, lldb::eDescriptionLevelBrief,
166314564Sdim                              loclist_base_addr, abi);
167314564Sdim  }
168254721Semaste
169314564Sdim  if (m_external)
170314564Sdim    s->PutCString(", external");
171254721Semaste
172314564Sdim  if (m_artificial)
173314564Sdim    s->PutCString(", artificial");
174254721Semaste
175314564Sdim  s->EOL();
176254721Semaste}
177254721Semaste
178314564Sdimbool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,
179314564Sdim                               bool show_module) {
180314564Sdim  bool dumped_declaration_info = false;
181314564Sdim  if (m_owner_scope) {
182314564Sdim    SymbolContext sc;
183314564Sdim    m_owner_scope->CalculateSymbolContext(&sc);
184314564Sdim    sc.block = nullptr;
185314564Sdim    sc.line_entry.Clear();
186314564Sdim    bool show_inlined_frames = false;
187314564Sdim    const bool show_function_arguments = true;
188314564Sdim    const bool show_function_name = true;
189314564Sdim
190314564Sdim    dumped_declaration_info = sc.DumpStopContext(
191314564Sdim        s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,
192314564Sdim        show_function_arguments, show_function_name);
193314564Sdim
194314564Sdim    if (sc.function)
195314564Sdim      s->PutChar(':');
196314564Sdim  }
197314564Sdim  if (m_declaration.DumpStopContext(s, false))
198314564Sdim    dumped_declaration_info = true;
199314564Sdim  return dumped_declaration_info;
200254721Semaste}
201254721Semaste
202314564Sdimsize_t Variable::MemorySize() const { return sizeof(Variable); }
203314564Sdim
204314564SdimCompilerDeclContext Variable::GetDeclContext() {
205314564Sdim  Type *type = GetType();
206314564Sdim  if (type)
207314564Sdim    return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());
208314564Sdim  return CompilerDeclContext();
209254721Semaste}
210254721Semaste
211314564SdimCompilerDecl Variable::GetDecl() {
212314564Sdim  Type *type = GetType();
213314564Sdim  return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();
214296417Sdim}
215254721Semaste
216314564Sdimvoid Variable::CalculateSymbolContext(SymbolContext *sc) {
217314564Sdim  if (m_owner_scope) {
218314564Sdim    m_owner_scope->CalculateSymbolContext(sc);
219314564Sdim    sc->variable = this;
220314564Sdim  } else
221314564Sdim    sc->Clear(false);
222296417Sdim}
223296417Sdim
224314564Sdimbool Variable::LocationIsValidForFrame(StackFrame *frame) {
225314564Sdim  // Is the variable is described by a single location?
226314564Sdim  if (!m_location.IsLocationList()) {
227314564Sdim    // Yes it is, the location is valid.
228314564Sdim    return true;
229314564Sdim  }
230314564Sdim
231314564Sdim  if (frame) {
232314564Sdim    Function *function =
233314564Sdim        frame->GetSymbolContext(eSymbolContextFunction).function;
234314564Sdim    if (function) {
235314564Sdim      TargetSP target_sp(frame->CalculateTarget());
236314564Sdim
237314564Sdim      addr_t loclist_base_load_addr =
238314564Sdim          function->GetAddressRange().GetBaseAddress().GetLoadAddress(
239314564Sdim              target_sp.get());
240314564Sdim      if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
241314564Sdim        return false;
242341825Sdim      // It is a location list. We just need to tell if the location list
243341825Sdim      // contains the current address when converted to a load address
244314564Sdim      return m_location.LocationListContainsAddress(
245314564Sdim          loclist_base_load_addr,
246314564Sdim          frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()));
247288943Sdim    }
248314564Sdim  }
249314564Sdim  return false;
250254721Semaste}
251254721Semaste
252314564Sdimbool Variable::LocationIsValidForAddress(const Address &address) {
253341825Sdim  // Be sure to resolve the address to section offset prior to calling this
254341825Sdim  // function.
255314564Sdim  if (address.IsSectionOffset()) {
256314564Sdim    SymbolContext sc;
257314564Sdim    CalculateSymbolContext(&sc);
258314564Sdim    if (sc.module_sp == address.GetModule()) {
259314564Sdim      // Is the variable is described by a single location?
260314564Sdim      if (!m_location.IsLocationList()) {
261314564Sdim        // Yes it is, the location is valid.
262254721Semaste        return true;
263314564Sdim      }
264254721Semaste
265314564Sdim      if (sc.function) {
266314564Sdim        addr_t loclist_base_file_addr =
267314564Sdim            sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
268314564Sdim        if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
269314564Sdim          return false;
270341825Sdim        // It is a location list. We just need to tell if the location list
271341825Sdim        // contains the current address when converted to a load address
272314564Sdim        return m_location.LocationListContainsAddress(loclist_base_file_addr,
273314564Sdim                                                      address.GetFileAddress());
274314564Sdim      }
275254721Semaste    }
276314564Sdim  }
277314564Sdim  return false;
278254721Semaste}
279254721Semaste
280314564Sdimbool Variable::IsInScope(StackFrame *frame) {
281314564Sdim  switch (m_scope) {
282314564Sdim  case eValueTypeRegister:
283314564Sdim  case eValueTypeRegisterSet:
284314564Sdim    return frame != nullptr;
285254721Semaste
286314564Sdim  case eValueTypeConstResult:
287314564Sdim  case eValueTypeVariableGlobal:
288314564Sdim  case eValueTypeVariableStatic:
289314564Sdim  case eValueTypeVariableThreadLocal:
290314564Sdim    return true;
291254721Semaste
292314564Sdim  case eValueTypeVariableArgument:
293314564Sdim  case eValueTypeVariableLocal:
294314564Sdim    if (frame) {
295341825Sdim      // We don't have a location list, we just need to see if the block that
296341825Sdim      // this variable was defined in is currently
297314564Sdim      Block *deepest_frame_block =
298314564Sdim          frame->GetSymbolContext(eSymbolContextBlock).block;
299314564Sdim      if (deepest_frame_block) {
300314564Sdim        SymbolContext variable_sc;
301314564Sdim        CalculateSymbolContext(&variable_sc);
302254721Semaste
303314564Sdim        // Check for static or global variable defined at the compile unit
304314564Sdim        // level that wasn't defined in a block
305314564Sdim        if (variable_sc.block == nullptr)
306314564Sdim          return true;
307309124Sdim
308314564Sdim        // Check if the variable is valid in the current block
309314564Sdim        if (variable_sc.block != deepest_frame_block &&
310314564Sdim            !variable_sc.block->Contains(deepest_frame_block))
311314564Sdim          return false;
312254721Semaste
313314564Sdim        // If no scope range is specified then it means that the scope is the
314341825Sdim        // same as the scope of the enclosing lexical block.
315314564Sdim        if (m_scope_range.IsEmpty())
316314564Sdim          return true;
317309124Sdim
318314564Sdim        addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();
319314564Sdim        return m_scope_range.FindEntryThatContains(file_address) != nullptr;
320314564Sdim      }
321314564Sdim    }
322314564Sdim    break;
323309124Sdim
324314564Sdim  default:
325314564Sdim    break;
326314564Sdim  }
327314564Sdim  return false;
328314564Sdim}
329314564Sdim
330321369SdimStatus Variable::GetValuesForVariableExpressionPath(
331314564Sdim    llvm::StringRef variable_expr_path, ExecutionContextScope *scope,
332314564Sdim    GetVariableCallback callback, void *baton, VariableList &variable_list,
333314564Sdim    ValueObjectList &valobj_list) {
334321369Sdim  Status error;
335314564Sdim  if (!callback || variable_expr_path.empty()) {
336314564Sdim    error.SetErrorString("unknown error");
337314564Sdim    return error;
338314564Sdim  }
339314564Sdim
340314564Sdim  switch (variable_expr_path.front()) {
341314564Sdim  case '*':
342314564Sdim    error = Variable::GetValuesForVariableExpressionPath(
343314564Sdim        variable_expr_path.drop_front(), scope, callback, baton, variable_list,
344314564Sdim        valobj_list);
345314564Sdim    if (error.Fail()) {
346314564Sdim      error.SetErrorString("unknown error");
347314564Sdim      return error;
348314564Sdim    }
349314564Sdim    for (uint32_t i = 0; i < valobj_list.GetSize();) {
350321369Sdim      Status tmp_error;
351314564Sdim      ValueObjectSP valobj_sp(
352314564Sdim          valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
353314564Sdim      if (tmp_error.Fail()) {
354314564Sdim        variable_list.RemoveVariableAtIndex(i);
355314564Sdim        valobj_list.RemoveValueObjectAtIndex(i);
356314564Sdim      } else {
357314564Sdim        valobj_list.SetValueObjectAtIndex(i, valobj_sp);
358314564Sdim        ++i;
359314564Sdim      }
360314564Sdim    }
361314564Sdim    return error;
362314564Sdim  case '&': {
363314564Sdim    error = Variable::GetValuesForVariableExpressionPath(
364314564Sdim        variable_expr_path.drop_front(), scope, callback, baton, variable_list,
365314564Sdim        valobj_list);
366314564Sdim    if (error.Success()) {
367314564Sdim      for (uint32_t i = 0; i < valobj_list.GetSize();) {
368321369Sdim        Status tmp_error;
369314564Sdim        ValueObjectSP valobj_sp(
370314564Sdim            valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
371314564Sdim        if (tmp_error.Fail()) {
372314564Sdim          variable_list.RemoveVariableAtIndex(i);
373314564Sdim          valobj_list.RemoveValueObjectAtIndex(i);
374314564Sdim        } else {
375314564Sdim          valobj_list.SetValueObjectAtIndex(i, valobj_sp);
376314564Sdim          ++i;
377254721Semaste        }
378314564Sdim      }
379314564Sdim    } else {
380314564Sdim      error.SetErrorString("unknown error");
381314564Sdim    }
382314564Sdim    return error;
383314564Sdim  } break;
384254721Semaste
385314564Sdim  default: {
386314564Sdim    static RegularExpression g_regex(
387314564Sdim        llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));
388314564Sdim    RegularExpression::Match regex_match(1);
389314564Sdim    std::string variable_name;
390314564Sdim    variable_list.Clear();
391314564Sdim    if (!g_regex.Execute(variable_expr_path, &regex_match)) {
392314564Sdim      error.SetErrorStringWithFormat(
393314564Sdim          "unable to extract a variable name from '%s'",
394314564Sdim          variable_expr_path.str().c_str());
395314564Sdim      return error;
396254721Semaste    }
397314564Sdim    if (!regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) {
398314564Sdim      error.SetErrorStringWithFormat(
399314564Sdim          "unable to extract a variable name from '%s'",
400314564Sdim          variable_expr_path.str().c_str());
401314564Sdim      return error;
402314564Sdim    }
403314564Sdim    if (!callback(baton, variable_name.c_str(), variable_list)) {
404314564Sdim      error.SetErrorString("unknown error");
405314564Sdim      return error;
406314564Sdim    }
407314564Sdim    uint32_t i = 0;
408314564Sdim    while (i < variable_list.GetSize()) {
409314564Sdim      VariableSP var_sp(variable_list.GetVariableAtIndex(i));
410314564Sdim      ValueObjectSP valobj_sp;
411314564Sdim      if (!var_sp) {
412314564Sdim        variable_list.RemoveVariableAtIndex(i);
413314564Sdim        continue;
414314564Sdim      }
415314564Sdim      ValueObjectSP variable_valobj_sp(
416314564Sdim          ValueObjectVariable::Create(scope, var_sp));
417314564Sdim      if (!variable_valobj_sp) {
418314564Sdim        variable_list.RemoveVariableAtIndex(i);
419314564Sdim        continue;
420314564Sdim      }
421254721Semaste
422314564Sdim      llvm::StringRef variable_sub_expr_path =
423314564Sdim          variable_expr_path.drop_front(variable_name.size());
424314564Sdim      if (!variable_sub_expr_path.empty()) {
425314564Sdim        valobj_sp = variable_valobj_sp->GetValueForExpressionPath(
426327952Sdim            variable_sub_expr_path);
427314564Sdim        if (!valobj_sp) {
428314564Sdim          error.SetErrorStringWithFormat(
429314564Sdim              "invalid expression path '%s' for variable '%s'",
430314564Sdim              variable_sub_expr_path.str().c_str(),
431314564Sdim              var_sp->GetName().GetCString());
432314564Sdim          variable_list.RemoveVariableAtIndex(i);
433314564Sdim          continue;
434314564Sdim        }
435314564Sdim      } else {
436314564Sdim        // Just the name of a variable with no extras
437314564Sdim        valobj_sp = variable_valobj_sp;
438314564Sdim      }
439254721Semaste
440314564Sdim      valobj_list.Append(valobj_sp);
441314564Sdim      ++i;
442254721Semaste    }
443314564Sdim
444314564Sdim    if (variable_list.GetSize() > 0) {
445314564Sdim      error.Clear();
446314564Sdim      return error;
447314564Sdim    }
448314564Sdim  } break;
449314564Sdim  }
450314564Sdim  error.SetErrorString("unknown error");
451314564Sdim  return error;
452254721Semaste}
453254721Semaste
454314564Sdimbool Variable::DumpLocationForAddress(Stream *s, const Address &address) {
455341825Sdim  // Be sure to resolve the address to section offset prior to calling this
456341825Sdim  // function.
457314564Sdim  if (address.IsSectionOffset()) {
458314564Sdim    SymbolContext sc;
459314564Sdim    CalculateSymbolContext(&sc);
460314564Sdim    if (sc.module_sp == address.GetModule()) {
461314564Sdim      ABI *abi = nullptr;
462314564Sdim      if (m_owner_scope) {
463314564Sdim        ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
464314564Sdim        if (module_sp)
465321369Sdim          abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture()).get();
466314564Sdim      }
467254721Semaste
468314564Sdim      const addr_t file_addr = address.GetFileAddress();
469314564Sdim      if (sc.function) {
470314564Sdim        if (sc.function->GetAddressRange().ContainsFileAddress(address)) {
471314564Sdim          addr_t loclist_base_file_addr =
472314564Sdim              sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
473314564Sdim          if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
474314564Sdim            return false;
475314564Sdim          return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
476314564Sdim                                                   loclist_base_file_addr,
477314564Sdim                                                   file_addr, abi);
478254721Semaste        }
479314564Sdim      }
480314564Sdim      return m_location.DumpLocationForAddress(
481314564Sdim          s, eDescriptionLevelBrief, LLDB_INVALID_ADDRESS, file_addr, abi);
482254721Semaste    }
483314564Sdim  }
484314564Sdim  return false;
485254721Semaste}
486254721Semaste
487314564Sdimstatic void PrivateAutoComplete(
488314564Sdim    StackFrame *frame, llvm::StringRef partial_path,
489314564Sdim    const llvm::Twine
490314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
491314564Sdim    const CompilerType &compiler_type,
492314564Sdim    StringList &matches, bool &word_complete);
493254721Semaste
494314564Sdimstatic void PrivateAutoCompleteMembers(
495314564Sdim    StackFrame *frame, const std::string &partial_member_name,
496314564Sdim    llvm::StringRef partial_path,
497314564Sdim    const llvm::Twine
498314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
499314564Sdim    const CompilerType &compiler_type,
500314564Sdim    StringList &matches, bool &word_complete);
501254721Semaste
502314564Sdimstatic void PrivateAutoCompleteMembers(
503314564Sdim    StackFrame *frame, const std::string &partial_member_name,
504314564Sdim    llvm::StringRef partial_path,
505314564Sdim    const llvm::Twine
506314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
507314564Sdim    const CompilerType &compiler_type,
508314564Sdim    StringList &matches, bool &word_complete) {
509254721Semaste
510314564Sdim  // We are in a type parsing child members
511314564Sdim  const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();
512254721Semaste
513314564Sdim  if (num_bases > 0) {
514314564Sdim    for (uint32_t i = 0; i < num_bases; ++i) {
515314564Sdim      CompilerType base_class_type =
516314564Sdim          compiler_type.GetDirectBaseClassAtIndex(i, nullptr);
517314564Sdim
518314564Sdim      PrivateAutoCompleteMembers(
519314564Sdim          frame, partial_member_name, partial_path, prefix_path,
520314564Sdim          base_class_type.GetCanonicalType(), matches, word_complete);
521254721Semaste    }
522314564Sdim  }
523254721Semaste
524314564Sdim  const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();
525314564Sdim
526314564Sdim  if (num_vbases > 0) {
527314564Sdim    for (uint32_t i = 0; i < num_vbases; ++i) {
528314564Sdim      CompilerType vbase_class_type =
529314564Sdim          compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);
530314564Sdim
531314564Sdim      PrivateAutoCompleteMembers(
532314564Sdim          frame, partial_member_name, partial_path, prefix_path,
533314564Sdim          vbase_class_type.GetCanonicalType(), matches, word_complete);
534254721Semaste    }
535314564Sdim  }
536254721Semaste
537314564Sdim  // We are in a type parsing child members
538314564Sdim  const uint32_t num_fields = compiler_type.GetNumFields();
539314564Sdim
540314564Sdim  if (num_fields > 0) {
541314564Sdim    for (uint32_t i = 0; i < num_fields; ++i) {
542314564Sdim      std::string member_name;
543314564Sdim
544314564Sdim      CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(
545314564Sdim          i, member_name, nullptr, nullptr, nullptr);
546314564Sdim
547314564Sdim      if (partial_member_name.empty() ||
548314564Sdim          member_name.find(partial_member_name) == 0) {
549314564Sdim        if (member_name == partial_member_name) {
550314564Sdim          PrivateAutoComplete(
551314564Sdim              frame, partial_path,
552314564Sdim              prefix_path + member_name, // Anything that has been resolved
553314564Sdim                                         // already will be in here
554314564Sdim              member_compiler_type.GetCanonicalType(), matches, word_complete);
555314564Sdim        } else {
556314564Sdim          matches.AppendString((prefix_path + member_name).str());
557254721Semaste        }
558314564Sdim      }
559254721Semaste    }
560314564Sdim  }
561254721Semaste}
562254721Semaste
563314564Sdimstatic void PrivateAutoComplete(
564314564Sdim    StackFrame *frame, llvm::StringRef partial_path,
565314564Sdim    const llvm::Twine
566314564Sdim        &prefix_path, // Anything that has been resolved already will be in here
567314564Sdim    const CompilerType &compiler_type,
568314564Sdim    StringList &matches, bool &word_complete) {
569314564Sdim  //    printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =
570314564Sdim  //    '%s'\n", prefix_path.c_str(), partial_path.c_str());
571314564Sdim  std::string remaining_partial_path;
572254721Semaste
573314564Sdim  const lldb::TypeClass type_class = compiler_type.GetTypeClass();
574314564Sdim  if (partial_path.empty()) {
575314564Sdim    if (compiler_type.IsValid()) {
576314564Sdim      switch (type_class) {
577314564Sdim      default:
578314564Sdim      case eTypeClassArray:
579314564Sdim      case eTypeClassBlockPointer:
580314564Sdim      case eTypeClassBuiltin:
581314564Sdim      case eTypeClassComplexFloat:
582314564Sdim      case eTypeClassComplexInteger:
583314564Sdim      case eTypeClassEnumeration:
584314564Sdim      case eTypeClassFunction:
585314564Sdim      case eTypeClassMemberPointer:
586314564Sdim      case eTypeClassReference:
587314564Sdim      case eTypeClassTypedef:
588314564Sdim      case eTypeClassVector: {
589314564Sdim        matches.AppendString(prefix_path.str());
590314564Sdim        word_complete = matches.GetSize() == 1;
591314564Sdim      } break;
592254721Semaste
593314564Sdim      case eTypeClassClass:
594314564Sdim      case eTypeClassStruct:
595314564Sdim      case eTypeClassUnion:
596314564Sdim        if (prefix_path.str().back() != '.')
597314564Sdim          matches.AppendString((prefix_path + ".").str());
598314564Sdim        break;
599314564Sdim
600314564Sdim      case eTypeClassObjCObject:
601314564Sdim      case eTypeClassObjCInterface:
602314564Sdim        break;
603314564Sdim      case eTypeClassObjCObjectPointer:
604314564Sdim      case eTypeClassPointer: {
605314564Sdim        bool omit_empty_base_classes = true;
606314564Sdim        if (compiler_type.GetNumChildren(omit_empty_base_classes) > 0)
607314564Sdim          matches.AppendString((prefix_path + "->").str());
608314564Sdim        else {
609314564Sdim          matches.AppendString(prefix_path.str());
610314564Sdim          word_complete = true;
611254721Semaste        }
612314564Sdim      } break;
613314564Sdim      }
614314564Sdim    } else {
615314564Sdim      if (frame) {
616314564Sdim        const bool get_file_globals = true;
617314564Sdim
618314564Sdim        VariableList *variable_list = frame->GetVariableList(get_file_globals);
619314564Sdim
620314564Sdim        if (variable_list) {
621314564Sdim          const size_t num_variables = variable_list->GetSize();
622314564Sdim          for (size_t i = 0; i < num_variables; ++i) {
623314564Sdim            Variable *variable = variable_list->GetVariableAtIndex(i).get();
624314564Sdim            matches.AppendString(variable->GetName().AsCString());
625314564Sdim          }
626254721Semaste        }
627314564Sdim      }
628254721Semaste    }
629314564Sdim  } else {
630314564Sdim    const char ch = partial_path[0];
631314564Sdim    switch (ch) {
632314564Sdim    case '*':
633314564Sdim      if (prefix_path.str().empty()) {
634314564Sdim        PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,
635314564Sdim                            matches, word_complete);
636314564Sdim      }
637314564Sdim      break;
638254721Semaste
639314564Sdim    case '&':
640314564Sdim      if (prefix_path.isTriviallyEmpty()) {
641314564Sdim        PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
642314564Sdim                            compiler_type, matches, word_complete);
643314564Sdim      }
644314564Sdim      break;
645314564Sdim
646314564Sdim    case '-':
647341825Sdim      if (partial_path.size() > 1 && partial_path[1] == '>' &&
648341825Sdim          !prefix_path.str().empty()) {
649314564Sdim        switch (type_class) {
650314564Sdim        case lldb::eTypeClassPointer: {
651314564Sdim          CompilerType pointee_type(compiler_type.GetPointeeType());
652341825Sdim          if (partial_path.size() > 2 && partial_path[2]) {
653314564Sdim            // If there is more after the "->", then search deeper
654314564Sdim            PrivateAutoComplete(
655314564Sdim                frame, partial_path.substr(2), prefix_path + "->",
656314564Sdim                pointee_type.GetCanonicalType(), matches, word_complete);
657314564Sdim          } else {
658314564Sdim            // Nothing after the "->", so list all members
659314564Sdim            PrivateAutoCompleteMembers(
660314564Sdim                frame, std::string(), std::string(), prefix_path + "->",
661314564Sdim                pointee_type.GetCanonicalType(), matches, word_complete);
662314564Sdim          }
663314564Sdim        } break;
664314564Sdim        default:
665314564Sdim          break;
666314564Sdim        }
667314564Sdim      }
668314564Sdim      break;
669314564Sdim
670314564Sdim    case '.':
671314564Sdim      if (compiler_type.IsValid()) {
672314564Sdim        switch (type_class) {
673314564Sdim        case lldb::eTypeClassUnion:
674314564Sdim        case lldb::eTypeClassStruct:
675314564Sdim        case lldb::eTypeClassClass:
676341825Sdim          if (partial_path.size() > 1 && partial_path[1]) {
677314564Sdim            // If there is more after the ".", then search deeper
678314564Sdim            PrivateAutoComplete(frame, partial_path.substr(1),
679314564Sdim                                prefix_path + ".", compiler_type, matches,
680314564Sdim                                word_complete);
681314564Sdim
682314564Sdim          } else {
683314564Sdim            // Nothing after the ".", so list all members
684314564Sdim            PrivateAutoCompleteMembers(frame, std::string(), partial_path,
685314564Sdim                                       prefix_path + ".", compiler_type,
686314564Sdim                                       matches, word_complete);
687314564Sdim          }
688314564Sdim          break;
689314564Sdim        default:
690314564Sdim          break;
691314564Sdim        }
692314564Sdim      }
693314564Sdim      break;
694314564Sdim    default:
695314564Sdim      if (isalpha(ch) || ch == '_' || ch == '$') {
696314564Sdim        const size_t partial_path_len = partial_path.size();
697314564Sdim        size_t pos = 1;
698314564Sdim        while (pos < partial_path_len) {
699314564Sdim          const char curr_ch = partial_path[pos];
700314564Sdim          if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {
701314564Sdim            ++pos;
702314564Sdim            continue;
703314564Sdim          }
704314564Sdim          break;
705314564Sdim        }
706314564Sdim
707314564Sdim        std::string token(partial_path, 0, pos);
708314564Sdim        remaining_partial_path = partial_path.substr(pos);
709314564Sdim
710314564Sdim        if (compiler_type.IsValid()) {
711314564Sdim          PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
712314564Sdim                                     prefix_path, compiler_type, matches,
713254721Semaste                                     word_complete);
714314564Sdim        } else if (frame) {
715314564Sdim          // We haven't found our variable yet
716314564Sdim          const bool get_file_globals = true;
717254721Semaste
718314564Sdim          VariableList *variable_list =
719314564Sdim              frame->GetVariableList(get_file_globals);
720314564Sdim
721314564Sdim          if (!variable_list)
722254721Semaste            break;
723254721Semaste
724314564Sdim          const size_t num_variables = variable_list->GetSize();
725314564Sdim          for (size_t i = 0; i < num_variables; ++i) {
726314564Sdim            Variable *variable = variable_list->GetVariableAtIndex(i).get();
727314564Sdim
728314564Sdim            if (!variable)
729314564Sdim              continue;
730314564Sdim
731314564Sdim            const char *variable_name = variable->GetName().AsCString();
732314564Sdim            if (strstr(variable_name, token.c_str()) == variable_name) {
733314564Sdim              if (strcmp(variable_name, token.c_str()) == 0) {
734314564Sdim                Type *variable_type = variable->GetType();
735314564Sdim                if (variable_type) {
736314564Sdim                  CompilerType variable_compiler_type(
737314564Sdim                      variable_type->GetForwardCompilerType());
738314564Sdim                  PrivateAutoComplete(
739314564Sdim                      frame, remaining_partial_path,
740314564Sdim                      prefix_path + token, // Anything that has been resolved
741314564Sdim                                           // already will be in here
742314564Sdim                      variable_compiler_type.GetCanonicalType(), matches,
743314564Sdim                      word_complete);
744314564Sdim                } else {
745314564Sdim                  matches.AppendString((prefix_path + variable_name).str());
746254721Semaste                }
747314564Sdim              } else if (remaining_partial_path.empty()) {
748314564Sdim                matches.AppendString((prefix_path + variable_name).str());
749314564Sdim              }
750254721Semaste            }
751314564Sdim          }
752254721Semaste        }
753314564Sdim      }
754314564Sdim      break;
755254721Semaste    }
756314564Sdim  }
757254721Semaste}
758254721Semaste
759314564Sdimsize_t Variable::AutoComplete(const ExecutionContext &exe_ctx,
760341825Sdim                              CompletionRequest &request) {
761314564Sdim  CompilerType compiler_type;
762254721Semaste
763341825Sdim  bool word_complete = false;
764341825Sdim  StringList matches;
765341825Sdim  PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
766341825Sdim                      "", compiler_type, matches, word_complete);
767341825Sdim  request.SetWordComplete(word_complete);
768341825Sdim  request.AddCompletions(matches);
769254721Semaste
770341825Sdim  return request.GetNumberOfMatches();
771254721Semaste}
772