Path: blob/main/contrib/llvm-project/lldb/source/Symbol/Variable.cpp
39587 views
//===-- Variable.cpp ------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Symbol/Variable.h"910#include "lldb/Core/Module.h"11#include "lldb/Core/ValueObject.h"12#include "lldb/Core/ValueObjectVariable.h"13#include "lldb/Symbol/Block.h"14#include "lldb/Symbol/CompileUnit.h"15#include "lldb/Symbol/CompilerDecl.h"16#include "lldb/Symbol/CompilerDeclContext.h"17#include "lldb/Symbol/Function.h"18#include "lldb/Symbol/SymbolContext.h"19#include "lldb/Symbol/SymbolFile.h"20#include "lldb/Symbol/Type.h"21#include "lldb/Symbol/TypeSystem.h"22#include "lldb/Symbol/VariableList.h"23#include "lldb/Target/ABI.h"24#include "lldb/Target/Process.h"25#include "lldb/Target/RegisterContext.h"26#include "lldb/Target/StackFrame.h"27#include "lldb/Target/Target.h"28#include "lldb/Target/Thread.h"29#include "lldb/Utility/LLDBLog.h"30#include "lldb/Utility/Log.h"31#include "lldb/Utility/RegularExpression.h"32#include "lldb/Utility/Stream.h"3334#include "llvm/ADT/Twine.h"3536using namespace lldb;37using namespace lldb_private;3839Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled,40const lldb::SymbolFileTypeSP &symfile_type_sp,41ValueType scope, SymbolContextScope *context,42const RangeList &scope_range, Declaration *decl_ptr,43const DWARFExpressionList &location_list, bool external,44bool artificial, bool location_is_constant_data,45bool static_member)46: UserID(uid), m_name(name), m_mangled(ConstString(mangled)),47m_symfile_type_sp(symfile_type_sp), m_scope(scope),48m_owner_scope(context), m_scope_range(scope_range),49m_declaration(decl_ptr), m_location_list(location_list), m_external(external),50m_artificial(artificial), m_loc_is_const_data(location_is_constant_data),51m_static_member(static_member) {}5253Variable::~Variable() = default;5455lldb::LanguageType Variable::GetLanguage() const {56lldb::LanguageType lang = m_mangled.GuessLanguage();57if (lang != lldb::eLanguageTypeUnknown)58return lang;5960if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) {61if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown)62return lang;63} else if (auto *comp_unit =64m_owner_scope->CalculateSymbolContextCompileUnit()) {65if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown)66return lang;67}6869return lldb::eLanguageTypeUnknown;70}7172ConstString Variable::GetName() const {73ConstString name = m_mangled.GetName();74if (name)75return name;76return m_name;77}7879ConstString Variable::GetUnqualifiedName() const { return m_name; }8081bool Variable::NameMatches(ConstString name) const {82if (m_name == name)83return true;84SymbolContext variable_sc;85m_owner_scope->CalculateSymbolContext(&variable_sc);8687return m_mangled.NameMatches(name);88}89bool Variable::NameMatches(const RegularExpression ®ex) const {90if (regex.Execute(m_name.AsCString()))91return true;92if (m_mangled)93return m_mangled.NameMatches(regex);94return false;95}9697Type *Variable::GetType() {98if (m_symfile_type_sp)99return m_symfile_type_sp->GetType();100return nullptr;101}102103void Variable::Dump(Stream *s, bool show_context) const {104s->Printf("%p: ", static_cast<const void *>(this));105s->Indent();106*s << "Variable" << (const UserID &)*this;107108if (m_name)109*s << ", name = \"" << m_name << "\"";110111if (m_symfile_type_sp) {112Type *type = m_symfile_type_sp->GetType();113if (type) {114s->Format(", type = {{{0:x-16}} {1} (", type->GetID(), type);115type->DumpTypeName(s);116s->PutChar(')');117}118}119120if (m_scope != eValueTypeInvalid) {121s->PutCString(", scope = ");122switch (m_scope) {123case eValueTypeVariableGlobal:124s->PutCString(m_external ? "global" : "static");125break;126case eValueTypeVariableArgument:127s->PutCString("parameter");128break;129case eValueTypeVariableLocal:130s->PutCString("local");131break;132case eValueTypeVariableThreadLocal:133s->PutCString("thread local");134break;135default:136s->AsRawOstream() << "??? (" << m_scope << ')';137}138}139140if (show_context && m_owner_scope != nullptr) {141s->PutCString(", context = ( ");142m_owner_scope->DumpSymbolContext(s);143s->PutCString(" )");144}145146bool show_fullpaths = false;147m_declaration.Dump(s, show_fullpaths);148149if (m_location_list.IsValid()) {150s->PutCString(", location = ");151ABISP abi;152if (m_owner_scope) {153ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());154if (module_sp)155abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());156}157m_location_list.GetDescription(s, lldb::eDescriptionLevelBrief, abi.get());158}159160if (m_external)161s->PutCString(", external");162163if (m_artificial)164s->PutCString(", artificial");165166s->EOL();167}168169bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,170bool show_module) {171bool dumped_declaration_info = false;172if (m_owner_scope) {173SymbolContext sc;174m_owner_scope->CalculateSymbolContext(&sc);175sc.block = nullptr;176sc.line_entry.Clear();177bool show_inlined_frames = false;178const bool show_function_arguments = true;179const bool show_function_name = true;180181dumped_declaration_info = sc.DumpStopContext(182s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,183show_function_arguments, show_function_name);184185if (sc.function)186s->PutChar(':');187}188if (m_declaration.DumpStopContext(s, false))189dumped_declaration_info = true;190return dumped_declaration_info;191}192193size_t Variable::MemorySize() const { return sizeof(Variable); }194195CompilerDeclContext Variable::GetDeclContext() {196Type *type = GetType();197if (type)198return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());199return CompilerDeclContext();200}201202CompilerDecl Variable::GetDecl() {203Type *type = GetType();204return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();205}206207void Variable::CalculateSymbolContext(SymbolContext *sc) {208if (m_owner_scope) {209m_owner_scope->CalculateSymbolContext(sc);210sc->variable = this;211} else212sc->Clear(false);213}214215bool Variable::LocationIsValidForFrame(StackFrame *frame) {216if (frame) {217Function *function =218frame->GetSymbolContext(eSymbolContextFunction).function;219if (function) {220TargetSP target_sp(frame->CalculateTarget());221222addr_t loclist_base_load_addr =223function->GetAddressRange().GetBaseAddress().GetLoadAddress(224target_sp.get());225if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)226return false;227// It is a location list. We just need to tell if the location list228// contains the current address when converted to a load address229return m_location_list.ContainsAddress(230loclist_base_load_addr,231frame->GetFrameCodeAddressForSymbolication().GetLoadAddress(232target_sp.get()));233}234}235return false;236}237238bool Variable::LocationIsValidForAddress(const Address &address) {239// Be sure to resolve the address to section offset prior to calling this240// function.241if (address.IsSectionOffset()) {242// We need to check if the address is valid for both scope range and value243// range.244// Empty scope range means block range.245bool valid_in_scope_range =246GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains(247address.GetFileAddress()) != nullptr;248if (!valid_in_scope_range)249return false;250SymbolContext sc;251CalculateSymbolContext(&sc);252if (sc.module_sp == address.GetModule()) {253// Is the variable is described by a single location?254if (m_location_list.IsAlwaysValidSingleExpr()) {255// Yes it is, the location is valid.256return true;257}258259if (sc.function) {260addr_t loclist_base_file_addr =261sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();262if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)263return false;264// It is a location list. We just need to tell if the location list265// contains the current address when converted to a load address266return m_location_list.ContainsAddress(loclist_base_file_addr,267address.GetFileAddress());268}269}270}271return false;272}273274bool Variable::IsInScope(StackFrame *frame) {275switch (m_scope) {276case eValueTypeRegister:277case eValueTypeRegisterSet:278return frame != nullptr;279280case eValueTypeConstResult:281case eValueTypeVariableGlobal:282case eValueTypeVariableStatic:283case eValueTypeVariableThreadLocal:284return true;285286case eValueTypeVariableArgument:287case eValueTypeVariableLocal:288if (frame) {289// We don't have a location list, we just need to see if the block that290// this variable was defined in is currently291Block *deepest_frame_block =292frame->GetSymbolContext(eSymbolContextBlock).block;293if (deepest_frame_block) {294SymbolContext variable_sc;295CalculateSymbolContext(&variable_sc);296297// Check for static or global variable defined at the compile unit298// level that wasn't defined in a block299if (variable_sc.block == nullptr)300return true;301302// Check if the variable is valid in the current block303if (variable_sc.block != deepest_frame_block &&304!variable_sc.block->Contains(deepest_frame_block))305return false;306307// If no scope range is specified then it means that the scope is the308// same as the scope of the enclosing lexical block.309if (m_scope_range.IsEmpty())310return true;311312addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress();313return m_scope_range.FindEntryThatContains(file_address) != nullptr;314}315}316break;317318default:319break;320}321return false;322}323324Status Variable::GetValuesForVariableExpressionPath(325llvm::StringRef variable_expr_path, ExecutionContextScope *scope,326GetVariableCallback callback, void *baton, VariableList &variable_list,327ValueObjectList &valobj_list) {328Status error;329if (!callback || variable_expr_path.empty()) {330error.SetErrorString("unknown error");331return error;332}333334switch (variable_expr_path.front()) {335case '*':336error = Variable::GetValuesForVariableExpressionPath(337variable_expr_path.drop_front(), scope, callback, baton, variable_list,338valobj_list);339if (error.Fail()) {340error.SetErrorString("unknown error");341return error;342}343for (uint32_t i = 0; i < valobj_list.GetSize();) {344Status tmp_error;345ValueObjectSP valobj_sp(346valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));347if (tmp_error.Fail()) {348variable_list.RemoveVariableAtIndex(i);349valobj_list.RemoveValueObjectAtIndex(i);350} else {351valobj_list.SetValueObjectAtIndex(i, valobj_sp);352++i;353}354}355return error;356case '&': {357error = Variable::GetValuesForVariableExpressionPath(358variable_expr_path.drop_front(), scope, callback, baton, variable_list,359valobj_list);360if (error.Success()) {361for (uint32_t i = 0; i < valobj_list.GetSize();) {362Status tmp_error;363ValueObjectSP valobj_sp(364valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));365if (tmp_error.Fail()) {366variable_list.RemoveVariableAtIndex(i);367valobj_list.RemoveValueObjectAtIndex(i);368} else {369valobj_list.SetValueObjectAtIndex(i, valobj_sp);370++i;371}372}373} else {374error.SetErrorString("unknown error");375}376return error;377} break;378379default: {380static RegularExpression g_regex(381llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));382llvm::SmallVector<llvm::StringRef, 2> matches;383variable_list.Clear();384if (!g_regex.Execute(variable_expr_path, &matches)) {385error.SetErrorStringWithFormatv(386"unable to extract a variable name from '{0}'", variable_expr_path);387return error;388}389std::string variable_name = matches[1].str();390if (!callback(baton, variable_name.c_str(), variable_list)) {391error.SetErrorString("unknown error");392return error;393}394uint32_t i = 0;395while (i < variable_list.GetSize()) {396VariableSP var_sp(variable_list.GetVariableAtIndex(i));397ValueObjectSP valobj_sp;398if (!var_sp) {399variable_list.RemoveVariableAtIndex(i);400continue;401}402ValueObjectSP variable_valobj_sp(403ValueObjectVariable::Create(scope, var_sp));404if (!variable_valobj_sp) {405variable_list.RemoveVariableAtIndex(i);406continue;407}408409llvm::StringRef variable_sub_expr_path =410variable_expr_path.drop_front(variable_name.size());411if (!variable_sub_expr_path.empty()) {412valobj_sp = variable_valobj_sp->GetValueForExpressionPath(413variable_sub_expr_path);414if (!valobj_sp) {415error.SetErrorStringWithFormatv(416"invalid expression path '{0}' for variable '{1}'",417variable_sub_expr_path, var_sp->GetName().GetCString());418variable_list.RemoveVariableAtIndex(i);419continue;420}421} else {422// Just the name of a variable with no extras423valobj_sp = variable_valobj_sp;424}425426valobj_list.Append(valobj_sp);427++i;428}429430if (variable_list.GetSize() > 0) {431error.Clear();432return error;433}434} break;435}436error.SetErrorString("unknown error");437return error;438}439440bool Variable::DumpLocations(Stream *s, const Address &address) {441SymbolContext sc;442CalculateSymbolContext(&sc);443ABISP abi;444if (m_owner_scope) {445ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());446if (module_sp)447abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());448}449450const addr_t file_addr = address.GetFileAddress();451if (sc.function) {452addr_t loclist_base_file_addr =453sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();454if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)455return false;456return m_location_list.DumpLocations(s, eDescriptionLevelBrief,457loclist_base_file_addr, file_addr,458abi.get());459}460return false;461}462463static void PrivateAutoComplete(464StackFrame *frame, llvm::StringRef partial_path,465const llvm::Twine466&prefix_path, // Anything that has been resolved already will be in here467const CompilerType &compiler_type, CompletionRequest &request);468469static void PrivateAutoCompleteMembers(470StackFrame *frame, const std::string &partial_member_name,471llvm::StringRef partial_path,472const llvm::Twine473&prefix_path, // Anything that has been resolved already will be in here474const CompilerType &compiler_type, CompletionRequest &request) {475476// We are in a type parsing child members477const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();478479if (num_bases > 0) {480for (uint32_t i = 0; i < num_bases; ++i) {481CompilerType base_class_type =482compiler_type.GetDirectBaseClassAtIndex(i, nullptr);483484PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,485prefix_path,486base_class_type.GetCanonicalType(), request);487}488}489490const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();491492if (num_vbases > 0) {493for (uint32_t i = 0; i < num_vbases; ++i) {494CompilerType vbase_class_type =495compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);496497PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,498prefix_path,499vbase_class_type.GetCanonicalType(), request);500}501}502503// We are in a type parsing child members504const uint32_t num_fields = compiler_type.GetNumFields();505506if (num_fields > 0) {507for (uint32_t i = 0; i < num_fields; ++i) {508std::string member_name;509510CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(511i, member_name, nullptr, nullptr, nullptr);512513if (partial_member_name.empty()) {514request.AddCompletion((prefix_path + member_name).str());515} else if (llvm::StringRef(member_name)516.starts_with(partial_member_name)) {517if (member_name == partial_member_name) {518PrivateAutoComplete(519frame, partial_path,520prefix_path + member_name, // Anything that has been resolved521// already will be in here522member_compiler_type.GetCanonicalType(), request);523} else if (partial_path.empty()) {524request.AddCompletion((prefix_path + member_name).str());525}526}527}528}529}530531static void PrivateAutoComplete(532StackFrame *frame, llvm::StringRef partial_path,533const llvm::Twine534&prefix_path, // Anything that has been resolved already will be in here535const CompilerType &compiler_type, CompletionRequest &request) {536// printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =537// '%s'\n", prefix_path.c_str(), partial_path.c_str());538std::string remaining_partial_path;539540const lldb::TypeClass type_class = compiler_type.GetTypeClass();541if (partial_path.empty()) {542if (compiler_type.IsValid()) {543switch (type_class) {544default:545case eTypeClassArray:546case eTypeClassBlockPointer:547case eTypeClassBuiltin:548case eTypeClassComplexFloat:549case eTypeClassComplexInteger:550case eTypeClassEnumeration:551case eTypeClassFunction:552case eTypeClassMemberPointer:553case eTypeClassReference:554case eTypeClassTypedef:555case eTypeClassVector: {556request.AddCompletion(prefix_path.str());557} break;558559case eTypeClassClass:560case eTypeClassStruct:561case eTypeClassUnion:562if (prefix_path.str().back() != '.')563request.AddCompletion((prefix_path + ".").str());564break;565566case eTypeClassObjCObject:567case eTypeClassObjCInterface:568break;569case eTypeClassObjCObjectPointer:570case eTypeClassPointer: {571bool omit_empty_base_classes = true;572if (llvm::expectedToStdOptional(573compiler_type.GetNumChildren(omit_empty_base_classes, nullptr))574.value_or(0))575request.AddCompletion((prefix_path + "->").str());576else {577request.AddCompletion(prefix_path.str());578}579} break;580}581} else {582if (frame) {583const bool get_file_globals = true;584585VariableList *variable_list = frame->GetVariableList(get_file_globals,586nullptr);587588if (variable_list) {589for (const VariableSP &var_sp : *variable_list)590request.AddCompletion(var_sp->GetName().AsCString());591}592}593}594} else {595const char ch = partial_path[0];596switch (ch) {597case '*':598if (prefix_path.str().empty()) {599PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,600request);601}602break;603604case '&':605if (prefix_path.isTriviallyEmpty()) {606PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),607compiler_type, request);608}609break;610611case '-':612if (partial_path.size() > 1 && partial_path[1] == '>' &&613!prefix_path.str().empty()) {614switch (type_class) {615case lldb::eTypeClassPointer: {616CompilerType pointee_type(compiler_type.GetPointeeType());617if (partial_path.size() > 2 && partial_path[2]) {618// If there is more after the "->", then search deeper619PrivateAutoComplete(frame, partial_path.substr(2),620prefix_path + "->",621pointee_type.GetCanonicalType(), request);622} else {623// Nothing after the "->", so list all members624PrivateAutoCompleteMembers(625frame, std::string(), std::string(), prefix_path + "->",626pointee_type.GetCanonicalType(), request);627}628} break;629default:630break;631}632}633break;634635case '.':636if (compiler_type.IsValid()) {637switch (type_class) {638case lldb::eTypeClassUnion:639case lldb::eTypeClassStruct:640case lldb::eTypeClassClass:641if (partial_path.size() > 1 && partial_path[1]) {642// If there is more after the ".", then search deeper643PrivateAutoComplete(frame, partial_path.substr(1),644prefix_path + ".", compiler_type, request);645646} else {647// Nothing after the ".", so list all members648PrivateAutoCompleteMembers(frame, std::string(), partial_path,649prefix_path + ".", compiler_type,650request);651}652break;653default:654break;655}656}657break;658default:659if (isalpha(ch) || ch == '_' || ch == '$') {660const size_t partial_path_len = partial_path.size();661size_t pos = 1;662while (pos < partial_path_len) {663const char curr_ch = partial_path[pos];664if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {665++pos;666continue;667}668break;669}670671std::string token(std::string(partial_path), 0, pos);672remaining_partial_path = std::string(partial_path.substr(pos));673674if (compiler_type.IsValid()) {675PrivateAutoCompleteMembers(frame, token, remaining_partial_path,676prefix_path, compiler_type, request);677} else if (frame) {678// We haven't found our variable yet679const bool get_file_globals = true;680681VariableList *variable_list =682frame->GetVariableList(get_file_globals, nullptr);683684if (!variable_list)685break;686687for (VariableSP var_sp : *variable_list) {688689if (!var_sp)690continue;691692llvm::StringRef variable_name = var_sp->GetName().GetStringRef();693if (variable_name.starts_with(token)) {694if (variable_name == token) {695Type *variable_type = var_sp->GetType();696if (variable_type) {697CompilerType variable_compiler_type(698variable_type->GetForwardCompilerType());699PrivateAutoComplete(700frame, remaining_partial_path,701prefix_path + token, // Anything that has been resolved702// already will be in here703variable_compiler_type.GetCanonicalType(), request);704} else {705request.AddCompletion((prefix_path + variable_name).str());706}707} else if (remaining_partial_path.empty()) {708request.AddCompletion((prefix_path + variable_name).str());709}710}711}712}713}714break;715}716}717}718719void Variable::AutoComplete(const ExecutionContext &exe_ctx,720CompletionRequest &request) {721CompilerType compiler_type;722723PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),724"", compiler_type, request);725}726727728