text: //===-- Variable.cpp ------------------------------------------------------===//
  //
  // 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::Variable(lldb::user_id_t uid, const char *name, const char *mangled,
                     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 location_is_constant_data,
                     bool static_member, bool constant)
      : 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(location_is_constant_data),
        m_static_member(static_member), m_constant(constant) {}
  
  Variable::~Variable() = default;
  
  lldb::LanguageType Variable::GetLanguage() const {
    lldb::LanguageType lang = m_mangled.GuessLanguage();
    if (lang != lldb::eLanguageTypeUnknown)
      return lang;
  
    if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) {
      if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown)
        return lang;
    } else if (auto *comp_unit =
                   m_owner_scope->CalculateSymbolContextCompileUnit()) {
      if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown)
        return lang;
    }
  
    return lldb::eLanguageTypeUnknown;
  }
  
  ConstString Variable::GetName() const {
    ConstString name = m_mangled.GetName();
    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);
  
    return m_mangled.NameMatches(name);
  }
  bool Variable::NameMatches(const RegularExpression &regex) const {
    if (regex.Execute(m_name.AsCString()))
      return true;
    if (m_mangled)
      return m_mangled.NameMatches(regex);
    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->Format(", type = {{{0:x-16}} {1} (", type->GetID(), 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->AsRawOstream() << "??? (" << 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 = ");
      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, 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()) {
      // We need to check if the address is valid for both scope range and value
      // range. 
      // Empty scope range means block range.
      bool valid_in_scope_range =
          GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains(
                                           address.GetFileAddress()) != nullptr;
      if (!valid_in_scope_range)
        return false;
      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:]*)(.*)"));
      llvm::SmallVector<llvm::StringRef, 2> matches;
      variable_list.Clear();
      if (!g_regex.Execute(variable_expr_path, &matches)) {
        error.SetErrorStringWithFormat(
            "unable to extract a variable name from '%s'",
            variable_expr_path.str().c_str());
        return error;
      }
      std::string variable_name = matches[1].str();
      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::DumpLocations(Stream *s, const Address &address) {
    SymbolContext sc;
    CalculateSymbolContext(&sc);
    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) {
      addr_t loclist_base_file_addr =
          sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
      if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)
        return false;
      return m_location.DumpLocations(s, eDescriptionLevelBrief,
                                      loclist_base_file_addr, 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, CompletionRequest &request);
  
  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, CompletionRequest &request) {
  
    // 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(), request);
      }
    }
  
    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(), request);
      }
    }
  
    // 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() ||
            llvm::StringRef(member_name).startswith(partial_member_name)) {
          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(), request);
          } else {
            request.AddCompletion((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, CompletionRequest &request) {
    //    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: {
          request.AddCompletion(prefix_path.str());
        } break;
  
        case eTypeClassClass:
        case eTypeClassStruct:
        case eTypeClassUnion:
          if (prefix_path.str().back() != '.')
            request.AddCompletion((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)
            request.AddCompletion((prefix_path + "->").str());
          else {
            request.AddCompletion(prefix_path.str());
          }
        } break;
        }
      } else {
        if (frame) {
          const bool get_file_globals = true;
  
          VariableList *variable_list = frame->GetVariableList(get_file_globals);
  
          if (variable_list) {
            for (const VariableSP &var_sp : *variable_list)
              request.AddCompletion(var_sp->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,
                              request);
        }
        break;
  
      case '&':
        if (prefix_path.isTriviallyEmpty()) {
          PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),
                              compiler_type, request);
        }
        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(), request);
            } else {
              // Nothing after the "->", so list all members
              PrivateAutoCompleteMembers(
                  frame, std::string(), std::string(), prefix_path + "->",
                  pointee_type.GetCanonicalType(), request);
            }
          } 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, request);
  
            } else {
              // Nothing after the ".", so list all members
              PrivateAutoCompleteMembers(frame, std::string(), partial_path,
                                         prefix_path + ".", compiler_type,
                                         request);
            }
            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(std::string(partial_path), 0, pos);
          remaining_partial_path = std::string(partial_path.substr(pos));
  
          if (compiler_type.IsValid()) {
            PrivateAutoCompleteMembers(frame, token, remaining_partial_path,
                                       prefix_path, compiler_type, request);
          } 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;
  
            for (VariableSP var_sp : *variable_list) {
  
              if (!var_sp)
                continue;
  
              llvm::StringRef variable_name = var_sp->GetName().GetStringRef();
              if (variable_name.startswith(token)) {
                if (variable_name == token) {
                  Type *variable_type = var_sp->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(), request);
                  } else {
                    request.AddCompletion((prefix_path + variable_name).str());
                  }
                } else if (remaining_partial_path.empty()) {
                  request.AddCompletion((prefix_path + variable_name).str());
                }
              }
            }
          }
        }
        break;
      }
    }
  }
  
  void Variable::AutoComplete(const ExecutionContext &exe_ctx,
                              CompletionRequest &request) {
    CompilerType compiler_type;
  
    PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),
                        "", compiler_type, request);
  }
  