summaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Symbol/Variable.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Symbol/Variable.cpp768
1 files changed, 768 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Symbol/Variable.cpp b/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
new file mode 100644
index 000000000000..29a7a5191f61
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
@@ -0,0 +1,768 @@
+//===-- Variable.cpp --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/Variable.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/CompilerDecl.h"
+#include "lldb/Symbol/CompilerDeclContext.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/RegularExpression.h"
+#include "lldb/Utility/Stream.h"
+
+#include "llvm/ADT/Twine.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Variable constructor
+Variable::Variable(
+ lldb::user_id_t uid, const char *name,
+ const char *mangled, // The mangled or fully qualified name of the variable.
+ const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope,
+ SymbolContextScope *context, const RangeList &scope_range,
+ Declaration *decl_ptr, const DWARFExpression &location, bool external,
+ bool artificial, bool static_member)
+ : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
+ m_symfile_type_sp(symfile_type_sp), m_scope(scope),
+ m_owner_scope(context), m_scope_range(scope_range),
+ m_declaration(decl_ptr), m_location(location), m_external(external),
+ m_artificial(artificial), m_loc_is_const_data(false),
+ m_static_member(static_member) {}
+
+// Destructor
+Variable::~Variable() {}
+
+lldb::LanguageType Variable::GetLanguage() const {
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+ if (variable_sc.comp_unit)
+ return variable_sc.comp_unit->GetLanguage();
+ return lldb::eLanguageTypeUnknown;
+}
+
+ConstString Variable::GetName() const {
+ ConstString name = m_mangled.GetName(GetLanguage());
+ if (name)
+ return name;
+ return m_name;
+}
+
+ConstString Variable::GetUnqualifiedName() const { return m_name; }
+
+bool Variable::NameMatches(ConstString name) const {
+ if (m_name == name)
+ return true;
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+
+ LanguageType language = eLanguageTypeUnknown;
+ if (variable_sc.comp_unit)
+ language = variable_sc.comp_unit->GetLanguage();
+ return m_mangled.NameMatches(name, language);
+}
+bool Variable::NameMatches(const RegularExpression &regex) const {
+ if (regex.Execute(m_name.AsCString()))
+ return true;
+ if (m_mangled)
+ return m_mangled.NameMatches(regex, GetLanguage());
+ return false;
+}
+
+Type *Variable::GetType() {
+ if (m_symfile_type_sp)
+ return m_symfile_type_sp->GetType();
+ return nullptr;
+}
+
+void Variable::Dump(Stream *s, bool show_context) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ *s << "Variable" << (const UserID &)*this;
+
+ if (m_name)
+ *s << ", name = \"" << m_name << "\"";
+
+ if (m_symfile_type_sp) {
+ Type *type = m_symfile_type_sp->GetType();
+ if (type) {
+ *s << ", type = {" << type->GetID() << "} " << (void *)type << " (";
+ type->DumpTypeName(s);
+ s->PutChar(')');
+ }
+ }
+
+ if (m_scope != eValueTypeInvalid) {
+ s->PutCString(", scope = ");
+ switch (m_scope) {
+ case eValueTypeVariableGlobal:
+ s->PutCString(m_external ? "global" : "static");
+ break;
+ case eValueTypeVariableArgument:
+ s->PutCString("parameter");
+ break;
+ case eValueTypeVariableLocal:
+ s->PutCString("local");
+ break;
+ case eValueTypeVariableThreadLocal:
+ s->PutCString("thread local");
+ break;
+ default:
+ *s << "??? (" << m_scope << ')';
+ }
+ }
+
+ if (show_context && m_owner_scope != nullptr) {
+ s->PutCString(", context = ( ");
+ m_owner_scope->DumpSymbolContext(s);
+ s->PutCString(" )");
+ }
+
+ bool show_fullpaths = false;
+ m_declaration.Dump(s, show_fullpaths);
+
+ if (m_location.IsValid()) {
+ s->PutCString(", location = ");
+ lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
+ if (m_location.IsLocationList()) {
+ SymbolContext variable_sc;
+ m_owner_scope->CalculateSymbolContext(&variable_sc);
+ if (variable_sc.function)
+ loclist_base_addr = variable_sc.function->GetAddressRange()
+ .GetBaseAddress()
+ .GetFileAddress();
+ }
+ ABISP abi;
+ if (m_owner_scope) {
+ ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
+ if (module_sp)
+ abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
+ }
+ m_location.GetDescription(s, lldb::eDescriptionLevelBrief,
+ loclist_base_addr, abi.get());
+ }
+
+ if (m_external)
+ s->PutCString(", external");
+
+ if (m_artificial)
+ s->PutCString(", artificial");
+
+ s->EOL();
+}
+
+bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,
+ bool show_module) {
+ bool dumped_declaration_info = false;
+ if (m_owner_scope) {
+ SymbolContext sc;
+ m_owner_scope->CalculateSymbolContext(&sc);
+ sc.block = nullptr;
+ sc.line_entry.Clear();
+ bool show_inlined_frames = false;
+ const bool show_function_arguments = true;
+ const bool show_function_name = true;
+
+ dumped_declaration_info = sc.DumpStopContext(
+ s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,
+ show_function_arguments, show_function_name);
+
+ if (sc.function)
+ s->PutChar(':');
+ }
+ if (m_declaration.DumpStopContext(s, false))
+ dumped_declaration_info = true;
+ return dumped_declaration_info;
+}
+
+size_t Variable::MemorySize() const { return sizeof(Variable); }
+
+CompilerDeclContext Variable::GetDeclContext() {
+ Type *type = GetType();
+ if (type)
+ return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());
+ return CompilerDeclContext();
+}
+
+CompilerDecl Variable::GetDecl() {
+ Type *type = GetType();
+ return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();
+}
+
+void Variable::CalculateSymbolContext(SymbolContext *sc) {
+ if (m_owner_scope) {
+ m_owner_scope->CalculateSymbolContext(sc);
+ sc->variable = this;
+ } else
+ sc->Clear(false);
+}
+
+bool Variable::LocationIsValidForFrame(StackFrame *frame) {
+ // Is the variable is described by a single location?
+ if (!m_location.IsLocationList()) {
+ // Yes it is, the location is valid.
+ return true;
+ }
+
+ if (frame) {
+ Function *function =
+ frame->GetSymbolContext(eSymbolContextFunction).function;
+ if (function) {
+ TargetSP target_sp(frame->CalculateTarget());
+
+ addr_t loclist_base_load_addr =
+ function->GetAddressRange().GetBaseAddress().GetLoadAddress(
+ target_sp.get());
+ if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ // It is a location list. We just need to tell if the location list
+ // contains the current address when converted to a load address
+ return m_location.LocationListContainsAddress(
+ loclist_base_load_addr,
+ frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get()));
+ }
+ }
+ return false;
+}
+
+bool Variable::LocationIsValidForAddress(const Address &address) {
+ // Be sure to resolve the address to section offset prior to calling this
+ // function.
+ if (address.IsSectionOffset()) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ if (sc.module_sp == address.GetModule()) {
+ // Is the variable is described by a single location?
+ if (!m_location.IsLocationList()) {
+ // Yes it is, the location is valid.
+ return true;
+ }
+
+ if (sc.function) {
+ addr_t loclist_base_file_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ // It is a location list. We just need to tell if the location list
+ // contains the current address when converted to a load address
+ return m_location.LocationListContainsAddress(loclist_base_file_addr,
+ address.GetFileAddress());
+ }
+ }
+ }
+ return false;
+}
+
+bool Variable::IsInScope(StackFrame *frame) {
+ switch (m_scope) {
+ case eValueTypeRegister:
+ case eValueTypeRegisterSet:
+ return frame != nullptr;
+
+ case eValueTypeConstResult:
+ case eValueTypeVariableGlobal:
+ case eValueTypeVariableStatic:
+ case eValueTypeVariableThreadLocal:
+ return true;
+
+ case eValueTypeVariableArgument:
+ case eValueTypeVariableLocal:
+ if (frame) {
+ // We don't have a location list, we just need to see if the block that
+ // this variable was defined in is currently
+ Block *deepest_frame_block =
+ frame->GetSymbolContext(eSymbolContextBlock).block;
+ if (deepest_frame_block) {
+ SymbolContext variable_sc;
+ CalculateSymbolContext(&variable_sc);
+
+ // Check for static or global variable defined at the compile unit
+ // level that wasn't defined in a block
+ if (variable_sc.block == nullptr)
+ return true;
+
+ // Check if the variable is valid in the current block
+ if (variable_sc.block != deepest_frame_block &&
+ !variable_sc.block->Contains(deepest_frame_block))
+ return false;
+
+ // If no scope range is specified then it means that the scope is the
+ // same as the scope of the enclosing lexical block.
+ if (m_scope_range.IsEmpty())
+ return true;
+
+ addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();
+ return m_scope_range.FindEntryThatContains(file_address) != nullptr;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+Status Variable::GetValuesForVariableExpressionPath(
+ llvm::StringRef variable_expr_path, ExecutionContextScope *scope,
+ GetVariableCallback callback, void *baton, VariableList &variable_list,
+ ValueObjectList &valobj_list) {
+ Status error;
+ if (!callback || variable_expr_path.empty()) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+
+ switch (variable_expr_path.front()) {
+ case '*':
+ error = Variable::GetValuesForVariableExpressionPath(
+ variable_expr_path.drop_front(), scope, callback, baton, variable_list,
+ valobj_list);
+ if (error.Fail()) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+ for (uint32_t i = 0; i < valobj_list.GetSize();) {
+ Status tmp_error;
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));
+ if (tmp_error.Fail()) {
+ variable_list.RemoveVariableAtIndex(i);
+ valobj_list.RemoveValueObjectAtIndex(i);
+ } else {
+ valobj_list.SetValueObjectAtIndex(i, valobj_sp);
+ ++i;
+ }
+ }
+ return error;
+ case '&': {
+ error = Variable::GetValuesForVariableExpressionPath(
+ variable_expr_path.drop_front(), scope, callback, baton, variable_list,
+ valobj_list);
+ if (error.Success()) {
+ for (uint32_t i = 0; i < valobj_list.GetSize();) {
+ Status tmp_error;
+ ValueObjectSP valobj_sp(
+ valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));
+ if (tmp_error.Fail()) {
+ variable_list.RemoveVariableAtIndex(i);
+ valobj_list.RemoveValueObjectAtIndex(i);
+ } else {
+ valobj_list.SetValueObjectAtIndex(i, valobj_sp);
+ ++i;
+ }
+ }
+ } else {
+ error.SetErrorString("unknown error");
+ }
+ return error;
+ } break;
+
+ default: {
+ static RegularExpression g_regex(
+ llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));
+ RegularExpression::Match regex_match(1);
+ std::string variable_name;
+ variable_list.Clear();
+ if (!g_regex.Execute(variable_expr_path, &regex_match)) {
+ error.SetErrorStringWithFormat(
+ "unable to extract a variable name from '%s'",
+ variable_expr_path.str().c_str());
+ return error;
+ }
+ if (!regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) {
+ error.SetErrorStringWithFormat(
+ "unable to extract a variable name from '%s'",
+ variable_expr_path.str().c_str());
+ return error;
+ }
+ if (!callback(baton, variable_name.c_str(), variable_list)) {
+ error.SetErrorString("unknown error");
+ return error;
+ }
+ uint32_t i = 0;
+ while (i < variable_list.GetSize()) {
+ VariableSP var_sp(variable_list.GetVariableAtIndex(i));
+ ValueObjectSP valobj_sp;
+ if (!var_sp) {
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+ ValueObjectSP variable_valobj_sp(
+ ValueObjectVariable::Create(scope, var_sp));
+ if (!variable_valobj_sp) {
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+
+ llvm::StringRef variable_sub_expr_path =
+ variable_expr_path.drop_front(variable_name.size());
+ if (!variable_sub_expr_path.empty()) {
+ valobj_sp = variable_valobj_sp->GetValueForExpressionPath(
+ variable_sub_expr_path);
+ if (!valobj_sp) {
+ error.SetErrorStringWithFormat(
+ "invalid expression path '%s' for variable '%s'",
+ variable_sub_expr_path.str().c_str(),
+ var_sp->GetName().GetCString());
+ variable_list.RemoveVariableAtIndex(i);
+ continue;
+ }
+ } else {
+ // Just the name of a variable with no extras
+ valobj_sp = variable_valobj_sp;
+ }
+
+ valobj_list.Append(valobj_sp);
+ ++i;
+ }
+
+ if (variable_list.GetSize() > 0) {
+ error.Clear();
+ return error;
+ }
+ } break;
+ }
+ error.SetErrorString("unknown error");
+ return error;
+}
+
+bool Variable::DumpLocationForAddress(Stream *s, const Address &address) {
+ // Be sure to resolve the address to section offset prior to calling this
+ // function.
+ if (address.IsSectionOffset()) {
+ SymbolContext sc;
+ CalculateSymbolContext(&sc);
+ if (sc.module_sp == address.GetModule()) {
+ ABISP abi;
+ if (m_owner_scope) {
+ ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());
+ if (module_sp)
+ abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());
+ }
+
+ const addr_t file_addr = address.GetFileAddress();
+ if (sc.function) {
+ if (sc.function->GetAddressRange().ContainsFileAddress(address)) {
+ addr_t loclist_base_file_addr =
+ sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+ if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
+ loclist_base_file_addr,
+ file_addr, abi.get());
+ }
+ }
+ return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief,
+ LLDB_INVALID_ADDRESS, file_addr,
+ abi.get());
+ }
+ }
+ return false;
+}
+
+static void PrivateAutoComplete(
+ StackFrame *frame, llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete);
+
+static void PrivateAutoCompleteMembers(
+ StackFrame *frame, const std::string &partial_member_name,
+ llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete);
+
+static void PrivateAutoCompleteMembers(
+ StackFrame *frame, const std::string &partial_member_name,
+ llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete) {
+
+ // We are in a type parsing child members
+ const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();
+
+ if (num_bases > 0) {
+ for (uint32_t i = 0; i < num_bases; ++i) {
+ CompilerType base_class_type =
+ compiler_type.GetDirectBaseClassAtIndex(i, nullptr);
+
+ PrivateAutoCompleteMembers(
+ frame, partial_member_name, partial_path, prefix_path,
+ base_class_type.GetCanonicalType(), matches, word_complete);
+ }
+ }
+
+ const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();
+
+ if (num_vbases > 0) {
+ for (uint32_t i = 0; i < num_vbases; ++i) {
+ CompilerType vbase_class_type =
+ compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);
+
+ PrivateAutoCompleteMembers(
+ frame, partial_member_name, partial_path, prefix_path,
+ vbase_class_type.GetCanonicalType(), matches, word_complete);
+ }
+ }
+
+ // We are in a type parsing child members
+ const uint32_t num_fields = compiler_type.GetNumFields();
+
+ if (num_fields > 0) {
+ for (uint32_t i = 0; i < num_fields; ++i) {
+ std::string member_name;
+
+ CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(
+ i, member_name, nullptr, nullptr, nullptr);
+
+ if (partial_member_name.empty() ||
+ member_name.find(partial_member_name) == 0) {
+ if (member_name == partial_member_name) {
+ PrivateAutoComplete(
+ frame, partial_path,
+ prefix_path + member_name, // Anything that has been resolved
+ // already will be in here
+ member_compiler_type.GetCanonicalType(), matches, word_complete);
+ } else {
+ matches.AppendString((prefix_path + member_name).str());
+ }
+ }
+ }
+ }
+}
+
+static void PrivateAutoComplete(
+ StackFrame *frame, llvm::StringRef partial_path,
+ const llvm::Twine
+ &prefix_path, // Anything that has been resolved already will be in here
+ const CompilerType &compiler_type,
+ StringList &matches, bool &word_complete) {
+ // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =
+ // '%s'\n", prefix_path.c_str(), partial_path.c_str());
+ std::string remaining_partial_path;
+
+ const lldb::TypeClass type_class = compiler_type.GetTypeClass();
+ if (partial_path.empty()) {
+ if (compiler_type.IsValid()) {
+ switch (type_class) {
+ default:
+ case eTypeClassArray:
+ case eTypeClassBlockPointer:
+ case eTypeClassBuiltin:
+ case eTypeClassComplexFloat:
+ case eTypeClassComplexInteger:
+ case eTypeClassEnumeration:
+ case eTypeClassFunction:
+ case eTypeClassMemberPointer:
+ case eTypeClassReference:
+ case eTypeClassTypedef:
+ case eTypeClassVector: {
+ matches.AppendString(prefix_path.str());
+ word_complete = matches.GetSize() == 1;
+ } break;
+
+ case eTypeClassClass:
+ case eTypeClassStruct:
+ case eTypeClassUnion:
+ if (prefix_path.str().back() != '.')
+ matches.AppendString((prefix_path + ".").str());
+ break;
+
+ case eTypeClassObjCObject:
+ case eTypeClassObjCInterface:
+ break;
+ case eTypeClassObjCObjectPointer:
+ case eTypeClassPointer: {
+ bool omit_empty_base_classes = true;
+ if (compiler_type.GetNumChildren(omit_empty_base_classes, nullptr) > 0)
+ matches.AppendString((prefix_path + "->").str());
+ else {
+ matches.AppendString(prefix_path.str());
+ word_complete = true;
+ }
+ } break;
+ }
+ } else {
+ if (frame) {
+ const bool get_file_globals = true;
+
+ VariableList *variable_list = frame->GetVariableList(get_file_globals);
+
+ if (variable_list) {
+ const size_t num_variables = variable_list->GetSize();
+ for (size_t i = 0; i < num_variables; ++i) {
+ Variable *variable = variable_list->GetVariableAtIndex(i).get();
+ matches.AppendString(variable->GetName().AsCString());
+ }
+ }
+ }
+ }
+ } else {
+ const char ch = partial_path[0];
+ switch (ch) {
+ case '*':
+ if (prefix_path.str().empty()) {
+ PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,
+ matches, word_complete);
+ }
+ break;
+
+ case '&':
+ if (prefix_path.isTriviallyEmpty()) {
+ PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
+ compiler_type, matches, word_complete);
+ }
+ break;
+
+ case '-':
+ if (partial_path.size() > 1 && partial_path[1] == '>' &&
+ !prefix_path.str().empty()) {
+ switch (type_class) {
+ case lldb::eTypeClassPointer: {
+ CompilerType pointee_type(compiler_type.GetPointeeType());
+ if (partial_path.size() > 2 && partial_path[2]) {
+ // If there is more after the "->", then search deeper
+ PrivateAutoComplete(
+ frame, partial_path.substr(2), prefix_path + "->",
+ pointee_type.GetCanonicalType(), matches, word_complete);
+ } else {
+ // Nothing after the "->", so list all members
+ PrivateAutoCompleteMembers(
+ frame, std::string(), std::string(), prefix_path + "->",
+ pointee_type.GetCanonicalType(), matches, word_complete);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case '.':
+ if (compiler_type.IsValid()) {
+ switch (type_class) {
+ case lldb::eTypeClassUnion:
+ case lldb::eTypeClassStruct:
+ case lldb::eTypeClassClass:
+ if (partial_path.size() > 1 && partial_path[1]) {
+ // If there is more after the ".", then search deeper
+ PrivateAutoComplete(frame, partial_path.substr(1),
+ prefix_path + ".", compiler_type, matches,
+ word_complete);
+
+ } else {
+ // Nothing after the ".", so list all members
+ PrivateAutoCompleteMembers(frame, std::string(), partial_path,
+ prefix_path + ".", compiler_type,
+ matches, word_complete);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ if (isalpha(ch) || ch == '_' || ch == '$') {
+ const size_t partial_path_len = partial_path.size();
+ size_t pos = 1;
+ while (pos < partial_path_len) {
+ const char curr_ch = partial_path[pos];
+ if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {
+ ++pos;
+ continue;
+ }
+ break;
+ }
+
+ std::string token(partial_path, 0, pos);
+ remaining_partial_path = partial_path.substr(pos);
+
+ if (compiler_type.IsValid()) {
+ PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
+ prefix_path, compiler_type, matches,
+ word_complete);
+ } else if (frame) {
+ // We haven't found our variable yet
+ const bool get_file_globals = true;
+
+ VariableList *variable_list =
+ frame->GetVariableList(get_file_globals);
+
+ if (!variable_list)
+ break;
+
+ const size_t num_variables = variable_list->GetSize();
+ for (size_t i = 0; i < num_variables; ++i) {
+ Variable *variable = variable_list->GetVariableAtIndex(i).get();
+
+ if (!variable)
+ continue;
+
+ const char *variable_name = variable->GetName().AsCString();
+ if (strstr(variable_name, token.c_str()) == variable_name) {
+ if (strcmp(variable_name, token.c_str()) == 0) {
+ Type *variable_type = variable->GetType();
+ if (variable_type) {
+ CompilerType variable_compiler_type(
+ variable_type->GetForwardCompilerType());
+ PrivateAutoComplete(
+ frame, remaining_partial_path,
+ prefix_path + token, // Anything that has been resolved
+ // already will be in here
+ variable_compiler_type.GetCanonicalType(), matches,
+ word_complete);
+ } else {
+ matches.AppendString((prefix_path + variable_name).str());
+ }
+ } else if (remaining_partial_path.empty()) {
+ matches.AppendString((prefix_path + variable_name).str());
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+size_t Variable::AutoComplete(const ExecutionContext &exe_ctx,
+ CompletionRequest &request) {
+ CompilerType compiler_type;
+
+ bool word_complete = false;
+ StringList matches;
+ PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
+ "", compiler_type, matches, word_complete);
+ request.SetWordComplete(word_complete);
+ request.AddCompletions(matches);
+
+ return request.GetNumberOfMatches();
+}