diff options
Diffstat (limited to 'source/Symbol')
27 files changed, 20577 insertions, 0 deletions
diff --git a/source/Symbol/Block.cpp b/source/Symbol/Block.cpp new file mode 100644 index 000000000000..4ab86e54bf68 --- /dev/null +++ b/source/Symbol/Block.cpp @@ -0,0 +1,631 @@ +//===-- Block.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/Block.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +Block::Block(lldb::user_id_t uid) : + UserID(uid), + m_parent_scope (NULL), + m_children (), + m_ranges (), + m_inlineInfoSP (), + m_variable_list_sp (), + m_parsed_block_info (false), + m_parsed_block_variables (false), + m_parsed_child_blocks (false) +{ +} + +Block::~Block () +{ +} + +void +Block::GetDescription(Stream *s, Function *function, lldb::DescriptionLevel level, Target *target) const +{ + *s << "id = " << ((const UserID&)*this); + + size_t num_ranges = m_ranges.GetSize(); + if (num_ranges > 0) + { + + addr_t base_addr = LLDB_INVALID_ADDRESS; + if (target) + base_addr = function->GetAddressRange().GetBaseAddress().GetLoadAddress(target); + if (base_addr == LLDB_INVALID_ADDRESS) + base_addr = function->GetAddressRange().GetBaseAddress().GetFileAddress(); + + s->Printf(", range%s = ", num_ranges > 1 ? "s" : ""); + for (size_t i=0; i<num_ranges; ++i) + { + const Range &range = m_ranges.GetEntryRef(i); + s->AddressRange(base_addr + range.GetRangeBase(), base_addr + range.GetRangeEnd(), 4); + } + } + + if (m_inlineInfoSP.get() != NULL) + { + bool show_fullpaths = (level == eDescriptionLevelVerbose); + m_inlineInfoSP->Dump(s, show_fullpaths); + } +} + +void +Block::Dump(Stream *s, addr_t base_addr, int32_t depth, bool show_context) const +{ + if (depth < 0) + { + Block *parent = GetParent(); + if (parent) + { + // We have a depth that is less than zero, print our parent blocks + // first + parent->Dump(s, base_addr, depth + 1, show_context); + } + } + + s->Printf("%p: ", this); + s->Indent(); + *s << "Block" << ((const UserID&)*this); + const Block* parent_block = GetParent(); + if (parent_block) + { + s->Printf(", parent = {0x%8.8" PRIx64 "}", parent_block->GetID()); + } + if (m_inlineInfoSP.get() != NULL) + { + bool show_fullpaths = false; + m_inlineInfoSP->Dump(s, show_fullpaths); + } + + if (!m_ranges.IsEmpty()) + { + *s << ", ranges ="; + + size_t num_ranges = m_ranges.GetSize(); + for (size_t i=0; i<num_ranges; ++i) + { + const Range &range = m_ranges.GetEntryRef(i); + if (parent_block != NULL && parent_block->Contains(range) == false) + *s << '!'; + else + *s << ' '; + s->AddressRange(base_addr + range.GetRangeBase(), base_addr + range.GetRangeEnd(), 4); + } + } + s->EOL(); + + if (depth > 0) + { + s->IndentMore(); + + if (m_variable_list_sp.get()) + { + m_variable_list_sp->Dump(s, show_context); + } + + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + (*pos)->Dump(s, base_addr, depth - 1, show_context); + + s->IndentLess(); + } + +} + + +Block * +Block::FindBlockByID (user_id_t block_id) +{ + if (block_id == GetID()) + return this; + + Block *matching_block = NULL; + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + { + matching_block = (*pos)->FindBlockByID (block_id); + if (matching_block) + break; + } + return matching_block; +} + +void +Block::CalculateSymbolContext (SymbolContext* sc) +{ + if (m_parent_scope) + m_parent_scope->CalculateSymbolContext(sc); + sc->block = this; +} + +lldb::ModuleSP +Block::CalculateSymbolContextModule () +{ + if (m_parent_scope) + return m_parent_scope->CalculateSymbolContextModule (); + return lldb::ModuleSP(); +} + +CompileUnit * +Block::CalculateSymbolContextCompileUnit () +{ + if (m_parent_scope) + return m_parent_scope->CalculateSymbolContextCompileUnit (); + return NULL; +} + +Function * +Block::CalculateSymbolContextFunction () +{ + if (m_parent_scope) + return m_parent_scope->CalculateSymbolContextFunction (); + return NULL; +} + +Block * +Block::CalculateSymbolContextBlock () +{ + return this; +} + +void +Block::DumpSymbolContext(Stream *s) +{ + Function *function = CalculateSymbolContextFunction(); + if (function) + function->DumpSymbolContext(s); + s->Printf(", Block{0x%8.8" PRIx64 "}", GetID()); +} + +void +Block::DumpAddressRanges (Stream *s, lldb::addr_t base_addr) +{ + if (!m_ranges.IsEmpty()) + { + size_t num_ranges = m_ranges.GetSize(); + for (size_t i=0; i<num_ranges; ++i) + { + const Range &range = m_ranges.GetEntryRef(i); + s->AddressRange(base_addr + range.GetRangeBase(), base_addr + range.GetRangeEnd(), 4); + } + } +} + +bool +Block::Contains (addr_t range_offset) const +{ + return m_ranges.FindEntryThatContains(range_offset) != NULL; +} + +bool +Block::Contains (const Block *block) const +{ + if (this == block) + return false; // This block doesn't contain itself... + + // Walk the parent chain for "block" and see if any if them match this block + const Block *block_parent; + for (block_parent = block->GetParent(); + block_parent != NULL; + block_parent = block_parent->GetParent()) + { + if (this == block_parent) + return true; // One of the parents of "block" is this object! + } + return false; +} + +bool +Block::Contains (const Range& range) const +{ + return m_ranges.FindEntryThatContains (range) != NULL; +} + +Block * +Block::GetParent () const +{ + if (m_parent_scope) + return m_parent_scope->CalculateSymbolContextBlock(); + return NULL; +} + +Block * +Block::GetContainingInlinedBlock () +{ + if (GetInlinedFunctionInfo()) + return this; + return GetInlinedParent (); +} + +Block * +Block::GetInlinedParent () +{ + Block *parent_block = GetParent (); + if (parent_block) + { + if (parent_block->GetInlinedFunctionInfo()) + return parent_block; + else + return parent_block->GetInlinedParent(); + } + return NULL; +} + + +bool +Block::GetRangeContainingOffset (const addr_t offset, Range &range) +{ + const Range *range_ptr = m_ranges.FindEntryThatContains (offset); + if (range_ptr) + { + range = *range_ptr; + return true; + } + range.Clear(); + return false; +} + + +bool +Block::GetRangeContainingAddress (const Address& addr, AddressRange &range) +{ + Function *function = CalculateSymbolContextFunction(); + if (function) + { + const AddressRange &func_range = function->GetAddressRange(); + if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) + { + const addr_t addr_offset = addr.GetOffset(); + const addr_t func_offset = func_range.GetBaseAddress().GetOffset(); + if (addr_offset >= func_offset && addr_offset < func_offset + func_range.GetByteSize()) + { + addr_t offset = addr_offset - func_offset; + + const Range *range_ptr = m_ranges.FindEntryThatContains (offset); + + if (range_ptr) + { + range.GetBaseAddress() = func_range.GetBaseAddress(); + range.GetBaseAddress().SetOffset(func_offset + range_ptr->GetRangeBase()); + range.SetByteSize(range_ptr->GetByteSize()); + return true; + } + } + } + } + range.Clear(); + return false; +} + +bool +Block::GetRangeContainingLoadAddress (lldb::addr_t load_addr, Target &target, AddressRange &range) +{ + Address load_address; + load_address.SetLoadAddress(load_addr, &target); + AddressRange containing_range; + return GetRangeContainingAddress(load_address, containing_range); +} + + +uint32_t +Block::GetRangeIndexContainingAddress (const Address& addr) +{ + Function *function = CalculateSymbolContextFunction(); + if (function) + { + const AddressRange &func_range = function->GetAddressRange(); + if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) + { + const addr_t addr_offset = addr.GetOffset(); + const addr_t func_offset = func_range.GetBaseAddress().GetOffset(); + if (addr_offset >= func_offset && addr_offset < func_offset + func_range.GetByteSize()) + { + addr_t offset = addr_offset - func_offset; + return m_ranges.FindEntryIndexThatContains (offset); + } + } + } + return UINT32_MAX; +} + +bool +Block::GetRangeAtIndex (uint32_t range_idx, AddressRange &range) +{ + if (range_idx < m_ranges.GetSize()) + { + Function *function = CalculateSymbolContextFunction(); + if (function) + { + const Range &vm_range = m_ranges.GetEntryRef(range_idx); + range.GetBaseAddress() = function->GetAddressRange().GetBaseAddress(); + range.GetBaseAddress().Slide(vm_range.GetRangeBase ()); + range.SetByteSize (vm_range.GetByteSize()); + return true; + } + } + return false; +} + +bool +Block::GetStartAddress (Address &addr) +{ + if (m_ranges.IsEmpty()) + return false; + + Function *function = CalculateSymbolContextFunction(); + if (function) + { + addr = function->GetAddressRange().GetBaseAddress(); + addr.Slide(m_ranges.GetEntryRef(0).GetRangeBase ()); + return true; + } + return false; +} + +void +Block::FinalizeRanges () +{ + m_ranges.Sort(); + m_ranges.CombineConsecutiveRanges (); +} + +void +Block::AddRange (const Range& range) +{ + Block *parent_block = GetParent (); + if (parent_block && !parent_block->Contains(range)) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); + if (log) + { + ModuleSP module_sp (m_parent_scope->CalculateSymbolContextModule()); + Function *function = m_parent_scope->CalculateSymbolContextFunction(); + const addr_t function_file_addr = function->GetAddressRange().GetBaseAddress().GetFileAddress(); + const addr_t block_start_addr = function_file_addr + range.GetRangeBase (); + const addr_t block_end_addr = function_file_addr + range.GetRangeEnd (); + Type *func_type = function->GetType(); + + const Declaration &func_decl = func_type->GetDeclaration(); + if (func_decl.GetLine()) + { + log->Printf ("warning: %s:%u block {0x%8.8" PRIx64 "} has range[%u] [0x%" PRIx64 " - 0x%" PRIx64 ") which is not contained in parent block {0x%8.8" PRIx64 "} in function {0x%8.8" PRIx64 "} from %s", + func_decl.GetFile().GetPath().c_str(), + func_decl.GetLine(), + GetID(), + (uint32_t)m_ranges.GetSize(), + block_start_addr, + block_end_addr, + parent_block->GetID(), + function->GetID(), + module_sp->GetFileSpec().GetPath().c_str()); + } + else + { + log->Printf ("warning: block {0x%8.8" PRIx64 "} has range[%u] [0x%" PRIx64 " - 0x%" PRIx64 ") which is not contained in parent block {0x%8.8" PRIx64 "} in function {0x%8.8" PRIx64 "} from %s", + GetID(), + (uint32_t)m_ranges.GetSize(), + block_start_addr, + block_end_addr, + parent_block->GetID(), + function->GetID(), + module_sp->GetFileSpec().GetPath().c_str()); + } + } + parent_block->AddRange (range); + } + m_ranges.Append(range); +} + +// Return the current number of bytes that this object occupies in memory +size_t +Block::MemorySize() const +{ + size_t mem_size = sizeof(Block) + m_ranges.GetSize() * sizeof(Range); + if (m_inlineInfoSP.get()) + mem_size += m_inlineInfoSP->MemorySize(); + if (m_variable_list_sp.get()) + mem_size += m_variable_list_sp->MemorySize(); + return mem_size; + +} + +void +Block::AddChild(const BlockSP &child_block_sp) +{ + if (child_block_sp) + { + child_block_sp->SetParentScope (this); + m_children.push_back (child_block_sp); + } +} + +void +Block::SetInlinedFunctionInfo(const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr) +{ + m_inlineInfoSP.reset(new InlineFunctionInfo(name, mangled, decl_ptr, call_decl_ptr)); +} + + + +VariableListSP +Block::GetBlockVariableList (bool can_create) +{ + if (m_parsed_block_variables == false) + { + if (m_variable_list_sp.get() == NULL && can_create) + { + m_parsed_block_variables = true; + SymbolContext sc; + CalculateSymbolContext(&sc); + assert(sc.module_sp); + sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc); + } + } + return m_variable_list_sp; +} + +uint32_t +Block::AppendBlockVariables (bool can_create, + bool get_child_block_variables, + bool stop_if_child_block_is_inlined_function, + VariableList *variable_list) +{ + uint32_t num_variables_added = 0; + VariableList *block_var_list = GetBlockVariableList (can_create).get(); + if (block_var_list) + { + num_variables_added += block_var_list->GetSize(); + variable_list->AddVariables (block_var_list); + } + + if (get_child_block_variables) + { + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + { + Block *child_block = pos->get(); + if (stop_if_child_block_is_inlined_function == false || + child_block->GetInlinedFunctionInfo() == NULL) + { + num_variables_added += child_block->AppendBlockVariables (can_create, + get_child_block_variables, + stop_if_child_block_is_inlined_function, + variable_list); + } + } + } + return num_variables_added; +} + +uint32_t +Block::AppendVariables +( + bool can_create, + bool get_parent_variables, + bool stop_if_block_is_inlined_function, + VariableList *variable_list +) +{ + uint32_t num_variables_added = 0; + VariableListSP variable_list_sp(GetBlockVariableList(can_create)); + + bool is_inlined_function = GetInlinedFunctionInfo() != NULL; + if (variable_list_sp.get()) + { + num_variables_added = variable_list_sp->GetSize(); + variable_list->AddVariables(variable_list_sp.get()); + } + + if (get_parent_variables) + { + if (stop_if_block_is_inlined_function && is_inlined_function) + return num_variables_added; + + Block* parent_block = GetParent(); + if (parent_block) + num_variables_added += parent_block->AppendVariables (can_create, get_parent_variables, stop_if_block_is_inlined_function, variable_list); + } + return num_variables_added; +} + +clang::DeclContext * +Block::GetClangDeclContext() +{ + SymbolContext sc; + + CalculateSymbolContext (&sc); + + if (!sc.module_sp) + return NULL; + + SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor(); + + if (!sym_vendor) + return NULL; + + SymbolFile *sym_file = sym_vendor->GetSymbolFile(); + + if (!sym_file) + return NULL; + + return sym_file->GetClangDeclContextForTypeUID (sc, m_uid); +} + +void +Block::SetBlockInfoHasBeenParsed (bool b, bool set_children) +{ + m_parsed_block_info = b; + if (set_children) + { + m_parsed_child_blocks = true; + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + (*pos)->SetBlockInfoHasBeenParsed (b, true); + } +} + +void +Block::SetDidParseVariables (bool b, bool set_children) +{ + m_parsed_block_variables = b; + if (set_children) + { + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + (*pos)->SetDidParseVariables (b, true); + } +} + + +Block * +Block::GetSibling() const +{ + if (m_parent_scope) + { + Block *parent_block = GetParent(); + if (parent_block) + return parent_block->GetSiblingForChild (this); + } + return NULL; +} +// A parent of child blocks can be asked to find a sibling block given +// one of its child blocks +Block * +Block::GetSiblingForChild (const Block *child_block) const +{ + if (!m_children.empty()) + { + collection::const_iterator pos, end = m_children.end(); + for (pos = m_children.begin(); pos != end; ++pos) + { + if (pos->get() == child_block) + { + if (++pos != end) + return pos->get(); + break; + } + } + } + return NULL; +} + diff --git a/source/Symbol/ClangASTContext.cpp b/source/Symbol/ClangASTContext.cpp new file mode 100644 index 000000000000..5ba9c6a8d796 --- /dev/null +++ b/source/Symbol/ClangASTContext.cpp @@ -0,0 +1,2291 @@ +//===-- ClangASTContext.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/ClangASTContext.h" + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes + +// Clang headers like to use NDEBUG inside of them to enable/disable debug +// releated features using "#ifndef NDEBUG" preprocessor blocks to do one thing +// or another. This is bad because it means that if clang was built in release +// mode, it assumes that you are building in release mode which is not always +// the case. You can end up with functions that are defined as empty in header +// files when NDEBUG is not defined, and this can cause link errors with the +// clang .a files that you have since you might be missing functions in the .a +// file. So we have to define NDEBUG when including clang headers to avoid any +// mismatches. This is covered by rdar://problem/8691220 + +#if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF) +#define LLDB_DEFINED_NDEBUG_FOR_CLANG +#define NDEBUG +// Need to include assert.h so it is as clang would expect it to be (disabled) +#include <assert.h> +#endif + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/LangStandard.h" + +#ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG +#undef NDEBUG +#undef LLDB_DEFINED_NDEBUG_FOR_CLANG +// Need to re-include assert.h so it is as _we_ would expect it to be (enabled) +#include <assert.h> +#endif + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/VerifyDecl.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +#include <stdio.h> + +#include <mutex> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; +using namespace clang; + +clang::AccessSpecifier +ClangASTContext::ConvertAccessTypeToAccessSpecifier (AccessType access) +{ + switch (access) + { + default: break; + case eAccessNone: return AS_none; + case eAccessPublic: return AS_public; + case eAccessPrivate: return AS_private; + case eAccessProtected: return AS_protected; + } + return AS_none; +} + + +static void +ParseLangArgs +( + LangOptions &Opts, + InputKind IK +) +{ + // FIXME: Cleanup per-file based stuff. + + // Set some properties which depend soley on the input kind; it would be nice + // to move these to the language standard, and have the driver resolve the + // input kind + language standard. + if (IK == IK_Asm) { + Opts.AsmPreprocessor = 1; + } else if (IK == IK_ObjC || + IK == IK_ObjCXX || + IK == IK_PreprocessedObjC || + IK == IK_PreprocessedObjCXX) { + Opts.ObjC1 = Opts.ObjC2 = 1; + } + + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK) { + case IK_None: + case IK_AST: + case IK_LLVM_IR: + assert (!"Invalid input kind!"); + case IK_OpenCL: + LangStd = LangStandard::lang_opencl; + break; + case IK_CUDA: + LangStd = LangStandard::lang_cuda; + break; + case IK_Asm: + case IK_C: + case IK_PreprocessedC: + case IK_ObjC: + case IK_PreprocessedObjC: + LangStd = LangStandard::lang_gnu99; + break; + case IK_CXX: + case IK_PreprocessedCXX: + case IK_ObjCXX: + case IK_PreprocessedObjCXX: + LangStd = LangStandard::lang_gnucxx98; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.LineComment = Std.hasLineComments(); + Opts.C99 = Std.isC99(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus11 = Std.isCPlusPlus11(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Std.isC99(); + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + Opts.WChar = true; + + // OpenCL has some additional defaults. + if (LangStd == LangStandard::lang_opencl) { + Opts.OpenCL = 1; + Opts.AltiVec = 1; + Opts.CXXOperatorNames = 1; + Opts.LaxVectorConversions = 1; + } + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + +// if (Opts.CPlusPlus) +// Opts.CXXOperatorNames = !Args.hasArg(OPT_fno_operator_names); +// +// if (Args.hasArg(OPT_fobjc_gc_only)) +// Opts.setGCMode(LangOptions::GCOnly); +// else if (Args.hasArg(OPT_fobjc_gc)) +// Opts.setGCMode(LangOptions::HybridGC); +// +// if (Args.hasArg(OPT_print_ivar_layout)) +// Opts.ObjCGCBitmapPrint = 1; +// +// if (Args.hasArg(OPT_faltivec)) +// Opts.AltiVec = 1; +// +// if (Args.hasArg(OPT_pthread)) +// Opts.POSIXThreads = 1; +// +// llvm::StringRef Vis = getLastArgValue(Args, OPT_fvisibility, +// "default"); +// if (Vis == "default") + Opts.setValueVisibilityMode(DefaultVisibility); +// else if (Vis == "hidden") +// Opts.setVisibilityMode(LangOptions::Hidden); +// else if (Vis == "protected") +// Opts.setVisibilityMode(LangOptions::Protected); +// else +// Diags.Report(diag::err_drv_invalid_value) +// << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; + +// Opts.OverflowChecking = Args.hasArg(OPT_ftrapv); + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + Opts.Trigraphs = !Opts.GNUMode; +// if (Args.hasArg(OPT_trigraphs)) +// Opts.Trigraphs = 1; +// +// Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, +// OPT_fno_dollars_in_identifiers, +// !Opts.AsmPreprocessor); +// Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); +// Opts.Microsoft = Args.hasArg(OPT_fms_extensions); +// Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); +// if (Args.hasArg(OPT_fno_lax_vector_conversions)) +// Opts.LaxVectorConversions = 0; +// Opts.Exceptions = Args.hasArg(OPT_fexceptions); +// Opts.RTTI = !Args.hasArg(OPT_fno_rtti); +// Opts.Blocks = Args.hasArg(OPT_fblocks); +// Opts.CharIsSigned = !Args.hasArg(OPT_fno_signed_char); +// Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); +// Opts.Freestanding = Args.hasArg(OPT_ffreestanding); +// Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; +// Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); +// Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); +// Opts.AccessControl = Args.hasArg(OPT_faccess_control); +// Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); +// Opts.MathErrno = !Args.hasArg(OPT_fno_math_errno); +// Opts.InstantiationDepth = getLastArgIntValue(Args, OPT_ftemplate_depth, 99, +// Diags); +// Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); +// Opts.ObjCConstantStringClass = getLastArgValue(Args, +// OPT_fconstant_string_class); +// Opts.ObjCNonFragileABI = Args.hasArg(OPT_fobjc_nonfragile_abi); +// Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); +// Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); +// Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); +// Opts.Static = Args.hasArg(OPT_static_define); + Opts.OptimizeSize = 0; + + // FIXME: Eliminate this dependency. +// unsigned Opt = +// Args.hasArg(OPT_Os) ? 2 : getLastArgIntValue(Args, OPT_O, 0, Diags); +// Opts.Optimize = Opt != 0; + unsigned Opt = 0; + + // This is the __NO_INLINE__ define, which just depends on things like the + // optimization level and -fno-inline, not actually whether the backend has + // inlining enabled. + // + // FIXME: This is affected by other options (-fno-inline). + Opts.NoInlineDefine = !Opt; + +// unsigned SSP = getLastArgIntValue(Args, OPT_stack_protector, 0, Diags); +// switch (SSP) { +// default: +// Diags.Report(diag::err_drv_invalid_value) +// << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << SSP; +// break; +// case 0: Opts.setStackProtectorMode(LangOptions::SSPOff); break; +// case 1: Opts.setStackProtectorMode(LangOptions::SSPOn); break; +// case 2: Opts.setStackProtectorMode(LangOptions::SSPReq); break; +// } +} + + +ClangASTContext::ClangASTContext (const char *target_triple) : + m_target_triple(), + m_ast_ap(), + m_language_options_ap(), + m_source_manager_ap(), + m_diagnostics_engine_ap(), + m_target_options_rp(), + m_target_info_ap(), + m_identifier_table_ap(), + m_selector_table_ap(), + m_builtins_ap(), + m_callback_tag_decl (NULL), + m_callback_objc_decl (NULL), + m_callback_baton (NULL), + m_pointer_byte_size (0) + +{ + if (target_triple && target_triple[0]) + SetTargetTriple (target_triple); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangASTContext::~ClangASTContext() +{ + m_builtins_ap.reset(); + m_selector_table_ap.reset(); + m_identifier_table_ap.reset(); + m_target_info_ap.reset(); + m_target_options_rp.reset(); + m_diagnostics_engine_ap.reset(); + m_source_manager_ap.reset(); + m_language_options_ap.reset(); + m_ast_ap.reset(); +} + + +void +ClangASTContext::Clear() +{ + m_ast_ap.reset(); + m_language_options_ap.reset(); + m_source_manager_ap.reset(); + m_diagnostics_engine_ap.reset(); + m_target_options_rp.reset(); + m_target_info_ap.reset(); + m_identifier_table_ap.reset(); + m_selector_table_ap.reset(); + m_builtins_ap.reset(); + m_pointer_byte_size = 0; +} + +const char * +ClangASTContext::GetTargetTriple () +{ + return m_target_triple.c_str(); +} + +void +ClangASTContext::SetTargetTriple (const char *target_triple) +{ + Clear(); + m_target_triple.assign(target_triple); +} + +void +ClangASTContext::SetArchitecture (const ArchSpec &arch) +{ + SetTargetTriple(arch.GetTriple().str().c_str()); +} + +bool +ClangASTContext::HasExternalSource () +{ + ASTContext *ast = getASTContext(); + if (ast) + return ast->getExternalSource () != NULL; + return false; +} + +void +ClangASTContext::SetExternalSource (llvm::OwningPtr<ExternalASTSource> &ast_source_ap) +{ + ASTContext *ast = getASTContext(); + if (ast) + { + ast->setExternalSource (ast_source_ap); + ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(true); + //ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(true); + } +} + +void +ClangASTContext::RemoveExternalSource () +{ + ASTContext *ast = getASTContext(); + + if (ast) + { + llvm::OwningPtr<ExternalASTSource> empty_ast_source_ap; + ast->setExternalSource (empty_ast_source_ap); + ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(false); + //ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(false); + } +} + + + +ASTContext * +ClangASTContext::getASTContext() +{ + if (m_ast_ap.get() == NULL) + { + m_ast_ap.reset(new ASTContext (*getLanguageOptions(), + *getSourceManager(), + getTargetInfo(), + *getIdentifierTable(), + *getSelectorTable(), + *getBuiltinContext(), + 0)); + + if ((m_callback_tag_decl || m_callback_objc_decl) && m_callback_baton) + { + m_ast_ap->getTranslationUnitDecl()->setHasExternalLexicalStorage(); + //m_ast_ap->getTranslationUnitDecl()->setHasExternalVisibleStorage(); + } + + m_ast_ap->getDiagnostics().setClient(getDiagnosticConsumer(), false); + } + return m_ast_ap.get(); +} + +Builtin::Context * +ClangASTContext::getBuiltinContext() +{ + if (m_builtins_ap.get() == NULL) + m_builtins_ap.reset (new Builtin::Context()); + return m_builtins_ap.get(); +} + +IdentifierTable * +ClangASTContext::getIdentifierTable() +{ + if (m_identifier_table_ap.get() == NULL) + m_identifier_table_ap.reset(new IdentifierTable (*ClangASTContext::getLanguageOptions(), NULL)); + return m_identifier_table_ap.get(); +} + +LangOptions * +ClangASTContext::getLanguageOptions() +{ + if (m_language_options_ap.get() == NULL) + { + m_language_options_ap.reset(new LangOptions()); + ParseLangArgs(*m_language_options_ap, IK_ObjCXX); +// InitializeLangOptions(*m_language_options_ap, IK_ObjCXX); + } + return m_language_options_ap.get(); +} + +SelectorTable * +ClangASTContext::getSelectorTable() +{ + if (m_selector_table_ap.get() == NULL) + m_selector_table_ap.reset (new SelectorTable()); + return m_selector_table_ap.get(); +} + +clang::FileManager * +ClangASTContext::getFileManager() +{ + if (m_file_manager_ap.get() == NULL) + { + clang::FileSystemOptions file_system_options; + m_file_manager_ap.reset(new clang::FileManager(file_system_options)); + } + return m_file_manager_ap.get(); +} + +clang::SourceManager * +ClangASTContext::getSourceManager() +{ + if (m_source_manager_ap.get() == NULL) + m_source_manager_ap.reset(new clang::SourceManager(*getDiagnosticsEngine(), *getFileManager())); + return m_source_manager_ap.get(); +} + +clang::DiagnosticsEngine * +ClangASTContext::getDiagnosticsEngine() +{ + if (m_diagnostics_engine_ap.get() == NULL) + { + llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_id_sp(new DiagnosticIDs()); + m_diagnostics_engine_ap.reset(new DiagnosticsEngine(diag_id_sp, new DiagnosticOptions())); + } + return m_diagnostics_engine_ap.get(); +} + +class NullDiagnosticConsumer : public DiagnosticConsumer +{ +public: + NullDiagnosticConsumer () + { + m_log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS); + } + + void HandleDiagnostic (DiagnosticsEngine::Level DiagLevel, const Diagnostic &info) + { + if (m_log) + { + llvm::SmallVector<char, 32> diag_str(10); + info.FormatDiagnostic(diag_str); + diag_str.push_back('\0'); + m_log->Printf("Compiler diagnostic: %s\n", diag_str.data()); + } + } + + DiagnosticConsumer *clone (DiagnosticsEngine &Diags) const + { + return new NullDiagnosticConsumer (); + } +private: + Log * m_log; +}; + +DiagnosticConsumer * +ClangASTContext::getDiagnosticConsumer() +{ + if (m_diagnostic_consumer_ap.get() == NULL) + m_diagnostic_consumer_ap.reset(new NullDiagnosticConsumer); + + return m_diagnostic_consumer_ap.get(); +} + +TargetOptions * +ClangASTContext::getTargetOptions() +{ + if (m_target_options_rp.getPtr() == NULL && !m_target_triple.empty()) + { + m_target_options_rp.reset (); + m_target_options_rp = new TargetOptions(); + if (m_target_options_rp.getPtr() != NULL) + m_target_options_rp->Triple = m_target_triple; + } + return m_target_options_rp.getPtr(); +} + + +TargetInfo * +ClangASTContext::getTargetInfo() +{ + // target_triple should be something like "x86_64-apple-macosx" + if (m_target_info_ap.get() == NULL && !m_target_triple.empty()) + m_target_info_ap.reset (TargetInfo::CreateTargetInfo(*getDiagnosticsEngine(), getTargetOptions())); + return m_target_info_ap.get(); +} + +#pragma mark Basic Types + +static inline bool +QualTypeMatchesBitSize(const uint64_t bit_size, ASTContext *ast, QualType qual_type) +{ + uint64_t qual_type_bit_size = ast->getTypeSize(qual_type); + if (qual_type_bit_size == bit_size) + return true; + return false; +} +ClangASTType +ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (Encoding encoding, uint32_t bit_size) +{ + return ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (getASTContext(), encoding, bit_size); +} + +ClangASTType +ClangASTContext::GetBuiltinTypeForEncodingAndBitSize (ASTContext *ast, Encoding encoding, uint32_t bit_size) +{ + if (!ast) + return ClangASTType(); + + switch (encoding) + { + case eEncodingInvalid: + if (QualTypeMatchesBitSize (bit_size, ast, ast->VoidPtrTy)) + return ClangASTType (ast, ast->VoidPtrTy.getAsOpaquePtr()); + break; + + case eEncodingUint: + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedCharTy)) + return ClangASTType (ast, ast->UnsignedCharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedShortTy)) + return ClangASTType (ast, ast->UnsignedShortTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedIntTy)) + return ClangASTType (ast, ast->UnsignedIntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongTy)) + return ClangASTType (ast, ast->UnsignedLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongLongTy)) + return ClangASTType (ast, ast->UnsignedLongLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedInt128Ty)) + return ClangASTType (ast, ast->UnsignedInt128Ty.getAsOpaquePtr()); + break; + + case eEncodingSint: + if (QualTypeMatchesBitSize (bit_size, ast, ast->CharTy)) + return ClangASTType (ast, ast->CharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->ShortTy)) + return ClangASTType (ast, ast->ShortTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->IntTy)) + return ClangASTType (ast, ast->IntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongTy)) + return ClangASTType (ast, ast->LongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongLongTy)) + return ClangASTType (ast, ast->LongLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->Int128Ty)) + return ClangASTType (ast, ast->Int128Ty.getAsOpaquePtr()); + break; + + case eEncodingIEEE754: + if (QualTypeMatchesBitSize (bit_size, ast, ast->FloatTy)) + return ClangASTType (ast, ast->FloatTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->DoubleTy)) + return ClangASTType (ast, ast->DoubleTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongDoubleTy)) + return ClangASTType (ast, ast->LongDoubleTy.getAsOpaquePtr()); + break; + + case eEncodingVector: + // Sanity check that bit_size is a multiple of 8's. + if (bit_size && !(bit_size & 0x7u)) + return ClangASTType (ast, ast->getExtVectorType (ast->UnsignedCharTy, bit_size/8).getAsOpaquePtr()); + break; + } + + return ClangASTType(); +} + + + +lldb::BasicType +ClangASTContext::GetBasicTypeEnumeration (const ConstString &name) +{ + if (name) + { + typedef UniqueCStringMap<lldb::BasicType> TypeNameToBasicTypeMap; + static TypeNameToBasicTypeMap g_type_map; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, [](){ + // "void" + g_type_map.Append(ConstString("void").GetCString(), eBasicTypeVoid); + + // "char" + g_type_map.Append(ConstString("char").GetCString(), eBasicTypeChar); + g_type_map.Append(ConstString("signed char").GetCString(), eBasicTypeSignedChar); + g_type_map.Append(ConstString("unsigned char").GetCString(), eBasicTypeUnsignedChar); + g_type_map.Append(ConstString("wchar_t").GetCString(), eBasicTypeWChar); + g_type_map.Append(ConstString("signed wchar_t").GetCString(), eBasicTypeSignedWChar); + g_type_map.Append(ConstString("unsigned wchar_t").GetCString(), eBasicTypeUnsignedWChar); + // "short" + g_type_map.Append(ConstString("short").GetCString(), eBasicTypeShort); + g_type_map.Append(ConstString("short int").GetCString(), eBasicTypeShort); + g_type_map.Append(ConstString("unsigned short").GetCString(), eBasicTypeUnsignedShort); + g_type_map.Append(ConstString("unsigned short int").GetCString(), eBasicTypeUnsignedShort); + + // "int" + g_type_map.Append(ConstString("int").GetCString(), eBasicTypeInt); + g_type_map.Append(ConstString("signed int").GetCString(), eBasicTypeInt); + g_type_map.Append(ConstString("unsigned int").GetCString(), eBasicTypeUnsignedInt); + g_type_map.Append(ConstString("unsigned").GetCString(), eBasicTypeUnsignedInt); + + // "long" + g_type_map.Append(ConstString("long").GetCString(), eBasicTypeLong); + g_type_map.Append(ConstString("long int").GetCString(), eBasicTypeLong); + g_type_map.Append(ConstString("unsigned long").GetCString(), eBasicTypeUnsignedLong); + g_type_map.Append(ConstString("unsigned long int").GetCString(), eBasicTypeUnsignedLong); + + // "long long" + g_type_map.Append(ConstString("long long").GetCString(), eBasicTypeLongLong); + g_type_map.Append(ConstString("long long int").GetCString(), eBasicTypeLongLong); + g_type_map.Append(ConstString("unsigned long long").GetCString(), eBasicTypeUnsignedLongLong); + g_type_map.Append(ConstString("unsigned long long int").GetCString(), eBasicTypeUnsignedLongLong); + + // "int128" + g_type_map.Append(ConstString("__int128_t").GetCString(), eBasicTypeInt128); + g_type_map.Append(ConstString("__uint128_t").GetCString(), eBasicTypeUnsignedInt128); + + // Miscelaneous + g_type_map.Append(ConstString("bool").GetCString(), eBasicTypeBool); + g_type_map.Append(ConstString("float").GetCString(), eBasicTypeFloat); + g_type_map.Append(ConstString("double").GetCString(), eBasicTypeDouble); + g_type_map.Append(ConstString("long double").GetCString(), eBasicTypeLongDouble); + g_type_map.Append(ConstString("id").GetCString(), eBasicTypeObjCID); + g_type_map.Append(ConstString("SEL").GetCString(), eBasicTypeObjCSel); + g_type_map.Append(ConstString("nullptr").GetCString(), eBasicTypeNullPtr); + g_type_map.Sort(); + }); + + return g_type_map.Find(name.GetCString(), eBasicTypeInvalid); + } + return eBasicTypeInvalid; +} + +ClangASTType +ClangASTContext::GetBasicType (ASTContext *ast, const ConstString &name) +{ + if (ast) + { + lldb::BasicType basic_type = ClangASTContext::GetBasicTypeEnumeration (name); + return ClangASTContext::GetBasicType (ast, basic_type); + } + return ClangASTType(); +} + +uint32_t +ClangASTContext::GetPointerByteSize () +{ + if (m_pointer_byte_size == 0) + m_pointer_byte_size = GetBasicType(lldb::eBasicTypeVoid).GetPointerType().GetByteSize(); + return m_pointer_byte_size; +} + +ClangASTType +ClangASTContext::GetBasicType (lldb::BasicType basic_type) +{ + return GetBasicType (getASTContext(), basic_type); +} + +ClangASTType +ClangASTContext::GetBasicType (ASTContext *ast, lldb::BasicType basic_type) +{ + if (ast) + { + clang_type_t clang_type = NULL; + + switch (basic_type) + { + case eBasicTypeInvalid: + case eBasicTypeOther: + break; + case eBasicTypeVoid: + clang_type = ast->VoidTy.getAsOpaquePtr(); + break; + case eBasicTypeChar: + clang_type = ast->CharTy.getAsOpaquePtr(); + break; + case eBasicTypeSignedChar: + clang_type = ast->SignedCharTy.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedChar: + clang_type = ast->UnsignedCharTy.getAsOpaquePtr(); + break; + case eBasicTypeWChar: + clang_type = ast->getWCharType().getAsOpaquePtr(); + break; + case eBasicTypeSignedWChar: + clang_type = ast->getSignedWCharType().getAsOpaquePtr(); + break; + case eBasicTypeUnsignedWChar: + clang_type = ast->getUnsignedWCharType().getAsOpaquePtr(); + break; + case eBasicTypeChar16: + clang_type = ast->Char16Ty.getAsOpaquePtr(); + break; + case eBasicTypeChar32: + clang_type = ast->Char32Ty.getAsOpaquePtr(); + break; + case eBasicTypeShort: + clang_type = ast->ShortTy.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedShort: + clang_type = ast->UnsignedShortTy.getAsOpaquePtr(); + break; + case eBasicTypeInt: + clang_type = ast->IntTy.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedInt: + clang_type = ast->UnsignedIntTy.getAsOpaquePtr(); + break; + case eBasicTypeLong: + clang_type = ast->LongTy.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedLong: + clang_type = ast->UnsignedLongTy.getAsOpaquePtr(); + break; + case eBasicTypeLongLong: + clang_type = ast->LongLongTy.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedLongLong: + clang_type = ast->UnsignedLongLongTy.getAsOpaquePtr(); + break; + case eBasicTypeInt128: + clang_type = ast->Int128Ty.getAsOpaquePtr(); + break; + case eBasicTypeUnsignedInt128: + clang_type = ast->UnsignedInt128Ty.getAsOpaquePtr(); + break; + case eBasicTypeBool: + clang_type = ast->BoolTy.getAsOpaquePtr(); + break; + case eBasicTypeHalf: + clang_type = ast->HalfTy.getAsOpaquePtr(); + break; + case eBasicTypeFloat: + clang_type = ast->FloatTy.getAsOpaquePtr(); + break; + case eBasicTypeDouble: + clang_type = ast->DoubleTy.getAsOpaquePtr(); + break; + case eBasicTypeLongDouble: + clang_type = ast->LongDoubleTy.getAsOpaquePtr(); + break; + case eBasicTypeFloatComplex: + clang_type = ast->FloatComplexTy.getAsOpaquePtr(); + break; + case eBasicTypeDoubleComplex: + clang_type = ast->DoubleComplexTy.getAsOpaquePtr(); + break; + case eBasicTypeLongDoubleComplex: + clang_type = ast->LongDoubleComplexTy.getAsOpaquePtr(); + break; + case eBasicTypeObjCID: + clang_type = ast->getObjCIdType().getAsOpaquePtr(); + break; + case eBasicTypeObjCClass: + clang_type = ast->getObjCClassType().getAsOpaquePtr(); + break; + case eBasicTypeObjCSel: + clang_type = ast->getObjCSelType().getAsOpaquePtr(); + break; + case eBasicTypeNullPtr: + clang_type = ast->NullPtrTy.getAsOpaquePtr(); + break; + } + + if (clang_type) + return ClangASTType (ast, clang_type); + } + return ClangASTType(); +} + + +ClangASTType +ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize (const char *type_name, uint32_t dw_ate, uint32_t bit_size) +{ + ASTContext *ast = getASTContext(); + +#define streq(a,b) strcmp(a,b) == 0 + assert (ast != NULL); + if (ast) + { + switch (dw_ate) + { + default: + break; + + case DW_ATE_address: + if (QualTypeMatchesBitSize (bit_size, ast, ast->VoidPtrTy)) + return ClangASTType (ast, ast->VoidPtrTy.getAsOpaquePtr()); + break; + + case DW_ATE_boolean: + if (QualTypeMatchesBitSize (bit_size, ast, ast->BoolTy)) + return ClangASTType (ast, ast->BoolTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedCharTy)) + return ClangASTType (ast, ast->UnsignedCharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedShortTy)) + return ClangASTType (ast, ast->UnsignedShortTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedIntTy)) + return ClangASTType (ast, ast->UnsignedIntTy.getAsOpaquePtr()); + break; + + case DW_ATE_lo_user: + // This has been seen to mean DW_AT_complex_integer + if (type_name) + { + if (::strstr(type_name, "complex")) + { + ClangASTType complex_int_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize ("int", DW_ATE_signed, bit_size/2); + return ClangASTType (ast, ast->getComplexType (complex_int_clang_type.GetQualType()).getAsOpaquePtr()); + } + } + break; + + case DW_ATE_complex_float: + if (QualTypeMatchesBitSize (bit_size, ast, ast->FloatComplexTy)) + return ClangASTType (ast, ast->FloatComplexTy.getAsOpaquePtr()); + else if (QualTypeMatchesBitSize (bit_size, ast, ast->DoubleComplexTy)) + return ClangASTType (ast, ast->DoubleComplexTy.getAsOpaquePtr()); + else if (QualTypeMatchesBitSize (bit_size, ast, ast->LongDoubleComplexTy)) + return ClangASTType (ast, ast->LongDoubleComplexTy.getAsOpaquePtr()); + else + { + ClangASTType complex_float_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize ("float", DW_ATE_float, bit_size/2); + return ClangASTType (ast, ast->getComplexType (complex_float_clang_type.GetQualType()).getAsOpaquePtr()); + } + break; + + case DW_ATE_float: + if (QualTypeMatchesBitSize (bit_size, ast, ast->FloatTy)) + return ClangASTType (ast, ast->FloatTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->DoubleTy)) + return ClangASTType (ast, ast->DoubleTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongDoubleTy)) + return ClangASTType (ast, ast->LongDoubleTy.getAsOpaquePtr()); + break; + + case DW_ATE_signed: + if (type_name) + { + if (streq(type_name, "wchar_t") && + QualTypeMatchesBitSize (bit_size, ast, ast->WCharTy)) + return ClangASTType (ast, ast->WCharTy.getAsOpaquePtr()); + if (streq(type_name, "void") && + QualTypeMatchesBitSize (bit_size, ast, ast->VoidTy)) + return ClangASTType (ast, ast->VoidTy.getAsOpaquePtr()); + if (strstr(type_name, "long long") && + QualTypeMatchesBitSize (bit_size, ast, ast->LongLongTy)) + return ClangASTType (ast, ast->LongLongTy.getAsOpaquePtr()); + if (strstr(type_name, "long") && + QualTypeMatchesBitSize (bit_size, ast, ast->LongTy)) + return ClangASTType (ast, ast->LongTy.getAsOpaquePtr()); + if (strstr(type_name, "short") && + QualTypeMatchesBitSize (bit_size, ast, ast->ShortTy)) + return ClangASTType (ast, ast->ShortTy.getAsOpaquePtr()); + if (strstr(type_name, "char")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->CharTy)) + return ClangASTType (ast, ast->CharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->SignedCharTy)) + return ClangASTType (ast, ast->SignedCharTy.getAsOpaquePtr()); + } + if (strstr(type_name, "int")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->IntTy)) + return ClangASTType (ast, ast->IntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->Int128Ty)) + return ClangASTType (ast, ast->Int128Ty.getAsOpaquePtr()); + } + } + // We weren't able to match up a type name, just search by size + if (QualTypeMatchesBitSize (bit_size, ast, ast->CharTy)) + return ClangASTType (ast, ast->CharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->ShortTy)) + return ClangASTType (ast, ast->ShortTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->IntTy)) + return ClangASTType (ast, ast->IntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongTy)) + return ClangASTType (ast, ast->LongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->LongLongTy)) + return ClangASTType (ast, ast->LongLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->Int128Ty)) + return ClangASTType (ast, ast->Int128Ty.getAsOpaquePtr()); + break; + + case DW_ATE_signed_char: + if (type_name) + { + if (streq(type_name, "signed char")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->SignedCharTy)) + return ClangASTType (ast, ast->SignedCharTy.getAsOpaquePtr()); + } + } + if (QualTypeMatchesBitSize (bit_size, ast, ast->CharTy)) + return ClangASTType (ast, ast->CharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->SignedCharTy)) + return ClangASTType (ast, ast->SignedCharTy.getAsOpaquePtr()); + break; + + case DW_ATE_unsigned: + if (type_name) + { + if (strstr(type_name, "long long")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongLongTy)) + return ClangASTType (ast, ast->UnsignedLongLongTy.getAsOpaquePtr()); + } + else if (strstr(type_name, "long")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongTy)) + return ClangASTType (ast, ast->UnsignedLongTy.getAsOpaquePtr()); + } + else if (strstr(type_name, "short")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedShortTy)) + return ClangASTType (ast, ast->UnsignedShortTy.getAsOpaquePtr()); + } + else if (strstr(type_name, "char")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedCharTy)) + return ClangASTType (ast, ast->UnsignedCharTy.getAsOpaquePtr()); + } + else if (strstr(type_name, "int")) + { + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedIntTy)) + return ClangASTType (ast, ast->UnsignedIntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedInt128Ty)) + return ClangASTType (ast, ast->UnsignedInt128Ty.getAsOpaquePtr()); + } + } + // We weren't able to match up a type name, just search by size + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedCharTy)) + return ClangASTType (ast, ast->UnsignedCharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedShortTy)) + return ClangASTType (ast, ast->UnsignedShortTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedIntTy)) + return ClangASTType (ast, ast->UnsignedIntTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongTy)) + return ClangASTType (ast, ast->UnsignedLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedLongLongTy)) + return ClangASTType (ast, ast->UnsignedLongLongTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedInt128Ty)) + return ClangASTType (ast, ast->UnsignedInt128Ty.getAsOpaquePtr()); + break; + + case DW_ATE_unsigned_char: + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedCharTy)) + return ClangASTType (ast, ast->UnsignedCharTy.getAsOpaquePtr()); + if (QualTypeMatchesBitSize (bit_size, ast, ast->UnsignedShortTy)) + return ClangASTType (ast, ast->UnsignedShortTy.getAsOpaquePtr()); + break; + + case DW_ATE_imaginary_float: + break; + + case DW_ATE_UTF: + if (type_name) + { + if (streq(type_name, "char16_t")) + { + return ClangASTType (ast, ast->Char16Ty.getAsOpaquePtr()); + } + else if (streq(type_name, "char32_t")) + { + return ClangASTType (ast, ast->Char32Ty.getAsOpaquePtr()); + } + } + break; + } + } + // This assert should fire for anything that we don't catch above so we know + // to fix any issues we run into. + if (type_name) + { + Host::SystemLog (Host::eSystemLogError, "error: need to add support for DW_TAG_base_type '%s' encoded with DW_ATE = 0x%x, bit_size = %u\n", type_name, dw_ate, bit_size); + } + else + { + Host::SystemLog (Host::eSystemLogError, "error: need to add support for DW_TAG_base_type encoded with DW_ATE = 0x%x, bit_size = %u\n", dw_ate, bit_size); + } + return ClangASTType (); +} + +ClangASTType +ClangASTContext::GetUnknownAnyType(clang::ASTContext *ast) +{ + if (ast) + return ClangASTType (ast, ast->UnknownAnyTy.getAsOpaquePtr()); + return ClangASTType(); +} + +ClangASTType +ClangASTContext::GetCStringType (bool is_const) +{ + ASTContext *ast = getASTContext(); + QualType char_type(ast->CharTy); + + if (is_const) + char_type.addConst(); + + return ClangASTType (ast, ast->getPointerType(char_type).getAsOpaquePtr()); +} + +clang::DeclContext * +ClangASTContext::GetTranslationUnitDecl (clang::ASTContext *ast) +{ + return ast->getTranslationUnitDecl(); +} + +ClangASTType +ClangASTContext::CopyType (ASTContext *dst_ast, + ClangASTType src) +{ + FileSystemOptions file_system_options; + ASTContext *src_ast = src.GetASTContext(); + FileManager file_manager (file_system_options); + ASTImporter importer(*dst_ast, file_manager, + *src_ast, file_manager, + false); + + QualType dst (importer.Import(src.GetQualType())); + + return ClangASTType (dst_ast, dst.getAsOpaquePtr()); +} + + +clang::Decl * +ClangASTContext::CopyDecl (ASTContext *dst_ast, + ASTContext *src_ast, + clang::Decl *source_decl) +{ + FileSystemOptions file_system_options; + FileManager file_manager (file_system_options); + ASTImporter importer(*dst_ast, file_manager, + *src_ast, file_manager, + false); + + return importer.Import(source_decl); +} + +bool +ClangASTContext::AreTypesSame (ClangASTType type1, + ClangASTType type2, + bool ignore_qualifiers) +{ + ASTContext *ast = type1.GetASTContext(); + if (ast != type2.GetASTContext()) + return false; + + if (type1.GetOpaqueQualType() == type2.GetOpaqueQualType()) + return true; + + QualType type1_qual = type1.GetQualType(); + QualType type2_qual = type2.GetQualType(); + + if (ignore_qualifiers) + { + type1_qual = type1_qual.getUnqualifiedType(); + type2_qual = type2_qual.getUnqualifiedType(); + } + + return ast->hasSameType (type1_qual, type2_qual); +} + + +ClangASTType +ClangASTContext::GetTypeForDecl (TagDecl *decl) +{ + // No need to call the getASTContext() accessor (which can create the AST + // if it isn't created yet, because we can't have created a decl in this + // AST if our AST didn't already exist... + ASTContext *ast = m_ast_ap.get(); + if (ast) + return ClangASTType (ast, ast->getTagDeclType(decl).getAsOpaquePtr()); + return ClangASTType(); +} + +ClangASTType +ClangASTContext::GetTypeForDecl (ObjCInterfaceDecl *decl) +{ + // No need to call the getASTContext() accessor (which can create the AST + // if it isn't created yet, because we can't have created a decl in this + // AST if our AST didn't already exist... + ASTContext *ast = m_ast_ap.get(); + if (ast) + return ClangASTType (ast, ast->getObjCInterfaceType(decl).getAsOpaquePtr()); + return ClangASTType(); +} + +#pragma mark Structure, Unions, Classes + +ClangASTType +ClangASTContext::CreateRecordType (DeclContext *decl_ctx, + AccessType access_type, + const char *name, + int kind, + LanguageType language, + ClangASTMetadata *metadata) +{ + ASTContext *ast = getASTContext(); + assert (ast != NULL); + + if (decl_ctx == NULL) + decl_ctx = ast->getTranslationUnitDecl(); + + + if (language == eLanguageTypeObjC || language == eLanguageTypeObjC_plus_plus) + { + bool isForwardDecl = true; + bool isInternal = false; + return CreateObjCClass (name, decl_ctx, isForwardDecl, isInternal, metadata); + } + + // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and + // we will need to update this code. I was told to currently always use + // the CXXRecordDecl class since we often don't know from debug information + // if something is struct or a class, so we default to always use the more + // complete definition just in case. + CXXRecordDecl *decl = CXXRecordDecl::Create (*ast, + (TagDecl::TagKind)kind, + decl_ctx, + SourceLocation(), + SourceLocation(), + name && name[0] ? &ast->Idents.get(name) : NULL); + + if (decl) + { + if (metadata) + SetMetadata(ast, decl, *metadata); + + if (access_type != eAccessNone) + decl->setAccess (ConvertAccessTypeToAccessSpecifier (access_type)); + + if (decl_ctx) + decl_ctx->addDecl (decl); + + return ClangASTType(ast, ast->getTagDeclType(decl).getAsOpaquePtr()); + } + return ClangASTType(); +} + +static TemplateParameterList * +CreateTemplateParameterList (ASTContext *ast, + const ClangASTContext::TemplateParameterInfos &template_param_infos, + llvm::SmallVector<NamedDecl *, 8> &template_param_decls) +{ + const bool parameter_pack = false; + const bool is_typename = false; + const unsigned depth = 0; + const size_t num_template_params = template_param_infos.GetSize(); + for (size_t i=0; i<num_template_params; ++i) + { + const char *name = template_param_infos.names[i]; + + IdentifierInfo *identifier_info = NULL; + if (name && name[0]) + identifier_info = &ast->Idents.get(name); + if (template_param_infos.args[i].getKind() == TemplateArgument::Integral) + { + template_param_decls.push_back (NonTypeTemplateParmDecl::Create (*ast, + ast->getTranslationUnitDecl(), // Is this the right decl context?, SourceLocation StartLoc, + SourceLocation(), + SourceLocation(), + depth, + i, + identifier_info, + template_param_infos.args[i].getIntegralType(), + parameter_pack, + NULL)); + + } + else + { + template_param_decls.push_back (TemplateTypeParmDecl::Create (*ast, + ast->getTranslationUnitDecl(), // Is this the right decl context? + SourceLocation(), + SourceLocation(), + depth, + i, + identifier_info, + is_typename, + parameter_pack)); + } + } + + TemplateParameterList *template_param_list = TemplateParameterList::Create (*ast, + SourceLocation(), + SourceLocation(), + &template_param_decls.front(), + template_param_decls.size(), + SourceLocation()); + return template_param_list; +} + +clang::FunctionTemplateDecl * +ClangASTContext::CreateFunctionTemplateDecl (clang::DeclContext *decl_ctx, + clang::FunctionDecl *func_decl, + const char *name, + const TemplateParameterInfos &template_param_infos) +{ +// /// \brief Create a function template node. + ASTContext *ast = getASTContext(); + + llvm::SmallVector<NamedDecl *, 8> template_param_decls; + + TemplateParameterList *template_param_list = CreateTemplateParameterList (ast, + template_param_infos, + template_param_decls); + FunctionTemplateDecl *func_tmpl_decl = FunctionTemplateDecl::Create (*ast, + decl_ctx, + func_decl->getLocation(), + func_decl->getDeclName(), + template_param_list, + func_decl); + + for (size_t i=0, template_param_decl_count = template_param_decls.size(); + i < template_param_decl_count; + ++i) + { + // TODO: verify which decl context we should put template_param_decls into.. + template_param_decls[i]->setDeclContext (func_decl); + } + + return func_tmpl_decl; +} + +void +ClangASTContext::CreateFunctionTemplateSpecializationInfo (FunctionDecl *func_decl, + clang::FunctionTemplateDecl *func_tmpl_decl, + const TemplateParameterInfos &infos) +{ + TemplateArgumentList template_args (TemplateArgumentList::OnStack, + infos.args.data(), + infos.args.size()); + + func_decl->setFunctionTemplateSpecialization (func_tmpl_decl, + &template_args, + NULL); +} + + +ClassTemplateDecl * +ClangASTContext::CreateClassTemplateDecl (DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *class_name, + int kind, + const TemplateParameterInfos &template_param_infos) +{ + ASTContext *ast = getASTContext(); + + ClassTemplateDecl *class_template_decl = NULL; + if (decl_ctx == NULL) + decl_ctx = ast->getTranslationUnitDecl(); + + IdentifierInfo &identifier_info = ast->Idents.get(class_name); + DeclarationName decl_name (&identifier_info); + + clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); + + for (NamedDecl *decl : result) + { + class_template_decl = dyn_cast<clang::ClassTemplateDecl>(decl); + if (class_template_decl) + return class_template_decl; + } + + llvm::SmallVector<NamedDecl *, 8> template_param_decls; + + TemplateParameterList *template_param_list = CreateTemplateParameterList (ast, + template_param_infos, + template_param_decls); + + CXXRecordDecl *template_cxx_decl = CXXRecordDecl::Create (*ast, + (TagDecl::TagKind)kind, + decl_ctx, // What decl context do we use here? TU? The actual decl context? + SourceLocation(), + SourceLocation(), + &identifier_info); + + for (size_t i=0, template_param_decl_count = template_param_decls.size(); + i < template_param_decl_count; + ++i) + { + template_param_decls[i]->setDeclContext (template_cxx_decl); + } + + // With templated classes, we say that a class is templated with + // specializations, but that the bare class has no functions. + //template_cxx_decl->startDefinition(); + //template_cxx_decl->completeDefinition(); + + class_template_decl = ClassTemplateDecl::Create (*ast, + decl_ctx, // What decl context do we use here? TU? The actual decl context? + SourceLocation(), + decl_name, + template_param_list, + template_cxx_decl, + NULL); + + if (class_template_decl) + { + if (access_type != eAccessNone) + class_template_decl->setAccess (ConvertAccessTypeToAccessSpecifier (access_type)); + + //if (TagDecl *ctx_tag_decl = dyn_cast<TagDecl>(decl_ctx)) + // CompleteTagDeclarationDefinition(GetTypeForDecl(ctx_tag_decl)); + + decl_ctx->addDecl (class_template_decl); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(class_template_decl); +#endif + } + + return class_template_decl; +} + + +ClassTemplateSpecializationDecl * +ClangASTContext::CreateClassTemplateSpecializationDecl (DeclContext *decl_ctx, + ClassTemplateDecl *class_template_decl, + int kind, + const TemplateParameterInfos &template_param_infos) +{ + ASTContext *ast = getASTContext(); + ClassTemplateSpecializationDecl *class_template_specialization_decl = ClassTemplateSpecializationDecl::Create (*ast, + (TagDecl::TagKind)kind, + decl_ctx, + SourceLocation(), + SourceLocation(), + class_template_decl, + &template_param_infos.args.front(), + template_param_infos.args.size(), + NULL); + + class_template_specialization_decl->setSpecializationKind(TSK_ExplicitSpecialization); + + return class_template_specialization_decl; +} + +ClangASTType +ClangASTContext::CreateClassTemplateSpecializationType (ClassTemplateSpecializationDecl *class_template_specialization_decl) +{ + if (class_template_specialization_decl) + { + ASTContext *ast = getASTContext(); + if (ast) + return ClangASTType(ast, ast->getTagDeclType(class_template_specialization_decl).getAsOpaquePtr()); + } + return ClangASTType(); +} + +static bool +IsOperator (const char *name, OverloadedOperatorKind &op_kind) +{ + if (name == NULL || name[0] == '\0') + return false; + +#define OPERATOR_PREFIX "operator" +#define OPERATOR_PREFIX_LENGTH (sizeof (OPERATOR_PREFIX) - 1) + + const char *post_op_name = NULL; + + bool no_space = true; + + if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) + return false; + + post_op_name = name + OPERATOR_PREFIX_LENGTH; + + if (post_op_name[0] == ' ') + { + post_op_name++; + no_space = false; + } + +#undef OPERATOR_PREFIX +#undef OPERATOR_PREFIX_LENGTH + + // This is an operator, set the overloaded operator kind to invalid + // in case this is a conversion operator... + op_kind = NUM_OVERLOADED_OPERATORS; + + switch (post_op_name[0]) + { + default: + if (no_space) + return false; + break; + case 'n': + if (no_space) + return false; + if (strcmp (post_op_name, "new") == 0) + op_kind = OO_New; + else if (strcmp (post_op_name, "new[]") == 0) + op_kind = OO_Array_New; + break; + + case 'd': + if (no_space) + return false; + if (strcmp (post_op_name, "delete") == 0) + op_kind = OO_Delete; + else if (strcmp (post_op_name, "delete[]") == 0) + op_kind = OO_Array_Delete; + break; + + case '+': + if (post_op_name[1] == '\0') + op_kind = OO_Plus; + else if (post_op_name[2] == '\0') + { + if (post_op_name[1] == '=') + op_kind = OO_PlusEqual; + else if (post_op_name[1] == '+') + op_kind = OO_PlusPlus; + } + break; + + case '-': + if (post_op_name[1] == '\0') + op_kind = OO_Minus; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_MinusEqual; break; + case '-': op_kind = OO_MinusMinus; break; + case '>': op_kind = OO_Arrow; break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '*') + op_kind = OO_ArrowStar; break; + } + break; + + case '*': + if (post_op_name[1] == '\0') + op_kind = OO_Star; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_StarEqual; + break; + + case '/': + if (post_op_name[1] == '\0') + op_kind = OO_Slash; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_SlashEqual; + break; + + case '%': + if (post_op_name[1] == '\0') + op_kind = OO_Percent; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_PercentEqual; + break; + + + case '^': + if (post_op_name[1] == '\0') + op_kind = OO_Caret; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_CaretEqual; + break; + + case '&': + if (post_op_name[1] == '\0') + op_kind = OO_Amp; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_AmpEqual; break; + case '&': op_kind = OO_AmpAmp; break; + } + } + break; + + case '|': + if (post_op_name[1] == '\0') + op_kind = OO_Pipe; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_PipeEqual; break; + case '|': op_kind = OO_PipePipe; break; + } + } + break; + + case '~': + if (post_op_name[1] == '\0') + op_kind = OO_Tilde; + break; + + case '!': + if (post_op_name[1] == '\0') + op_kind = OO_Exclaim; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_ExclaimEqual; + break; + + case '=': + if (post_op_name[1] == '\0') + op_kind = OO_Equal; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_EqualEqual; + break; + + case '<': + if (post_op_name[1] == '\0') + op_kind = OO_Less; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '<': op_kind = OO_LessLess; break; + case '=': op_kind = OO_LessEqual; break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '=') + op_kind = OO_LessLessEqual; + } + break; + + case '>': + if (post_op_name[1] == '\0') + op_kind = OO_Greater; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '>': op_kind = OO_GreaterGreater; break; + case '=': op_kind = OO_GreaterEqual; break; + } + } + else if (post_op_name[1] == '>' && + post_op_name[2] == '=' && + post_op_name[3] == '\0') + { + op_kind = OO_GreaterGreaterEqual; + } + break; + + case ',': + if (post_op_name[1] == '\0') + op_kind = OO_Comma; + break; + + case '(': + if (post_op_name[1] == ')' && post_op_name[2] == '\0') + op_kind = OO_Call; + break; + + case '[': + if (post_op_name[1] == ']' && post_op_name[2] == '\0') + op_kind = OO_Subscript; + break; + } + + return true; +} + +static inline bool +check_op_param (uint32_t op_kind, bool unary, bool binary, uint32_t num_params) +{ + // Special-case call since it can take any number of operands + if(op_kind == OO_Call) + return true; + + // The parameter count doens't include "this" + if (num_params == 0) + return unary; + if (num_params == 1) + return binary; + else + return false; +} + +bool +ClangASTContext::CheckOverloadedOperatorKindParameterCount (uint32_t op_kind, uint32_t num_params) +{ + switch (op_kind) + { + default: + break; + // C++ standard allows any number of arguments to new/delete + case OO_New: + case OO_Array_New: + case OO_Delete: + case OO_Array_Delete: + return true; + } + +#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) case OO_##Name: return check_op_param (op_kind, Unary, Binary, num_params); + switch (op_kind) + { +#include "clang/Basic/OperatorKinds.def" + default: break; + } + return false; +} + +clang::AccessSpecifier +ClangASTContext::UnifyAccessSpecifiers (clang::AccessSpecifier lhs, clang::AccessSpecifier rhs) +{ + clang::AccessSpecifier ret = lhs; + + // Make the access equal to the stricter of the field and the nested field's access + switch (ret) + { + case clang::AS_none: + break; + case clang::AS_private: + break; + case clang::AS_protected: + if (rhs == AS_private) + ret = AS_private; + break; + case clang::AS_public: + ret = rhs; + break; + } + + return ret; +} + +bool +ClangASTContext::FieldIsBitfield (FieldDecl* field, uint32_t& bitfield_bit_size) +{ + return FieldIsBitfield(getASTContext(), field, bitfield_bit_size); +} + +bool +ClangASTContext::FieldIsBitfield +( + ASTContext *ast, + FieldDecl* field, + uint32_t& bitfield_bit_size +) +{ + if (ast == NULL || field == NULL) + return false; + + if (field->isBitField()) + { + Expr* bit_width_expr = field->getBitWidth(); + if (bit_width_expr) + { + llvm::APSInt bit_width_apsint; + if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, *ast)) + { + bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX); + return true; + } + } + } + return false; +} + +bool +ClangASTContext::RecordHasFields (const RecordDecl *record_decl) +{ + if (record_decl == NULL) + return false; + + if (!record_decl->field_empty()) + return true; + + // No fields, lets check this is a CXX record and check the base classes + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + if (RecordHasFields(base_class_decl)) + return true; + } + } + return false; +} + +#pragma mark Objective C Classes + +ClangASTType +ClangASTContext::CreateObjCClass +( + const char *name, + DeclContext *decl_ctx, + bool isForwardDecl, + bool isInternal, + ClangASTMetadata *metadata +) +{ + ASTContext *ast = getASTContext(); + assert (ast != NULL); + assert (name && name[0]); + if (decl_ctx == NULL) + decl_ctx = ast->getTranslationUnitDecl(); + + ObjCInterfaceDecl *decl = ObjCInterfaceDecl::Create (*ast, + decl_ctx, + SourceLocation(), + &ast->Idents.get(name), + NULL, + SourceLocation(), + /*isForwardDecl,*/ + isInternal); + + if (decl && metadata) + SetMetadata(ast, decl, *metadata); + + return ClangASTType (ast, ast->getObjCInterfaceType(decl)); +} + +static inline bool +BaseSpecifierIsEmpty (const CXXBaseSpecifier *b) +{ + return ClangASTContext::RecordHasFields(b->getType()->getAsCXXRecordDecl()) == false; +} + +uint32_t +ClangASTContext::GetNumBaseClasses (const CXXRecordDecl *cxx_record_decl, bool omit_empty_base_classes) +{ + uint32_t num_bases = 0; + if (cxx_record_decl) + { + if (omit_empty_base_classes) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + // Skip empty base classes + if (omit_empty_base_classes) + { + if (BaseSpecifierIsEmpty (base_class)) + continue; + } + ++num_bases; + } + } + else + num_bases = cxx_record_decl->getNumBases(); + } + return num_bases; +} + + +#pragma mark Namespace Declarations + +NamespaceDecl * +ClangASTContext::GetUniqueNamespaceDeclaration (const char *name, DeclContext *decl_ctx) +{ + NamespaceDecl *namespace_decl = NULL; + ASTContext *ast = getASTContext(); + TranslationUnitDecl *translation_unit_decl = ast->getTranslationUnitDecl (); + if (decl_ctx == NULL) + decl_ctx = translation_unit_decl; + + if (name) + { + IdentifierInfo &identifier_info = ast->Idents.get(name); + DeclarationName decl_name (&identifier_info); + clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); + for (NamedDecl *decl : result) + { + namespace_decl = dyn_cast<clang::NamespaceDecl>(decl); + if (namespace_decl) + return namespace_decl; + } + + namespace_decl = NamespaceDecl::Create(*ast, + decl_ctx, + false, + SourceLocation(), + SourceLocation(), + &identifier_info, + NULL); + + decl_ctx->addDecl (namespace_decl); + } + else + { + if (decl_ctx == translation_unit_decl) + { + namespace_decl = translation_unit_decl->getAnonymousNamespace(); + if (namespace_decl) + return namespace_decl; + + namespace_decl = NamespaceDecl::Create(*ast, + decl_ctx, + false, + SourceLocation(), + SourceLocation(), + NULL, + NULL); + translation_unit_decl->setAnonymousNamespace (namespace_decl); + translation_unit_decl->addDecl (namespace_decl); + assert (namespace_decl == translation_unit_decl->getAnonymousNamespace()); + } + else + { + NamespaceDecl *parent_namespace_decl = cast<NamespaceDecl>(decl_ctx); + if (parent_namespace_decl) + { + namespace_decl = parent_namespace_decl->getAnonymousNamespace(); + if (namespace_decl) + return namespace_decl; + namespace_decl = NamespaceDecl::Create(*ast, + decl_ctx, + false, + SourceLocation(), + SourceLocation(), + NULL, + NULL); + parent_namespace_decl->setAnonymousNamespace (namespace_decl); + parent_namespace_decl->addDecl (namespace_decl); + assert (namespace_decl == parent_namespace_decl->getAnonymousNamespace()); + } + else + { + // BAD!!! + } + } + + + if (namespace_decl) + { + // If we make it here, we are creating the anonymous namespace decl + // for the first time, so we need to do the using directive magic + // like SEMA does + UsingDirectiveDecl* using_directive_decl = UsingDirectiveDecl::Create (*ast, + decl_ctx, + SourceLocation(), + SourceLocation(), + NestedNameSpecifierLoc(), + SourceLocation(), + namespace_decl, + decl_ctx); + using_directive_decl->setImplicit(); + decl_ctx->addDecl(using_directive_decl); + } + } +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(namespace_decl); +#endif + return namespace_decl; +} + + +#pragma mark Function Types + +FunctionDecl * +ClangASTContext::CreateFunctionDeclaration (DeclContext *decl_ctx, + const char *name, + const ClangASTType &function_clang_type, + int storage, + bool is_inline) +{ + FunctionDecl *func_decl = NULL; + ASTContext *ast = getASTContext(); + if (decl_ctx == NULL) + decl_ctx = ast->getTranslationUnitDecl(); + + + const bool hasWrittenPrototype = true; + const bool isConstexprSpecified = false; + + if (name && name[0]) + { + func_decl = FunctionDecl::Create (*ast, + decl_ctx, + SourceLocation(), + SourceLocation(), + DeclarationName (&ast->Idents.get(name)), + function_clang_type.GetQualType(), + NULL, + (FunctionDecl::StorageClass)storage, + is_inline, + hasWrittenPrototype, + isConstexprSpecified); + } + else + { + func_decl = FunctionDecl::Create (*ast, + decl_ctx, + SourceLocation(), + SourceLocation(), + DeclarationName (), + function_clang_type.GetQualType(), + NULL, + (FunctionDecl::StorageClass)storage, + is_inline, + hasWrittenPrototype, + isConstexprSpecified); + } + if (func_decl) + decl_ctx->addDecl (func_decl); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(func_decl); +#endif + + return func_decl; +} + +ClangASTType +ClangASTContext::CreateFunctionType (ASTContext *ast, + const ClangASTType& result_type, + const ClangASTType *args, + unsigned num_args, + bool is_variadic, + unsigned type_quals) +{ + assert (ast != NULL); + std::vector<QualType> qual_type_args; + for (unsigned i=0; i<num_args; ++i) + qual_type_args.push_back (args[i].GetQualType()); + + // TODO: Detect calling convention in DWARF? + FunctionProtoType::ExtProtoInfo proto_info; + proto_info.Variadic = is_variadic; + proto_info.ExceptionSpecType = EST_None; + proto_info.TypeQuals = type_quals; + proto_info.RefQualifier = RQ_None; + proto_info.NumExceptions = 0; + proto_info.Exceptions = NULL; + + return ClangASTType (ast, ast->getFunctionType (result_type.GetQualType(), + qual_type_args, + proto_info).getAsOpaquePtr()); +} + +ParmVarDecl * +ClangASTContext::CreateParameterDeclaration (const char *name, const ClangASTType ¶m_type, int storage) +{ + ASTContext *ast = getASTContext(); + assert (ast != NULL); + return ParmVarDecl::Create(*ast, + ast->getTranslationUnitDecl(), + SourceLocation(), + SourceLocation(), + name && name[0] ? &ast->Idents.get(name) : NULL, + param_type.GetQualType(), + NULL, + (VarDecl::StorageClass)storage, + 0); +} + +void +ClangASTContext::SetFunctionParameters (FunctionDecl *function_decl, ParmVarDecl **params, unsigned num_params) +{ + if (function_decl) + function_decl->setParams (ArrayRef<ParmVarDecl*>(params, num_params)); +} + + +#pragma mark Array Types + +ClangASTType +ClangASTContext::CreateArrayType (const ClangASTType &element_type, + size_t element_count, + bool is_vector) +{ + if (element_type.IsValid()) + { + ASTContext *ast = getASTContext(); + assert (ast != NULL); + + if (is_vector) + { + return ClangASTType (ast, ast->getExtVectorType(element_type.GetQualType(), element_count).getAsOpaquePtr()); + } + else + { + + llvm::APInt ap_element_count (64, element_count); + if (element_count == 0) + { + return ClangASTType (ast, ast->getIncompleteArrayType (element_type.GetQualType(), + ArrayType::Normal, + 0).getAsOpaquePtr()); + } + else + { + return ClangASTType (ast, ast->getConstantArrayType (element_type.GetQualType(), + ap_element_count, + ArrayType::Normal, + 0).getAsOpaquePtr()); + } + } + } + return ClangASTType(); +} + + + +#pragma mark Enumeration Types + +ClangASTType +ClangASTContext::CreateEnumerationType +( + const char *name, + DeclContext *decl_ctx, + const Declaration &decl, + const ClangASTType &integer_clang_type +) +{ + // TODO: Do something intelligent with the Declaration object passed in + // like maybe filling in the SourceLocation with it... + ASTContext *ast = getASTContext(); + + // TODO: ask about these... +// const bool IsScoped = false; +// const bool IsFixed = false; + + EnumDecl *enum_decl = EnumDecl::Create (*ast, + decl_ctx, + SourceLocation(), + SourceLocation(), + name && name[0] ? &ast->Idents.get(name) : NULL, + NULL, + false, // IsScoped + false, // IsScopedUsingClassTag + false); // IsFixed + + + if (enum_decl) + { + // TODO: check if we should be setting the promotion type too? + enum_decl->setIntegerType(integer_clang_type.GetQualType()); + + enum_decl->setAccess(AS_public); // TODO respect what's in the debug info + + return ClangASTType (ast, ast->getTagDeclType(enum_decl).getAsOpaquePtr()); + } + return ClangASTType(); +} + +// Disable this for now since I can't seem to get a nicely formatted float +// out of the APFloat class without just getting the float, double or quad +// and then using a formatted print on it which defeats the purpose. We ideally +// would like to get perfect string values for any kind of float semantics +// so we can support remote targets. The code below also requires a patch to +// llvm::APInt. +//bool +//ClangASTContext::ConvertFloatValueToString (ASTContext *ast, clang_type_t clang_type, const uint8_t* bytes, size_t byte_size, int apint_byte_order, std::string &float_str) +//{ +// uint32_t count = 0; +// bool is_complex = false; +// if (ClangASTContext::IsFloatingPointType (clang_type, count, is_complex)) +// { +// unsigned num_bytes_per_float = byte_size / count; +// unsigned num_bits_per_float = num_bytes_per_float * 8; +// +// float_str.clear(); +// uint32_t i; +// for (i=0; i<count; i++) +// { +// APInt ap_int(num_bits_per_float, bytes + i * num_bytes_per_float, (APInt::ByteOrder)apint_byte_order); +// bool is_ieee = false; +// APFloat ap_float(ap_int, is_ieee); +// char s[1024]; +// unsigned int hex_digits = 0; +// bool upper_case = false; +// +// if (ap_float.convertToHexString(s, hex_digits, upper_case, APFloat::rmNearestTiesToEven) > 0) +// { +// if (i > 0) +// float_str.append(", "); +// float_str.append(s); +// if (i == 1 && is_complex) +// float_str.append(1, 'i'); +// } +// } +// return !float_str.empty(); +// } +// return false; +//} + + +ClangASTType +ClangASTContext::GetFloatTypeFromBitSize (clang::ASTContext *ast, + size_t bit_size) +{ + if (ast) + { + if (bit_size == ast->getTypeSize(ast->FloatTy)) + return ClangASTType(ast, ast->FloatTy.getAsOpaquePtr()); + else if (bit_size == ast->getTypeSize(ast->DoubleTy)) + return ClangASTType(ast, ast->DoubleTy.getAsOpaquePtr()); + else if (bit_size == ast->getTypeSize(ast->LongDoubleTy)) + return ClangASTType(ast, ast->LongDoubleTy.getAsOpaquePtr()); + else if (bit_size == ast->getTypeSize(ast->HalfTy)) + return ClangASTType(ast, ast->HalfTy.getAsOpaquePtr()); + } + return ClangASTType(); +} + +bool +ClangASTContext::GetCompleteDecl (clang::ASTContext *ast, + clang::Decl *decl) +{ + if (!decl) + return false; + + ExternalASTSource *ast_source = ast->getExternalSource(); + + if (!ast_source) + return false; + + if (clang::TagDecl *tag_decl = llvm::dyn_cast<clang::TagDecl>(decl)) + { + if (tag_decl->isCompleteDefinition()) + return true; + + if (!tag_decl->hasExternalLexicalStorage()) + return false; + + ast_source->CompleteType(tag_decl); + + return !tag_decl->getTypeForDecl()->isIncompleteType(); + } + else if (clang::ObjCInterfaceDecl *objc_interface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl)) + { + if (objc_interface_decl->getDefinition()) + return true; + + if (!objc_interface_decl->hasExternalLexicalStorage()) + return false; + + ast_source->CompleteType(objc_interface_decl); + + return !objc_interface_decl->getTypeForDecl()->isIncompleteType(); + } + else + { + return false; + } +} + +void +ClangASTContext::SetMetadataAsUserID (const void *object, + user_id_t user_id) +{ + ClangASTMetadata meta_data; + meta_data.SetUserID (user_id); + SetMetadata (object, meta_data); +} + +void +ClangASTContext::SetMetadata (clang::ASTContext *ast, + const void *object, + ClangASTMetadata &metadata) +{ + ClangExternalASTSourceCommon *external_source = + static_cast<ClangExternalASTSourceCommon*>(ast->getExternalSource()); + + if (external_source) + external_source->SetMetadata(object, metadata); +} + +ClangASTMetadata * +ClangASTContext::GetMetadata (clang::ASTContext *ast, + const void *object) +{ + ClangExternalASTSourceCommon *external_source = + static_cast<ClangExternalASTSourceCommon*>(ast->getExternalSource()); + + if (external_source && external_source->HasMetadata(object)) + return external_source->GetMetadata(object); + else + return NULL; +} + +clang::DeclContext * +ClangASTContext::GetAsDeclContext (clang::CXXMethodDecl *cxx_method_decl) +{ + return llvm::dyn_cast<clang::DeclContext>(cxx_method_decl); +} + +clang::DeclContext * +ClangASTContext::GetAsDeclContext (clang::ObjCMethodDecl *objc_method_decl) +{ + return llvm::dyn_cast<clang::DeclContext>(objc_method_decl); +} + + +bool +ClangASTContext::GetClassMethodInfoForDeclContext (clang::DeclContext *decl_ctx, + lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name) +{ + language_object_name.Clear(); + language = eLanguageTypeUnknown; + is_instance_method = false; + + if (decl_ctx) + { + if (clang::CXXMethodDecl *method_decl = llvm::dyn_cast<clang::CXXMethodDecl>(decl_ctx)) + { + if (method_decl->isStatic()) + { + is_instance_method = false; + } + else + { + language_object_name.SetCString("this"); + is_instance_method = true; + } + language = eLanguageTypeC_plus_plus; + return true; + } + else if (clang::ObjCMethodDecl *method_decl = llvm::dyn_cast<clang::ObjCMethodDecl>(decl_ctx)) + { + // Both static and instance methods have a "self" object in objective C + language_object_name.SetCString("self"); + if (method_decl->isInstanceMethod()) + { + is_instance_method = true; + } + else + { + is_instance_method = false; + } + language = eLanguageTypeObjC; + return true; + } + else if (clang::FunctionDecl *function_decl = llvm::dyn_cast<clang::FunctionDecl>(decl_ctx)) + { + ClangASTMetadata *metadata = GetMetadata (&decl_ctx->getParentASTContext(), function_decl); + if (metadata && metadata->HasObjectPtr()) + { + language_object_name.SetCString (metadata->GetObjectPtrName()); + language = eLanguageTypeObjC; + is_instance_method = true; + } + return true; + } + } + return false; +} + diff --git a/source/Symbol/ClangASTImporter.cpp b/source/Symbol/ClangASTImporter.cpp new file mode 100644 index 000000000000..152949620450 --- /dev/null +++ b/source/Symbol/ClangASTImporter.cpp @@ -0,0 +1,718 @@ +//===-- ClangASTImporter.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "llvm/Support/raw_ostream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" + +using namespace lldb_private; +using namespace clang; + +ClangASTMetrics::Counters ClangASTMetrics::global_counters = { 0, 0, 0, 0, 0, 0 }; +ClangASTMetrics::Counters ClangASTMetrics::local_counters = { 0, 0, 0, 0, 0, 0 }; + +void ClangASTMetrics::DumpCounters (Log *log, ClangASTMetrics::Counters &counters) +{ + log->Printf(" Number of visible Decl queries by name : %" PRIu64, counters.m_visible_query_count); + log->Printf(" Number of lexical Decl queries : %" PRIu64, counters.m_lexical_query_count); + log->Printf(" Number of imports initiated by LLDB : %" PRIu64, counters.m_lldb_import_count); + log->Printf(" Number of imports conducted by Clang : %" PRIu64, counters.m_clang_import_count); + log->Printf(" Number of Decls completed : %" PRIu64, counters.m_decls_completed_count); + log->Printf(" Number of records laid out : %" PRIu64, counters.m_record_layout_count); +} + +void ClangASTMetrics::DumpCounters (Log *log) +{ + if (!log) + return; + + log->Printf("== ClangASTMetrics output =="); + log->Printf("-- Global metrics --"); + DumpCounters (log, global_counters); + log->Printf("-- Local metrics --"); + DumpCounters (log, local_counters); +} + +clang::QualType +ClangASTImporter::CopyType (clang::ASTContext *dst_ast, + clang::ASTContext *src_ast, + clang::QualType type) +{ + MinionSP minion_sp (GetMinion(dst_ast, src_ast)); + + if (minion_sp) + return minion_sp->Import(type); + + return QualType(); +} + +lldb::clang_type_t +ClangASTImporter::CopyType (clang::ASTContext *dst_ast, + clang::ASTContext *src_ast, + lldb::clang_type_t type) +{ + return CopyType (dst_ast, src_ast, QualType::getFromOpaquePtr(type)).getAsOpaquePtr(); +} + +clang::Decl * +ClangASTImporter::CopyDecl (clang::ASTContext *dst_ast, + clang::ASTContext *src_ast, + clang::Decl *decl) +{ + MinionSP minion_sp; + + minion_sp = GetMinion(dst_ast, src_ast); + + if (minion_sp) + { + clang::Decl *result = minion_sp->Import(decl); + + if (!result) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + lldb::user_id_t user_id; + ClangASTMetadata *metadata = GetDeclMetadata(decl); + if (metadata) + user_id = metadata->GetUserID(); + + if (NamedDecl *named_decl = dyn_cast<NamedDecl>(decl)) + log->Printf(" [ClangASTImporter] WARNING: Failed to import a %s '%s', metadata 0x%" PRIx64, + decl->getDeclKindName(), + named_decl->getNameAsString().c_str(), + user_id); + else + log->Printf(" [ClangASTImporter] WARNING: Failed to import a %s, metadata 0x%" PRIx64, + decl->getDeclKindName(), + user_id); + } + } + + return result; + } + + return NULL; +} + +lldb::clang_type_t +ClangASTImporter::DeportType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + lldb::clang_type_t type) +{ + MinionSP minion_sp (GetMinion (dst_ctx, src_ctx)); + + if (!minion_sp) + return NULL; + + std::set<NamedDecl *> decls_to_deport; + std::set<NamedDecl *> decls_already_deported; + + minion_sp->InitDeportWorkQueues(&decls_to_deport, + &decls_already_deported); + + lldb::clang_type_t result = CopyType(dst_ctx, src_ctx, type); + + minion_sp->ExecuteDeportWorkQueues(); + + if (!result) + return NULL; + + return result; + +} + +clang::Decl * +ClangASTImporter::DeportDecl (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::Decl *decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf(" [ClangASTImporter] DeportDecl called on (%sDecl*)%p from (ASTContext*)%p to (ASTContex*)%p", + decl->getDeclKindName(), + decl, + src_ctx, + dst_ctx); + + MinionSP minion_sp (GetMinion (dst_ctx, src_ctx)); + + if (!minion_sp) + return NULL; + + std::set<NamedDecl *> decls_to_deport; + std::set<NamedDecl *> decls_already_deported; + + minion_sp->InitDeportWorkQueues(&decls_to_deport, + &decls_already_deported); + + clang::Decl *result = CopyDecl(dst_ctx, src_ctx, decl); + + minion_sp->ExecuteDeportWorkQueues(); + + if (!result) + return NULL; + + if (log) + log->Printf(" [ClangASTImporter] DeportDecl deported (%sDecl*)%p to (%sDecl*)%p", + decl->getDeclKindName(), + decl, + result->getDeclKindName(), + result); + + return result; +} + +void +ClangASTImporter::CompleteDecl (clang::Decl *decl) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf(" [ClangASTImporter] CompleteDecl called on (%sDecl*)%p", + decl->getDeclKindName(), + decl); + + if (ObjCInterfaceDecl *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl)) + { + if (!interface_decl->getDefinition()) + { + interface_decl->startDefinition(); + CompleteObjCInterfaceDecl(interface_decl); + } + } + else if (ObjCProtocolDecl *protocol_decl = dyn_cast<ObjCProtocolDecl>(decl)) + { + if (!protocol_decl->getDefinition()) + protocol_decl->startDefinition(); + } + else if (TagDecl *tag_decl = dyn_cast<TagDecl>(decl)) + { + if (!tag_decl->getDefinition() && !tag_decl->isBeingDefined()) + { + tag_decl->startDefinition(); + CompleteTagDecl(tag_decl); + tag_decl->setCompleteDefinition(true); + } + } + else + { + assert (0 && "CompleteDecl called on a Decl that can't be completed"); + } +} + +bool +ClangASTImporter::CompleteTagDecl (clang::TagDecl *decl) +{ + ClangASTMetrics::RegisterDeclCompletion(); + + DeclOrigin decl_origin = GetDeclOrigin(decl); + + if (!decl_origin.Valid()) + return false; + + if (!ClangASTContext::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + return false; + + MinionSP minion_sp (GetMinion(&decl->getASTContext(), decl_origin.ctx)); + + if (minion_sp) + minion_sp->ImportDefinitionTo(decl, decl_origin.decl); + + return true; +} + +bool +ClangASTImporter::CompleteTagDeclWithOrigin(clang::TagDecl *decl, clang::TagDecl *origin_decl) +{ + ClangASTMetrics::RegisterDeclCompletion(); + + clang::ASTContext *origin_ast_ctx = &origin_decl->getASTContext(); + + if (!ClangASTContext::GetCompleteDecl(origin_ast_ctx, origin_decl)) + return false; + + MinionSP minion_sp (GetMinion(&decl->getASTContext(), origin_ast_ctx)); + + if (minion_sp) + minion_sp->ImportDefinitionTo(decl, origin_decl); + + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + OriginMap &origins = context_md->m_origins; + + origins[decl] = DeclOrigin(origin_ast_ctx, origin_decl); + + return true; +} + +bool +ClangASTImporter::CompleteObjCInterfaceDecl (clang::ObjCInterfaceDecl *interface_decl) +{ + ClangASTMetrics::RegisterDeclCompletion(); + + DeclOrigin decl_origin = GetDeclOrigin(interface_decl); + + if (!decl_origin.Valid()) + return false; + + if (!ClangASTContext::GetCompleteDecl(decl_origin.ctx, decl_origin.decl)) + return false; + + MinionSP minion_sp (GetMinion(&interface_decl->getASTContext(), decl_origin.ctx)); + + if (minion_sp) + minion_sp->ImportDefinitionTo(interface_decl, decl_origin.decl); + + return true; +} + +bool +ClangASTImporter::RequireCompleteType (clang::QualType type) +{ + if (type.isNull()) + return false; + + if (const TagType *tag_type = type->getAs<TagType>()) + { + return CompleteTagDecl(tag_type->getDecl()); + } + if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>()) + { + if (ObjCInterfaceDecl *objc_interface_decl = objc_object_type->getInterface()) + return CompleteObjCInterfaceDecl(objc_interface_decl); + else + return false; + } + if (const ArrayType *array_type = type->getAsArrayTypeUnsafe()) + { + return RequireCompleteType(array_type->getElementType()); + } + if (const AtomicType *atomic_type = type->getAs<AtomicType>()) + { + return RequireCompleteType(atomic_type->getPointeeType()); + } + + return true; +} + +ClangASTMetadata * +ClangASTImporter::GetDeclMetadata (const clang::Decl *decl) +{ + DeclOrigin decl_origin = GetDeclOrigin(decl); + + if (decl_origin.Valid()) + return ClangASTContext::GetMetadata(decl_origin.ctx, decl_origin.decl); + else + return ClangASTContext::GetMetadata(&decl->getASTContext(), decl); +} + +ClangASTImporter::DeclOrigin +ClangASTImporter::GetDeclOrigin(const clang::Decl *decl) +{ + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + OriginMap &origins = context_md->m_origins; + + OriginMap::iterator iter = origins.find(decl); + + if (iter != origins.end()) + return iter->second; + else + return DeclOrigin(); +} + +void +ClangASTImporter::SetDeclOrigin (const clang::Decl *decl, clang::Decl *original_decl) +{ + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + OriginMap &origins = context_md->m_origins; + + OriginMap::iterator iter = origins.find(decl); + + if (iter != origins.end()) + { + iter->second.decl = original_decl; + iter->second.ctx = &original_decl->getASTContext(); + } + else + { + origins[decl] = DeclOrigin(&original_decl->getASTContext(), original_decl); + } +} + +void +ClangASTImporter::RegisterNamespaceMap(const clang::NamespaceDecl *decl, + NamespaceMapSP &namespace_map) +{ + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + context_md->m_namespace_maps[decl] = namespace_map; +} + +ClangASTImporter::NamespaceMapSP +ClangASTImporter::GetNamespaceMap(const clang::NamespaceDecl *decl) +{ + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + NamespaceMetaMap &namespace_maps = context_md->m_namespace_maps; + + NamespaceMetaMap::iterator iter = namespace_maps.find(decl); + + if (iter != namespace_maps.end()) + return iter->second; + else + return NamespaceMapSP(); +} + +void +ClangASTImporter::BuildNamespaceMap(const clang::NamespaceDecl *decl) +{ + assert (decl); + ASTContextMetadataSP context_md = GetContextMetadata(&decl->getASTContext()); + + const DeclContext *parent_context = decl->getDeclContext(); + const NamespaceDecl *parent_namespace = dyn_cast<NamespaceDecl>(parent_context); + NamespaceMapSP parent_map; + + if (parent_namespace) + parent_map = GetNamespaceMap(parent_namespace); + + NamespaceMapSP new_map; + + new_map.reset(new NamespaceMap); + + if (context_md->m_map_completer) + { + std::string namespace_string = decl->getDeclName().getAsString(); + + context_md->m_map_completer->CompleteNamespaceMap (new_map, ConstString(namespace_string.c_str()), parent_map); + } + + context_md->m_namespace_maps[decl] = new_map; +} + +void +ClangASTImporter::ForgetDestination (clang::ASTContext *dst_ast) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf(" [ClangASTImporter] Forgetting destination (ASTContext*)%p", dst_ast); + + m_metadata_map.erase(dst_ast); +} + +void +ClangASTImporter::ForgetSource (clang::ASTContext *dst_ast, clang::ASTContext *src_ast) +{ + ASTContextMetadataSP md = MaybeGetContextMetadata (dst_ast); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf(" [ClangASTImporter] Forgetting source->dest (ASTContext*)%p->(ASTContext*)%p", src_ast, dst_ast); + + if (!md) + return; + + md->m_minions.erase(src_ast); + + for (OriginMap::iterator iter = md->m_origins.begin(); + iter != md->m_origins.end(); + ) + { + if (iter->second.ctx == src_ast) + md->m_origins.erase(iter++); + else + ++iter; + } +} + +ClangASTImporter::MapCompleter::~MapCompleter () +{ + return; +} + +void +ClangASTImporter::Minion::InitDeportWorkQueues (std::set<clang::NamedDecl *> *decls_to_deport, + std::set<clang::NamedDecl *> *decls_already_deported) +{ + assert(!m_decls_to_deport); // TODO make debug only + assert(!m_decls_already_deported); + + m_decls_to_deport = decls_to_deport; + m_decls_already_deported = decls_already_deported; +} + +void +ClangASTImporter::Minion::ExecuteDeportWorkQueues () +{ + assert(m_decls_to_deport); // TODO make debug only + assert(m_decls_already_deported); + + ASTContextMetadataSP to_context_md = m_master.GetContextMetadata(&getToContext()); + + while (!m_decls_to_deport->empty()) + { + NamedDecl *decl = *m_decls_to_deport->begin(); + + m_decls_already_deported->insert(decl); + m_decls_to_deport->erase(decl); + + DeclOrigin &origin = to_context_md->m_origins[decl]; + + assert (origin.ctx == m_source_ctx); // otherwise we should never have added this + // because it doesn't need to be deported + + Decl *original_decl = to_context_md->m_origins[decl].decl; + + ClangASTContext::GetCompleteDecl (m_source_ctx, original_decl); + + if (TagDecl *tag_decl = dyn_cast<TagDecl>(decl)) + { + if (TagDecl *original_tag_decl = dyn_cast<TagDecl>(original_decl)) + if (original_tag_decl->isCompleteDefinition()) + ImportDefinitionTo(tag_decl, original_tag_decl); + + tag_decl->setHasExternalLexicalStorage(false); + tag_decl->setHasExternalVisibleStorage(false); + } + else if (ObjCInterfaceDecl *interface_decl = dyn_cast<ObjCInterfaceDecl>(decl)) + { + interface_decl->setHasExternalLexicalStorage(false); + interface_decl->setHasExternalVisibleStorage(false); + } + + to_context_md->m_origins.erase(decl); + } + + m_decls_to_deport = NULL; + m_decls_already_deported = NULL; +} + +void +ClangASTImporter::Minion::ImportDefinitionTo (clang::Decl *to, clang::Decl *from) +{ + ASTImporter::Imported(from, to); + + ObjCInterfaceDecl *to_objc_interface = dyn_cast<ObjCInterfaceDecl>(to); + + /* + if (to_objc_interface) + to_objc_interface->startDefinition(); + + CXXRecordDecl *to_cxx_record = dyn_cast<CXXRecordDecl>(to); + + if (to_cxx_record) + to_cxx_record->startDefinition(); + */ + + ImportDefinition(from); + + // If we're dealing with an Objective-C class, ensure that the inheritance has + // been set up correctly. The ASTImporter may not do this correctly if the + // class was originally sourced from symbols. + + if (to_objc_interface) + { + do + { + ObjCInterfaceDecl *to_superclass = to_objc_interface->getSuperClass(); + + if (to_superclass) + break; // we're not going to override it if it's set + + ObjCInterfaceDecl *from_objc_interface = dyn_cast<ObjCInterfaceDecl>(from); + + if (!from_objc_interface) + break; + + ObjCInterfaceDecl *from_superclass = from_objc_interface->getSuperClass(); + + if (!from_superclass) + break; + + Decl *imported_from_superclass_decl = Import(from_superclass); + + if (!imported_from_superclass_decl) + break; + + ObjCInterfaceDecl *imported_from_superclass = dyn_cast<ObjCInterfaceDecl>(imported_from_superclass_decl); + + if (!imported_from_superclass) + break; + + if (!to_objc_interface->hasDefinition()) + to_objc_interface->startDefinition(); + + to_objc_interface->setSuperClass(imported_from_superclass); + } + while (0); + } +} + +clang::Decl * +ClangASTImporter::Minion::Imported (clang::Decl *from, clang::Decl *to) +{ + ClangASTMetrics::RegisterClangImport(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + lldb::user_id_t user_id; + ClangASTMetadata *metadata = m_master.GetDeclMetadata(from); + if (metadata) + user_id = metadata->GetUserID(); + + if (NamedDecl *from_named_decl = dyn_cast<clang::NamedDecl>(from)) + { + std::string name_string; + llvm::raw_string_ostream name_stream(name_string); + from_named_decl->printName(name_stream); + name_stream.flush(); + + log->Printf(" [ClangASTImporter] Imported (%sDecl*)%p, named %s (from (Decl*)%p), metadata 0x%" PRIx64, + from->getDeclKindName(), + to, + name_string.c_str(), + from, + user_id); + } + else + { + log->Printf(" [ClangASTImporter] Imported (%sDecl*)%p (from (Decl*)%p), metadata 0x%" PRIx64, + from->getDeclKindName(), + to, + from, + user_id); + } + } + + ASTContextMetadataSP to_context_md = m_master.GetContextMetadata(&to->getASTContext()); + ASTContextMetadataSP from_context_md = m_master.MaybeGetContextMetadata(m_source_ctx); + + if (from_context_md) + { + OriginMap &origins = from_context_md->m_origins; + + OriginMap::iterator origin_iter = origins.find(from); + + if (origin_iter != origins.end()) + { + to_context_md->m_origins[to] = origin_iter->second; + + MinionSP direct_completer = m_master.GetMinion(&to->getASTContext(), origin_iter->second.ctx); + + if (direct_completer.get() != this) + direct_completer->ASTImporter::Imported(origin_iter->second.decl, to); + + if (log) + log->Printf(" [ClangASTImporter] Propagated origin (Decl*)%p/(ASTContext*)%p from (ASTContext*)%p to (ASTContext*)%p", + origin_iter->second.decl, + origin_iter->second.ctx, + &from->getASTContext(), + &to->getASTContext()); + } + else + { + if (m_decls_to_deport && m_decls_already_deported) + { + if (isa<TagDecl>(to) || isa<ObjCInterfaceDecl>(to)) + { + NamedDecl *to_named_decl = dyn_cast<NamedDecl>(to); + + if (!m_decls_already_deported->count(to_named_decl)) + m_decls_to_deport->insert(to_named_decl); + } + + } + to_context_md->m_origins[to] = DeclOrigin(m_source_ctx, from); + + if (log) + log->Printf(" [ClangASTImporter] Decl has no origin information in (ASTContext*)%p", + &from->getASTContext()); + } + + if (clang::NamespaceDecl *to_namespace = dyn_cast<clang::NamespaceDecl>(to)) + { + clang::NamespaceDecl *from_namespace = dyn_cast<clang::NamespaceDecl>(from); + + NamespaceMetaMap &namespace_maps = from_context_md->m_namespace_maps; + + NamespaceMetaMap::iterator namespace_map_iter = namespace_maps.find(from_namespace); + + if (namespace_map_iter != namespace_maps.end()) + to_context_md->m_namespace_maps[to_namespace] = namespace_map_iter->second; + } + } + else + { + to_context_md->m_origins[to] = DeclOrigin (m_source_ctx, from); + + if (log) + log->Printf(" [ClangASTImporter] Sourced origin (Decl*)%p/(ASTContext*)%p into (ASTContext*)%p", + from, + m_source_ctx, + &to->getASTContext()); + } + + if (TagDecl *from_tag_decl = dyn_cast<TagDecl>(from)) + { + TagDecl *to_tag_decl = dyn_cast<TagDecl>(to); + + to_tag_decl->setHasExternalLexicalStorage(); + to_tag_decl->setMustBuildLookupTable(); + + if (log) + log->Printf(" [ClangASTImporter] To is a TagDecl - attributes %s%s [%s->%s]", + (to_tag_decl->hasExternalLexicalStorage() ? " Lexical" : ""), + (to_tag_decl->hasExternalVisibleStorage() ? " Visible" : ""), + (from_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"), + (to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete")); + } + + if (isa<NamespaceDecl>(from)) + { + NamespaceDecl *to_namespace_decl = dyn_cast<NamespaceDecl>(to); + + m_master.BuildNamespaceMap(to_namespace_decl); + + to_namespace_decl->setHasExternalVisibleStorage(); + } + + if (isa<ObjCInterfaceDecl>(from)) + { + ObjCInterfaceDecl *to_interface_decl = dyn_cast<ObjCInterfaceDecl>(to); + + to_interface_decl->setHasExternalLexicalStorage(); + to_interface_decl->setHasExternalVisibleStorage(); + + /*to_interface_decl->setExternallyCompleted();*/ + + if (log) + log->Printf(" [ClangASTImporter] To is an ObjCInterfaceDecl - attributes %s%s%s", + (to_interface_decl->hasExternalLexicalStorage() ? " Lexical" : ""), + (to_interface_decl->hasExternalVisibleStorage() ? " Visible" : ""), + (to_interface_decl->hasDefinition() ? " HasDefinition" : "")); + } + + return clang::ASTImporter::Imported(from, to); +} diff --git a/source/Symbol/ClangASTType.cpp b/source/Symbol/ClangASTType.cpp new file mode 100644 index 000000000000..2e7c0790fe57 --- /dev/null +++ b/source/Symbol/ClangASTType.cpp @@ -0,0 +1,6486 @@ +//===-- ClangASTType.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Symbol/ClangASTType.h" + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" + +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/VerifyDecl.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +#include <mutex> + +using namespace lldb; +using namespace lldb_private; +using namespace clang; +using namespace llvm; + +static bool +GetCompleteQualType (ASTContext *ast, QualType qual_type, bool allow_completion = true) +{ + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::ConstantArray: + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + { + const ArrayType *array_type = dyn_cast<ArrayType>(qual_type.getTypePtr()); + + if (array_type) + return GetCompleteQualType (ast, array_type->getElementType(), allow_completion); + } + break; + + case clang::Type::Record: + case clang::Type::Enum: + { + const TagType *tag_type = dyn_cast<TagType>(qual_type.getTypePtr()); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + { + if (tag_decl->isCompleteDefinition()) + return true; + + if (!allow_completion) + return false; + + if (tag_decl->hasExternalLexicalStorage()) + { + if (ast) + { + ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) + { + external_ast_source->CompleteType(tag_decl); + return !tag_type->isIncompleteType(); + } + } + } + return false; + } + } + + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + // We currently can't complete objective C types through the newly added ASTContext + // because it only supports TagDecl objects right now... + if (class_interface_decl) + { + if (class_interface_decl->getDefinition()) + return true; + + if (!allow_completion) + return false; + + if (class_interface_decl->hasExternalLexicalStorage()) + { + if (ast) + { + ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) + { + external_ast_source->CompleteType (class_interface_decl); + return !objc_class_type->isIncompleteType(); + } + } + } + return false; + } + } + } + break; + + case clang::Type::Typedef: + return GetCompleteQualType (ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType(), allow_completion); + + case clang::Type::Elaborated: + return GetCompleteQualType (ast, cast<ElaboratedType>(qual_type)->getNamedType(), allow_completion); + + case clang::Type::Paren: + return GetCompleteQualType (ast, cast<ParenType>(qual_type)->desugar(), allow_completion); + + default: + break; + } + + return true; +} + +static ObjCIvarDecl::AccessControl +ConvertAccessTypeToObjCIvarAccessControl (AccessType access) +{ + switch (access) + { + case eAccessNone: return ObjCIvarDecl::None; + case eAccessPublic: return ObjCIvarDecl::Public; + case eAccessPrivate: return ObjCIvarDecl::Private; + case eAccessProtected: return ObjCIvarDecl::Protected; + case eAccessPackage: return ObjCIvarDecl::Package; + } + return ObjCIvarDecl::None; +} + +//---------------------------------------------------------------------- +// Tests +//---------------------------------------------------------------------- + +ClangASTType::ClangASTType (clang::ASTContext *ast, + clang::QualType qual_type) : + m_type (qual_type.getAsOpaquePtr()), + m_ast (ast) +{ +} + +ClangASTType::~ClangASTType() +{ +} + +//---------------------------------------------------------------------- +// Tests +//---------------------------------------------------------------------- + +bool +ClangASTType::IsAggregateType () const +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + case clang::Type::ConstantArray: + case clang::Type::ExtVector: + case clang::Type::Vector: + case clang::Type::Record: + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + return true; + case clang::Type::Elaborated: + return ClangASTType(m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsAggregateType(); + case clang::Type::Typedef: + return ClangASTType(m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsAggregateType(); + case clang::Type::Paren: + return ClangASTType(m_ast, cast<ParenType>(qual_type)->desugar()).IsAggregateType(); + default: + break; + } + // The clang type does have a value + return false; +} + +bool +ClangASTType::IsArrayType (ClangASTType *element_type_ptr, + uint64_t *size, + bool *is_incomplete) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + default: + break; + + case clang::Type::ConstantArray: + if (element_type_ptr) + element_type_ptr->SetClangType (m_ast, cast<ConstantArrayType>(qual_type)->getElementType()); + if (size) + *size = cast<ConstantArrayType>(qual_type)->getSize().getLimitedValue(ULLONG_MAX); + return true; + + case clang::Type::IncompleteArray: + if (element_type_ptr) + element_type_ptr->SetClangType (m_ast, cast<IncompleteArrayType>(qual_type)->getElementType()); + if (size) + *size = 0; + if (is_incomplete) + *is_incomplete = true; + return true; + + case clang::Type::VariableArray: + if (element_type_ptr) + element_type_ptr->SetClangType (m_ast, cast<VariableArrayType>(qual_type)->getElementType()); + if (size) + *size = 0; + return true; + + case clang::Type::DependentSizedArray: + if (element_type_ptr) + element_type_ptr->SetClangType (m_ast, cast<DependentSizedArrayType>(qual_type)->getElementType()); + if (size) + *size = 0; + return true; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsArrayType (element_type_ptr, + size, + is_incomplete); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsArrayType (element_type_ptr, + size, + is_incomplete); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsArrayType (element_type_ptr, + size, + is_incomplete); + } + } + if (element_type_ptr) + element_type_ptr->Clear(); + if (size) + *size = 0; + if (is_incomplete) + *is_incomplete = false; + return 0; +} + + +bool +ClangASTType::IsCharType () const +{ + if (!IsValid()) + return false; + return GetQualType().getUnqualifiedType()->isCharType(); +} + + +bool +ClangASTType::IsCompleteType () const +{ + if (!IsValid()) + return false; + const bool allow_completion = false; + return GetCompleteQualType (m_ast, GetQualType(), allow_completion); +} + +bool +ClangASTType::IsConst() const +{ + return GetQualType().isConstQualified(); +} + +bool +ClangASTType::IsCStringType (uint32_t &length) const +{ + ClangASTType pointee_or_element_clang_type; + length = 0; + Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); + + if (!pointee_or_element_clang_type.IsValid()) + return false; + + if (type_flags.AnySet (eTypeIsArray | eTypeIsPointer)) + { + if (pointee_or_element_clang_type.IsCharType()) + { + if (type_flags.Test (eTypeIsArray)) + { + // We know the size of the array and it could be a C string + // since it is an array of characters + length = cast<ConstantArrayType>(GetCanonicalQualType().getTypePtr())->getSize().getLimitedValue(); + } + return true; + + } + } + return false; +} + +bool +ClangASTType::IsFunctionType (bool *is_variadic_ptr) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + if (qual_type->isFunctionType()) + { + if (is_variadic_ptr) + { + const clang::FunctionProtoType *function_proto_type = llvm::dyn_cast<clang::FunctionProtoType>(qual_type.getTypePtr()); + if (function_proto_type) + *is_variadic_ptr = function_proto_type->isVariadic(); + else + *is_variadic_ptr = false; + } + return true; + } + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + default: + break; + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsFunctionType(); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsFunctionType(); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsFunctionType(); + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + { + const ReferenceType *reference_type = cast<ReferenceType>(qual_type.getTypePtr()); + if (reference_type) + return ClangASTType (m_ast, reference_type->getPointeeType()).IsFunctionType(); + } + break; + } + } + return false; +} + + +bool +ClangASTType::IsFunctionPointerType () const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + if (qual_type->isFunctionPointerType()) + return true; + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + default: + break; + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsFunctionPointerType(); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsFunctionPointerType(); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsFunctionPointerType(); + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + { + const ReferenceType *reference_type = cast<ReferenceType>(qual_type.getTypePtr()); + if (reference_type) + return ClangASTType (m_ast, reference_type->getPointeeType()).IsFunctionPointerType(); + } + break; + } + } + return false; + +} + +bool +ClangASTType::IsIntegerType (bool &is_signed) const +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + const BuiltinType *builtin_type = dyn_cast<BuiltinType>(qual_type->getCanonicalTypeInternal()); + + if (builtin_type) + { + if (builtin_type->isInteger()) + { + is_signed = builtin_type->isSignedInteger(); + return true; + } + } + + return false; +} + +bool +ClangASTType::IsPointerType (ClangASTType *pointee_type) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + switch (cast<clang::BuiltinType>(qual_type)->getKind()) + { + default: + break; + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + return true; + } + return false; + case clang::Type::ObjCObjectPointer: + if (pointee_type) + pointee_type->SetClangType (m_ast, cast<ObjCObjectPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::BlockPointer: + if (pointee_type) + pointee_type->SetClangType (m_ast, cast<BlockPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::Pointer: + if (pointee_type) + pointee_type->SetClangType (m_ast, cast<PointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::MemberPointer: + if (pointee_type) + pointee_type->SetClangType (m_ast, cast<MemberPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsPointerType(pointee_type); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsPointerType(pointee_type); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsPointerType(pointee_type); + default: + break; + } + } + if (pointee_type) + pointee_type->Clear(); + return false; +} + + +bool +ClangASTType::IsPointerOrReferenceType (ClangASTType *pointee_type) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + switch (cast<clang::BuiltinType>(qual_type)->getKind()) + { + default: + break; + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + return true; + } + return false; + case clang::Type::ObjCObjectPointer: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<ObjCObjectPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::BlockPointer: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<BlockPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::Pointer: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<PointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::MemberPointer: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<MemberPointerType>(qual_type)->getPointeeType()); + return true; + case clang::Type::LValueReference: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<LValueReferenceType>(qual_type)->desugar()); + return true; + case clang::Type::RValueReference: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<LValueReferenceType>(qual_type)->desugar()); + return true; + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsPointerOrReferenceType(pointee_type); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsPointerOrReferenceType(pointee_type); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsPointerOrReferenceType(pointee_type); + default: + break; + } + } + if (pointee_type) + pointee_type->Clear(); + return false; +} + + +bool +ClangASTType::IsReferenceType (ClangASTType *pointee_type) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + + switch (type_class) + { + case clang::Type::LValueReference: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<LValueReferenceType>(qual_type)->desugar()); + return true; + case clang::Type::RValueReference: + if (pointee_type) + pointee_type->SetClangType(m_ast, cast<LValueReferenceType>(qual_type)->desugar()); + return true; + case clang::Type::Typedef: + return ClangASTType(m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsReferenceType(pointee_type); + case clang::Type::Elaborated: + return ClangASTType(m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).IsReferenceType(pointee_type); + case clang::Type::Paren: + return ClangASTType(m_ast, cast<clang::ParenType>(qual_type)->desugar()).IsReferenceType(pointee_type); + + default: + break; + } + } + if (pointee_type) + pointee_type->Clear(); + return false; +} + +bool +ClangASTType::IsFloatingPointType (uint32_t &count, bool &is_complex) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + if (const BuiltinType *BT = dyn_cast<BuiltinType>(qual_type->getCanonicalTypeInternal())) + { + clang::BuiltinType::Kind kind = BT->getKind(); + if (kind >= BuiltinType::Float && kind <= BuiltinType::LongDouble) + { + count = 1; + is_complex = false; + return true; + } + } + else if (const ComplexType *CT = dyn_cast<ComplexType>(qual_type->getCanonicalTypeInternal())) + { + if (ClangASTType (m_ast, CT->getElementType()).IsFloatingPointType (count, is_complex)) + { + count = 2; + is_complex = true; + return true; + } + } + else if (const VectorType *VT = dyn_cast<VectorType>(qual_type->getCanonicalTypeInternal())) + { + if (ClangASTType (m_ast, VT->getElementType()).IsFloatingPointType (count, is_complex)) + { + count = VT->getNumElements(); + is_complex = false; + return true; + } + } + } + count = 0; + is_complex = false; + return false; +} + + +bool +ClangASTType::IsDefined() const +{ + if (!IsValid()) + return false; + + QualType qual_type(GetQualType()); + const TagType *tag_type = dyn_cast<TagType>(qual_type.getTypePtr()); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + return tag_decl->isCompleteDefinition(); + return false; + } + else + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + if (class_interface_decl) + return class_interface_decl->getDefinition() != NULL; + return false; + } + } + return true; +} + +bool +ClangASTType::IsObjCClassType () const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + const ObjCObjectPointerType *obj_pointer_type = dyn_cast<ObjCObjectPointerType>(qual_type); + + if (obj_pointer_type) + return obj_pointer_type->isObjCClassType(); + } + return false; +} + +bool +ClangASTType::IsObjCObjectOrInterfaceType () const +{ + if (IsValid()) + return GetCanonicalQualType()->isObjCObjectOrInterfaceType(); + return false; +} + +bool +ClangASTType::IsPolymorphicClass () const +{ + if (IsValid()) + { + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + if (record_decl) + { + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + if (cxx_record_decl) + return cxx_record_decl->isPolymorphic(); + } + } + break; + + default: + break; + } + } + return false; +} + +bool +ClangASTType::IsPossibleDynamicType (ClangASTType *dynamic_pointee_type, + bool check_cplusplus, + bool check_objc) const +{ + QualType pointee_qual_type; + if (m_type) + { + QualType qual_type (GetCanonicalQualType()); + bool success = false; + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + if (check_objc && cast<BuiltinType>(qual_type)->getKind() == BuiltinType::ObjCId) + { + if (dynamic_pointee_type) + dynamic_pointee_type->SetClangType(m_ast, m_type); + return true; + } + break; + + case clang::Type::ObjCObjectPointer: + if (check_objc) + { + if (dynamic_pointee_type) + dynamic_pointee_type->SetClangType(m_ast, cast<ObjCObjectPointerType>(qual_type)->getPointeeType()); + return true; + } + break; + + case clang::Type::Pointer: + pointee_qual_type = cast<PointerType>(qual_type)->getPointeeType(); + success = true; + break; + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + pointee_qual_type = cast<ReferenceType>(qual_type)->getPointeeType(); + success = true; + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, + cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).IsPossibleDynamicType (dynamic_pointee_type, + check_cplusplus, + check_objc); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, + cast<ElaboratedType>(qual_type)->getNamedType()).IsPossibleDynamicType (dynamic_pointee_type, + check_cplusplus, + check_objc); + + case clang::Type::Paren: + return ClangASTType (m_ast, + cast<ParenType>(qual_type)->desugar()).IsPossibleDynamicType (dynamic_pointee_type, + check_cplusplus, + check_objc); + default: + break; + } + + if (success) + { + // Check to make sure what we are pointing too is a possible dynamic C++ type + // We currently accept any "void *" (in case we have a class that has been + // watered down to an opaque pointer) and virtual C++ classes. + const clang::Type::TypeClass pointee_type_class = pointee_qual_type.getCanonicalType()->getTypeClass(); + switch (pointee_type_class) + { + case clang::Type::Builtin: + switch (cast<BuiltinType>(pointee_qual_type)->getKind()) + { + case BuiltinType::UnknownAny: + case BuiltinType::Void: + if (dynamic_pointee_type) + dynamic_pointee_type->SetClangType(m_ast, pointee_qual_type); + return true; + + case BuiltinType::NullPtr: + case BuiltinType::Bool: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::WChar_U: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::UShort: + case BuiltinType::UInt: + case BuiltinType::ULong: + case BuiltinType::ULongLong: + case BuiltinType::UInt128: + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::WChar_S: + case BuiltinType::Short: + case BuiltinType::Int: + case BuiltinType::Long: + case BuiltinType::LongLong: + case BuiltinType::Int128: + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + case BuiltinType::Dependent: + case BuiltinType::Overload: + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::BoundMember: + case BuiltinType::Half: + case BuiltinType::ARCUnbridgedCast: + case BuiltinType::PseudoObject: + case BuiltinType::BuiltinFn: + case BuiltinType::OCLEvent: + case BuiltinType::OCLImage1d: + case BuiltinType::OCLImage1dArray: + case BuiltinType::OCLImage1dBuffer: + case BuiltinType::OCLImage2d: + case BuiltinType::OCLImage2dArray: + case BuiltinType::OCLImage3d: + case BuiltinType::OCLSampler: + break; + } + break; + + case clang::Type::Record: + if (check_cplusplus) + { + CXXRecordDecl *cxx_record_decl = pointee_qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + bool is_complete = cxx_record_decl->isCompleteDefinition(); + + if (is_complete) + success = cxx_record_decl->isDynamicClass(); + else + { + ClangASTMetadata *metadata = ClangASTContext::GetMetadata (m_ast, cxx_record_decl); + if (metadata) + success = metadata->GetIsDynamicCXXType(); + else + { + is_complete = ClangASTType(m_ast, pointee_qual_type).GetCompleteType(); + if (is_complete) + success = cxx_record_decl->isDynamicClass(); + else + success = false; + } + } + + if (success) + { + if (dynamic_pointee_type) + dynamic_pointee_type->SetClangType(m_ast, pointee_qual_type); + return true; + } + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (check_objc) + { + if (dynamic_pointee_type) + dynamic_pointee_type->SetClangType(m_ast, pointee_qual_type); + return true; + } + break; + + default: + break; + } + } + } + if (dynamic_pointee_type) + dynamic_pointee_type->Clear(); + return false; +} + + +bool +ClangASTType::IsScalarType () const +{ + if (!IsValid()) + return false; + + return (GetTypeInfo (NULL) & eTypeIsScalar) != 0; +} + +bool +ClangASTType::IsTypedefType () const +{ + if (!IsValid()) + return false; + return GetQualType()->getTypeClass() == clang::Type::Typedef; +} + +bool +ClangASTType::IsVoidType () const +{ + if (!IsValid()) + return false; + return GetCanonicalQualType()->isVoidType(); +} + +bool +ClangASTType::IsPointerToScalarType () const +{ + if (!IsValid()) + return false; + + return IsPointerType() && GetPointeeType().IsScalarType(); +} + +bool +ClangASTType::IsArrayOfScalarType () const +{ + ClangASTType element_type; + if (IsArrayType(&element_type, NULL, NULL)) + return element_type.IsScalarType(); + return false; +} + + +bool +ClangASTType::GetCXXClassName (std::string &class_name) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + class_name.assign (cxx_record_decl->getIdentifier()->getNameStart()); + return true; + } + } + class_name.clear(); + return false; +} + + +bool +ClangASTType::IsCXXClassType () const +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + if (qual_type->getAsCXXRecordDecl() != NULL) + return true; + return false; +} + +bool +ClangASTType::IsBeingDefined () const +{ + if (!IsValid()) + return false; + QualType qual_type (GetCanonicalQualType()); + const clang::TagType *tag_type = dyn_cast<clang::TagType>(qual_type); + if (tag_type) + return tag_type->isBeingDefined(); + return false; +} + +bool +ClangASTType::IsObjCObjectPointerType (ClangASTType *class_type_ptr) +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + + if (qual_type->isObjCObjectPointerType()) + { + if (class_type_ptr) + { + if (!qual_type->isObjCClassType() && + !qual_type->isObjCIdType()) + { + const ObjCObjectPointerType *obj_pointer_type = dyn_cast<ObjCObjectPointerType>(qual_type); + if (obj_pointer_type == NULL) + class_type_ptr->Clear(); + else + class_type_ptr->SetClangType (m_ast, QualType(obj_pointer_type->getInterfaceType(), 0)); + } + } + return true; + } + if (class_type_ptr) + class_type_ptr->Clear(); + return false; +} + +bool +ClangASTType::GetObjCClassName (std::string &class_name) +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + + const ObjCObjectType *object_type = dyn_cast<ObjCObjectType>(qual_type); + if (object_type) + { + const ObjCInterfaceDecl *interface = object_type->getInterface(); + if (interface) + { + class_name = interface->getNameAsString(); + return true; + } + } + return false; +} + + +//---------------------------------------------------------------------- +// Type Completion +//---------------------------------------------------------------------- + +bool +ClangASTType::GetCompleteType () const +{ + if (!IsValid()) + return false; + const bool allow_completion = true; + return GetCompleteQualType (m_ast, GetQualType(), allow_completion); +} + +//---------------------------------------------------------------------- +// AST related queries +//---------------------------------------------------------------------- +size_t +ClangASTType::GetPointerByteSize () const +{ + if (m_ast) + return m_ast->getTypeSize(m_ast->VoidPtrTy) / 8; + return 0; +} + +ConstString +ClangASTType::GetConstQualifiedTypeName () const +{ + return GetConstTypeName (); +} + +ConstString +ClangASTType::GetConstTypeName () const +{ + if (IsValid()) + { + std::string type_name (GetTypeName()); + if (!type_name.empty()) + return ConstString (type_name.c_str()); + } + return ConstString("<invalid>"); +} + +std::string +ClangASTType::GetTypeName () const +{ + std::string type_name; + if (IsValid()) + { + PrintingPolicy printing_policy (m_ast->getPrintingPolicy()); + QualType qual_type(GetQualType()); + printing_policy.SuppressTagKeyword = true; + printing_policy.LangOpts.WChar = true; + const TypedefType *typedef_type = qual_type->getAs<TypedefType>(); + if (typedef_type) + { + const TypedefNameDecl *typedef_decl = typedef_type->getDecl(); + type_name = typedef_decl->getQualifiedNameAsString(printing_policy); + } + else + { + type_name = qual_type.getAsString(printing_policy); + } + } + return type_name; +} + + +uint32_t +ClangASTType::GetTypeInfo (ClangASTType *pointee_or_element_clang_type) const +{ + if (!IsValid()) + return 0; + + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->Clear(); + + QualType qual_type (GetQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + { + const BuiltinType *builtin_type = dyn_cast<BuiltinType>(qual_type->getCanonicalTypeInternal()); + + uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue; + switch (builtin_type->getKind()) + { + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, m_ast->ObjCBuiltinClassTy); + builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; + break; + + case clang::BuiltinType::ObjCSel: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, m_ast->CharTy); + builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; + break; + + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + builtin_type_flags |= eTypeIsScalar; + if (builtin_type->isInteger()) + { + builtin_type_flags |= eTypeIsInteger; + if (builtin_type->isSignedInteger()) + builtin_type_flags |= eTypeIsSigned; + } + else if (builtin_type->isFloatingPoint()) + builtin_type_flags |= eTypeIsFloat; + break; + default: + break; + } + return builtin_type_flags; + } + + case clang::Type::BlockPointer: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, qual_type->getPointeeType()); + return eTypeIsPointer | eTypeHasChildren | eTypeIsBlock; + + case clang::Type::Complex: + { + uint32_t complex_type_flags = eTypeIsBuiltIn | eTypeHasValue | eTypeIsComplex; + const ComplexType *complex_type = dyn_cast<ComplexType>(qual_type->getCanonicalTypeInternal()); + if (complex_type) + { + QualType complex_element_type (complex_type->getElementType()); + if (complex_element_type->isIntegerType()) + complex_type_flags |= eTypeIsFloat; + else if (complex_element_type->isFloatingType()) + complex_type_flags |= eTypeIsInteger; + } + return complex_type_flags; + } + break; + + case clang::Type::ConstantArray: + case clang::Type::DependentSizedArray: + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, cast<ArrayType>(qual_type.getTypePtr())->getElementType()); + return eTypeHasChildren | eTypeIsArray; + + case clang::Type::DependentName: return 0; + case clang::Type::DependentSizedExtVector: return eTypeHasChildren | eTypeIsVector; + case clang::Type::DependentTemplateSpecialization: return eTypeIsTemplate; + case clang::Type::Decltype: return 0; + + case clang::Type::Enum: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, cast<EnumType>(qual_type)->getDecl()->getIntegerType()); + return eTypeIsEnumeration | eTypeHasValue; + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetTypeInfo (pointee_or_element_clang_type); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetTypeInfo (pointee_or_element_clang_type); + + case clang::Type::FunctionProto: return eTypeIsFuncPrototype | eTypeHasValue; + case clang::Type::FunctionNoProto: return eTypeIsFuncPrototype | eTypeHasValue; + case clang::Type::InjectedClassName: return 0; + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, cast<ReferenceType>(qual_type.getTypePtr())->getPointeeType()); + return eTypeHasChildren | eTypeIsReference | eTypeHasValue; + + case clang::Type::MemberPointer: return eTypeIsPointer | eTypeIsMember | eTypeHasValue; + + case clang::Type::ObjCObjectPointer: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, qual_type->getPointeeType()); + return eTypeHasChildren | eTypeIsObjC | eTypeIsClass | eTypeIsPointer | eTypeHasValue; + + case clang::Type::ObjCObject: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; + case clang::Type::ObjCInterface: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; + + case clang::Type::Pointer: + if (pointee_or_element_clang_type) + pointee_or_element_clang_type->SetClangType(m_ast, qual_type->getPointeeType()); + return eTypeHasChildren | eTypeIsPointer | eTypeHasValue; + + case clang::Type::Record: + if (qual_type->getAsCXXRecordDecl()) + return eTypeHasChildren | eTypeIsClass | eTypeIsCPlusPlus; + else + return eTypeHasChildren | eTypeIsStructUnion; + break; + case clang::Type::SubstTemplateTypeParm: return eTypeIsTemplate; + case clang::Type::TemplateTypeParm: return eTypeIsTemplate; + case clang::Type::TemplateSpecialization: return eTypeIsTemplate; + + case clang::Type::Typedef: + return eTypeIsTypedef | ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetTypeInfo (pointee_or_element_clang_type); + case clang::Type::TypeOfExpr: return 0; + case clang::Type::TypeOf: return 0; + case clang::Type::UnresolvedUsing: return 0; + + case clang::Type::ExtVector: + case clang::Type::Vector: + { + uint32_t vector_type_flags = eTypeHasChildren | eTypeIsVector; + const VectorType *vector_type = dyn_cast<VectorType>(qual_type->getCanonicalTypeInternal()); + if (vector_type) + { + if (vector_type->isIntegerType()) + vector_type_flags |= eTypeIsFloat; + else if (vector_type->isFloatingType()) + vector_type_flags |= eTypeIsInteger; + } + return vector_type_flags; + } + default: return 0; + } + return 0; +} + + + +lldb::LanguageType +ClangASTType::GetMinimumLanguage () +{ + if (!IsValid()) + return lldb::eLanguageTypeC; + + // If the type is a reference, then resolve it to what it refers to first: + QualType qual_type (GetCanonicalQualType().getNonReferenceType()); + if (qual_type->isAnyPointerType()) + { + if (qual_type->isObjCObjectPointerType()) + return lldb::eLanguageTypeObjC; + + QualType pointee_type (qual_type->getPointeeType()); + if (pointee_type->getPointeeCXXRecordDecl() != NULL) + return lldb::eLanguageTypeC_plus_plus; + if (pointee_type->isObjCObjectOrInterfaceType()) + return lldb::eLanguageTypeObjC; + if (pointee_type->isObjCClassType()) + return lldb::eLanguageTypeObjC; + if (pointee_type.getTypePtr() == m_ast->ObjCBuiltinIdTy.getTypePtr()) + return lldb::eLanguageTypeObjC; + } + else + { + if (qual_type->isObjCObjectOrInterfaceType()) + return lldb::eLanguageTypeObjC; + if (qual_type->getAsCXXRecordDecl()) + return lldb::eLanguageTypeC_plus_plus; + switch (qual_type->getTypeClass()) + { + default: + break; + case clang::Type::Builtin: + switch (cast<BuiltinType>(qual_type)->getKind()) + { + default: + case BuiltinType::Void: + case BuiltinType::Bool: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::WChar_U: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::UShort: + case BuiltinType::UInt: + case BuiltinType::ULong: + case BuiltinType::ULongLong: + case BuiltinType::UInt128: + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::WChar_S: + case BuiltinType::Short: + case BuiltinType::Int: + case BuiltinType::Long: + case BuiltinType::LongLong: + case BuiltinType::Int128: + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + break; + + case BuiltinType::NullPtr: + return eLanguageTypeC_plus_plus; + + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + return eLanguageTypeObjC; + + case BuiltinType::Dependent: + case BuiltinType::Overload: + case BuiltinType::BoundMember: + case BuiltinType::UnknownAny: + break; + } + break; + case clang::Type::Typedef: + return ClangASTType(m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetMinimumLanguage(); + } + } + return lldb::eLanguageTypeC; +} + +lldb::TypeClass +ClangASTType::GetTypeClass () const +{ + if (!IsValid()) + return lldb::eTypeClassInvalid; + + QualType qual_type(GetQualType()); + + switch (qual_type->getTypeClass()) + { + case clang::Type::UnaryTransform: break; + case clang::Type::FunctionNoProto: return lldb::eTypeClassFunction; + case clang::Type::FunctionProto: return lldb::eTypeClassFunction; + case clang::Type::IncompleteArray: return lldb::eTypeClassArray; + case clang::Type::VariableArray: return lldb::eTypeClassArray; + case clang::Type::ConstantArray: return lldb::eTypeClassArray; + case clang::Type::DependentSizedArray: return lldb::eTypeClassArray; + case clang::Type::DependentSizedExtVector: return lldb::eTypeClassVector; + case clang::Type::ExtVector: return lldb::eTypeClassVector; + case clang::Type::Vector: return lldb::eTypeClassVector; + case clang::Type::Builtin: return lldb::eTypeClassBuiltin; + case clang::Type::ObjCObjectPointer: return lldb::eTypeClassObjCObjectPointer; + case clang::Type::BlockPointer: return lldb::eTypeClassBlockPointer; + case clang::Type::Pointer: return lldb::eTypeClassPointer; + case clang::Type::LValueReference: return lldb::eTypeClassReference; + case clang::Type::RValueReference: return lldb::eTypeClassReference; + case clang::Type::MemberPointer: return lldb::eTypeClassMemberPointer; + case clang::Type::Complex: + if (qual_type->isComplexType()) + return lldb::eTypeClassComplexFloat; + else + return lldb::eTypeClassComplexInteger; + case clang::Type::ObjCObject: return lldb::eTypeClassObjCObject; + case clang::Type::ObjCInterface: return lldb::eTypeClassObjCInterface; + case clang::Type::Record: + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + if (record_decl->isUnion()) + return lldb::eTypeClassUnion; + else if (record_decl->isStruct()) + return lldb::eTypeClassStruct; + else + return lldb::eTypeClassClass; + } + break; + case clang::Type::Enum: return lldb::eTypeClassEnumeration; + case clang::Type::Typedef: return lldb::eTypeClassTypedef; + case clang::Type::UnresolvedUsing: break; + case clang::Type::Paren: + return ClangASTType(m_ast, cast<ParenType>(qual_type)->desugar()).GetTypeClass(); + case clang::Type::Elaborated: + return ClangASTType(m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetTypeClass(); + + case clang::Type::Attributed: break; + case clang::Type::TemplateTypeParm: break; + case clang::Type::SubstTemplateTypeParm: break; + case clang::Type::SubstTemplateTypeParmPack:break; + case clang::Type::Auto: break; + case clang::Type::InjectedClassName: break; + case clang::Type::DependentName: break; + case clang::Type::DependentTemplateSpecialization: break; + case clang::Type::PackExpansion: break; + + case clang::Type::TypeOfExpr: break; + case clang::Type::TypeOf: break; + case clang::Type::Decltype: break; + case clang::Type::TemplateSpecialization: break; + case clang::Type::Atomic: break; + } + // We don't know hot to display this type... + return lldb::eTypeClassOther; + +} + +void +ClangASTType::SetClangType (clang::ASTContext *ast, clang::QualType qual_type) +{ + m_ast = ast; + m_type = qual_type.getAsOpaquePtr(); +} + +unsigned +ClangASTType::GetTypeQualifiers() const +{ + if (IsValid()) + return GetQualType().getQualifiers().getCVRQualifiers(); + return 0; +} + +//---------------------------------------------------------------------- +// Creating related types +//---------------------------------------------------------------------- + +ClangASTType +ClangASTType::AddConstModifier () const +{ + if (m_type) + { + QualType result(GetQualType()); + result.addConst(); + return ClangASTType (m_ast, result); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::AddRestrictModifier () const +{ + if (m_type) + { + QualType result(GetQualType()); + result.getQualifiers().setRestrict (true); + return ClangASTType (m_ast, result); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::AddVolatileModifier () const +{ + if (m_type) + { + QualType result(GetQualType()); + result.getQualifiers().setVolatile (true); + return ClangASTType (m_ast, result); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetArrayElementType (uint64_t& stride) const +{ + if (IsValid()) + { + QualType qual_type(GetCanonicalQualType()); + + ClangASTType element_type (m_ast, qual_type.getTypePtr()->getArrayElementTypeNoTypeQual()->getCanonicalTypeUnqualified()); + + // TODO: the real stride will be >= this value.. find the real one! + stride = element_type.GetByteSize(); + + return element_type; + + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetCanonicalType () const +{ + if (IsValid()) + return ClangASTType (m_ast, GetCanonicalQualType()); + return ClangASTType(); +} + +static QualType +GetFullyUnqualifiedType_Impl (ASTContext *ast, QualType qual_type) +{ + if (qual_type->isPointerType()) + qual_type = ast->getPointerType(GetFullyUnqualifiedType_Impl(ast, qual_type->getPointeeType())); + else + qual_type = qual_type.getUnqualifiedType(); + qual_type.removeLocalConst(); + qual_type.removeLocalRestrict(); + qual_type.removeLocalVolatile(); + return qual_type; +} + +ClangASTType +ClangASTType::GetFullyUnqualifiedType () const +{ + if (IsValid()) + return ClangASTType(m_ast, GetFullyUnqualifiedType_Impl(m_ast, GetQualType())); + return ClangASTType(); +} + + +int +ClangASTType::GetFunctionArgumentCount () const +{ + if (IsValid()) + { + const FunctionProtoType* func = dyn_cast<FunctionProtoType>(GetCanonicalQualType()); + if (func) + return func->getNumArgs(); + } + return -1; +} + +ClangASTType +ClangASTType::GetFunctionArgumentTypeAtIndex (size_t idx) +{ + if (IsValid()) + { + const FunctionProtoType* func = dyn_cast<FunctionProtoType>(GetCanonicalQualType()); + if (func) + { + const uint32_t num_args = func->getNumArgs(); + if (idx < num_args) + return ClangASTType(m_ast, func->getArgType(idx)); + } + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetFunctionReturnType () const +{ + if (IsValid()) + { + QualType qual_type(GetCanonicalQualType()); + const FunctionProtoType* func = dyn_cast<FunctionProtoType>(qual_type.getTypePtr()); + if (func) + return ClangASTType(m_ast, func->getResultType()); + } + return ClangASTType(); +} + + +ClangASTType +ClangASTType::GetLValueReferenceType () const +{ + if (IsValid()) + { + return ClangASTType(m_ast, m_ast->getLValueReferenceType(GetQualType())); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetRValueReferenceType () const +{ + if (IsValid()) + { + return ClangASTType(m_ast, m_ast->getRValueReferenceType(GetQualType())); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetNonReferenceType () const +{ + if (IsValid()) + return ClangASTType(m_ast, GetQualType().getNonReferenceType()); + return ClangASTType(); +} + +ClangASTType +ClangASTType::CreateTypedefType (const char *typedef_name, + clang::DeclContext *decl_ctx) const +{ + if (IsValid() && typedef_name && typedef_name[0]) + { + QualType qual_type (GetQualType()); + if (decl_ctx == NULL) + decl_ctx = m_ast->getTranslationUnitDecl(); + TypedefDecl *decl = TypedefDecl::Create (*m_ast, + decl_ctx, + SourceLocation(), + SourceLocation(), + &m_ast->Idents.get(typedef_name), + m_ast->getTrivialTypeSourceInfo(qual_type)); + + decl->setAccess(AS_public); // TODO respect proper access specifier + + // Get a uniqued QualType for the typedef decl type + return ClangASTType (m_ast, m_ast->getTypedefType (decl)); + } + return ClangASTType(); + +} + +ClangASTType +ClangASTType::GetPointeeType () const +{ + if (m_type) + { + QualType qual_type(GetQualType()); + return ClangASTType (m_ast, qual_type.getTypePtr()->getPointeeType()); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetPointerType () const +{ + if (IsValid()) + { + QualType qual_type (GetQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + return ClangASTType(m_ast, m_ast->getObjCObjectPointerType(qual_type).getAsOpaquePtr()); + + default: + return ClangASTType(m_ast, m_ast->getPointerType(qual_type).getAsOpaquePtr()); + } + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetTypedefedType () const +{ + if (IsValid()) + { + const TypedefType *typedef_type = dyn_cast<TypedefType>(GetQualType()); + if (typedef_type) + return ClangASTType (m_ast, typedef_type->getDecl()->getUnderlyingType()); + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::RemoveFastQualifiers () const +{ + if (m_type) + { + QualType qual_type(GetQualType()); + qual_type.getQualifiers().removeFastQualifiers(); + return ClangASTType (m_ast, qual_type); + } + return ClangASTType(); +} + + +//---------------------------------------------------------------------- +// Create related types using the current type's AST +//---------------------------------------------------------------------- + +ClangASTType +ClangASTType::GetBasicTypeFromAST (lldb::BasicType basic_type) const +{ + if (IsValid()) + return ClangASTContext::GetBasicType(m_ast, basic_type); + return ClangASTType(); +} +//---------------------------------------------------------------------- +// Exploring the type +//---------------------------------------------------------------------- + +uint64_t +ClangASTType::GetBitSize () const +{ + if (GetCompleteType ()) + { + QualType qual_type(GetCanonicalQualType()); + const uint32_t bit_size = m_ast->getTypeSize (qual_type); + if (bit_size == 0) + { + if (qual_type->isIncompleteArrayType()) + return m_ast->getTypeSize (qual_type->getArrayElementTypeNoTypeQual()->getCanonicalTypeUnqualified()); + } + if (qual_type->isObjCObjectOrInterfaceType()) + return bit_size + m_ast->getTypeSize(m_ast->ObjCBuiltinClassTy); + return bit_size; + } + return 0; +} + +uint64_t +ClangASTType::GetByteSize () const +{ + return (GetBitSize () + 7) / 8; +} + +size_t +ClangASTType::GetTypeBitAlign () const +{ + if (GetCompleteType ()) + return m_ast->getTypeAlign(GetQualType()); + return 0; +} + + +lldb::Encoding +ClangASTType::GetEncoding (uint64_t &count) const +{ + if (!IsValid()) + return lldb::eEncodingInvalid; + + count = 1; + QualType qual_type(GetCanonicalQualType()); + + switch (qual_type->getTypeClass()) + { + case clang::Type::UnaryTransform: + break; + + case clang::Type::FunctionNoProto: + case clang::Type::FunctionProto: + break; + + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + break; + + case clang::Type::ConstantArray: + break; + + case clang::Type::ExtVector: + case clang::Type::Vector: + // TODO: Set this to more than one??? + break; + + case clang::Type::Builtin: + switch (cast<BuiltinType>(qual_type)->getKind()) + { + default: assert(0 && "Unknown builtin type!"); + case BuiltinType::Void: + break; + + case BuiltinType::Bool: + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::WChar_S: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::Short: + case BuiltinType::Int: + case BuiltinType::Long: + case BuiltinType::LongLong: + case BuiltinType::Int128: return lldb::eEncodingSint; + + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::WChar_U: + case BuiltinType::UShort: + case BuiltinType::UInt: + case BuiltinType::ULong: + case BuiltinType::ULongLong: + case BuiltinType::UInt128: return lldb::eEncodingUint; + + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: return lldb::eEncodingIEEE754; + + case BuiltinType::ObjCClass: + case BuiltinType::ObjCId: + case BuiltinType::ObjCSel: return lldb::eEncodingUint; + + case BuiltinType::NullPtr: return lldb::eEncodingUint; + } + break; + // All pointer types are represented as unsigned integer encodings. + // We may nee to add a eEncodingPointer if we ever need to know the + // difference + case clang::Type::ObjCObjectPointer: + case clang::Type::BlockPointer: + case clang::Type::Pointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: return lldb::eEncodingUint; + case clang::Type::Complex: + { + lldb::Encoding encoding = lldb::eEncodingIEEE754; + if (qual_type->isComplexType()) + encoding = lldb::eEncodingIEEE754; + else + { + const ComplexType *complex_type = qual_type->getAsComplexIntegerType(); + if (complex_type) + encoding = ClangASTType(m_ast, complex_type->getElementType()).GetEncoding(count); + else + encoding = lldb::eEncodingSint; + } + count = 2; + return encoding; + } + + case clang::Type::ObjCInterface: break; + case clang::Type::Record: break; + case clang::Type::Enum: return lldb::eEncodingSint; + case clang::Type::Typedef: + return ClangASTType(m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetEncoding(count); + + case clang::Type::Elaborated: + return ClangASTType(m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetEncoding(count); + + case clang::Type::Paren: + return ClangASTType(m_ast, cast<ParenType>(qual_type)->desugar()).GetEncoding(count); + + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::UnresolvedUsing: + case clang::Type::Attributed: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::Auto: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::TemplateSpecialization: + case clang::Type::Atomic: + break; + + } + count = 0; + return lldb::eEncodingInvalid; +} + +lldb::Format +ClangASTType::GetFormat () const +{ + if (!IsValid()) + return lldb::eFormatDefault; + + QualType qual_type(GetCanonicalQualType()); + + switch (qual_type->getTypeClass()) + { + case clang::Type::UnaryTransform: + break; + + case clang::Type::FunctionNoProto: + case clang::Type::FunctionProto: + break; + + case clang::Type::IncompleteArray: + case clang::Type::VariableArray: + break; + + case clang::Type::ConstantArray: + return lldb::eFormatVoid; // no value + + case clang::Type::ExtVector: + case clang::Type::Vector: + break; + + case clang::Type::Builtin: + switch (cast<BuiltinType>(qual_type)->getKind()) + { + //default: assert(0 && "Unknown builtin type!"); + case BuiltinType::UnknownAny: + case BuiltinType::Void: + case BuiltinType::BoundMember: + break; + + case BuiltinType::Bool: return lldb::eFormatBoolean; + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::WChar_S: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::WChar_U: return lldb::eFormatChar; + case BuiltinType::Char16: return lldb::eFormatUnicode16; + case BuiltinType::Char32: return lldb::eFormatUnicode32; + case BuiltinType::UShort: return lldb::eFormatUnsigned; + case BuiltinType::Short: return lldb::eFormatDecimal; + case BuiltinType::UInt: return lldb::eFormatUnsigned; + case BuiltinType::Int: return lldb::eFormatDecimal; + case BuiltinType::ULong: return lldb::eFormatUnsigned; + case BuiltinType::Long: return lldb::eFormatDecimal; + case BuiltinType::ULongLong: return lldb::eFormatUnsigned; + case BuiltinType::LongLong: return lldb::eFormatDecimal; + case BuiltinType::UInt128: return lldb::eFormatUnsigned; + case BuiltinType::Int128: return lldb::eFormatDecimal; + case BuiltinType::Float: return lldb::eFormatFloat; + case BuiltinType::Double: return lldb::eFormatFloat; + case BuiltinType::LongDouble: return lldb::eFormatFloat; + case BuiltinType::NullPtr: + case BuiltinType::Overload: + case BuiltinType::Dependent: + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::Half: + case BuiltinType::ARCUnbridgedCast: + case BuiltinType::PseudoObject: + case BuiltinType::BuiltinFn: + case BuiltinType::OCLEvent: + case BuiltinType::OCLImage1d: + case BuiltinType::OCLImage1dArray: + case BuiltinType::OCLImage1dBuffer: + case BuiltinType::OCLImage2d: + case BuiltinType::OCLImage2dArray: + case BuiltinType::OCLImage3d: + case BuiltinType::OCLSampler: + return lldb::eFormatHex; + } + break; + case clang::Type::ObjCObjectPointer: return lldb::eFormatHex; + case clang::Type::BlockPointer: return lldb::eFormatHex; + case clang::Type::Pointer: return lldb::eFormatHex; + case clang::Type::LValueReference: + case clang::Type::RValueReference: return lldb::eFormatHex; + case clang::Type::MemberPointer: break; + case clang::Type::Complex: + { + if (qual_type->isComplexType()) + return lldb::eFormatComplex; + else + return lldb::eFormatComplexInteger; + } + case clang::Type::ObjCInterface: break; + case clang::Type::Record: break; + case clang::Type::Enum: return lldb::eFormatEnum; + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetFormat(); + case clang::Type::Auto: + return ClangASTType (m_ast, cast<AutoType>(qual_type)->desugar()).GetFormat(); + case clang::Type::Paren: + return ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).GetFormat(); + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetFormat(); + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::UnresolvedUsing: + case clang::Type::Attributed: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::TemplateSpecialization: + case clang::Type::Atomic: + break; + } + // We don't know hot to display this type... + return lldb::eFormatBytes; +} + +static bool +ObjCDeclHasIVars (ObjCInterfaceDecl *class_interface_decl, bool check_superclass) +{ + while (class_interface_decl) + { + if (class_interface_decl->ivar_size() > 0) + return true; + + if (check_superclass) + class_interface_decl = class_interface_decl->getSuperClass(); + else + break; + } + return false; +} + +uint32_t +ClangASTType::GetNumChildren (bool omit_empty_base_classes) const +{ + if (!IsValid()) + return 0; + + uint32_t num_children = 0; + QualType qual_type(GetQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + switch (cast<BuiltinType>(qual_type)->getKind()) + { + case BuiltinType::ObjCId: // child is Class + case BuiltinType::ObjCClass: // child is Class + num_children = 1; + break; + + default: + break; + } + break; + + case clang::Type::Complex: return 0; + + case clang::Type::Record: + if (GetCompleteQualType (m_ast, qual_type)) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + if (cxx_record_decl) + { + if (omit_empty_base_classes) + { + // Check each base classes to see if it or any of its + // base classes contain any fields. This can help + // limit the noise in variable views by not having to + // show base classes that contain no members. + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + + // Skip empty base classes + if (ClangASTContext::RecordHasFields(base_class_decl) == false) + continue; + + num_children++; + } + } + else + { + // Include all base classes + num_children += cxx_record_decl->getNumBases(); + } + + } + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) + ++num_children; + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteQualType (m_ast, qual_type)) + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + if (superclass_interface_decl) + { + if (omit_empty_base_classes) + { + if (ObjCDeclHasIVars (superclass_interface_decl, true)) + ++num_children; + } + else + ++num_children; + } + + num_children += class_interface_decl->ivar_size(); + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + { + const ObjCObjectPointerType *pointer_type = cast<ObjCObjectPointerType>(qual_type.getTypePtr()); + QualType pointee_type = pointer_type->getPointeeType(); + uint32_t num_pointee_children = ClangASTType (m_ast,pointee_type).GetNumChildren (omit_empty_base_classes); + // If this type points to a simple type, then it has 1 child + if (num_pointee_children == 0) + num_children = 1; + else + num_children = num_pointee_children; + } + break; + + case clang::Type::Vector: + case clang::Type::ExtVector: + num_children = cast<VectorType>(qual_type.getTypePtr())->getNumElements(); + break; + + case clang::Type::ConstantArray: + num_children = cast<ConstantArrayType>(qual_type.getTypePtr())->getSize().getLimitedValue(); + break; + + case clang::Type::Pointer: + { + const PointerType *pointer_type = cast<PointerType>(qual_type.getTypePtr()); + QualType pointee_type (pointer_type->getPointeeType()); + uint32_t num_pointee_children = ClangASTType (m_ast,pointee_type).GetNumChildren (omit_empty_base_classes); + if (num_pointee_children == 0) + { + // We have a pointer to a pointee type that claims it has no children. + // We will want to look at + num_children = ClangASTType (m_ast, pointee_type).GetNumPointeeChildren(); + } + else + num_children = num_pointee_children; + } + break; + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + { + const ReferenceType *reference_type = cast<ReferenceType>(qual_type.getTypePtr()); + QualType pointee_type = reference_type->getPointeeType(); + uint32_t num_pointee_children = ClangASTType (m_ast, pointee_type).GetNumChildren (omit_empty_base_classes); + // If this type points to a simple type, then it has 1 child + if (num_pointee_children == 0) + num_children = 1; + else + num_children = num_pointee_children; + } + break; + + + case clang::Type::Typedef: + num_children = ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumChildren (omit_empty_base_classes); + break; + + case clang::Type::Elaborated: + num_children = ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumChildren (omit_empty_base_classes); + break; + + case clang::Type::Paren: + num_children = ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).GetNumChildren (omit_empty_base_classes); + break; + default: + break; + } + return num_children; +} + +lldb::BasicType +ClangASTType::GetBasicTypeEnumeration () const +{ + if (IsValid()) + { + QualType qual_type(GetQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + if (type_class == clang::Type::Builtin) + { + switch (cast<clang::BuiltinType>(qual_type)->getKind()) + { + case clang::BuiltinType::Void: return eBasicTypeVoid; + case clang::BuiltinType::Bool: return eBasicTypeBool; + case clang::BuiltinType::Char_S: return eBasicTypeSignedChar; + case clang::BuiltinType::Char_U: return eBasicTypeUnsignedChar; + case clang::BuiltinType::Char16: return eBasicTypeChar16; + case clang::BuiltinType::Char32: return eBasicTypeChar32; + case clang::BuiltinType::UChar: return eBasicTypeUnsignedChar; + case clang::BuiltinType::SChar: return eBasicTypeSignedChar; + case clang::BuiltinType::WChar_S: return eBasicTypeSignedWChar; + case clang::BuiltinType::WChar_U: return eBasicTypeUnsignedWChar; + case clang::BuiltinType::Short: return eBasicTypeShort; + case clang::BuiltinType::UShort: return eBasicTypeUnsignedShort; + case clang::BuiltinType::Int: return eBasicTypeInt; + case clang::BuiltinType::UInt: return eBasicTypeUnsignedInt; + case clang::BuiltinType::Long: return eBasicTypeLong; + case clang::BuiltinType::ULong: return eBasicTypeUnsignedLong; + case clang::BuiltinType::LongLong: return eBasicTypeLongLong; + case clang::BuiltinType::ULongLong: return eBasicTypeUnsignedLongLong; + case clang::BuiltinType::Int128: return eBasicTypeInt128; + case clang::BuiltinType::UInt128: return eBasicTypeUnsignedInt128; + + case clang::BuiltinType::Half: return eBasicTypeHalf; + case clang::BuiltinType::Float: return eBasicTypeFloat; + case clang::BuiltinType::Double: return eBasicTypeDouble; + case clang::BuiltinType::LongDouble:return eBasicTypeLongDouble; + + case clang::BuiltinType::NullPtr: return eBasicTypeNullPtr; + case clang::BuiltinType::ObjCId: return eBasicTypeObjCID; + case clang::BuiltinType::ObjCClass: return eBasicTypeObjCClass; + case clang::BuiltinType::ObjCSel: return eBasicTypeObjCSel; + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::BuiltinFn: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLImage1d: + case clang::BuiltinType::OCLImage1dArray: + case clang::BuiltinType::OCLImage1dBuffer: + case clang::BuiltinType::OCLImage2d: + case clang::BuiltinType::OCLImage2dArray: + case clang::BuiltinType::OCLImage3d: + case clang::BuiltinType::OCLSampler: + return eBasicTypeOther; + } + } + } + return eBasicTypeInvalid; +} + + +#pragma mark Aggregate Types + +uint32_t +ClangASTType::GetNumDirectBaseClasses () const +{ + if (!IsValid()) + return 0; + + uint32_t count = 0; + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + count = cxx_record_decl->getNumBases(); + } + break; + + case clang::Type::ObjCObjectPointer: + if (GetCompleteType()) + { + const ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + if (class_interface_decl && class_interface_decl->getSuperClass()) + count = 1; + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + const ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl && class_interface_decl->getSuperClass()) + count = 1; + } + } + break; + + + case clang::Type::Typedef: + count = ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumDirectBaseClasses (); + break; + + case clang::Type::Elaborated: + count = ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumDirectBaseClasses (); + break; + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetNumDirectBaseClasses (); + + default: + break; + } + return count; +} + +uint32_t +ClangASTType::GetNumVirtualBaseClasses () const +{ + if (!IsValid()) + return 0; + + uint32_t count = 0; + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + count = cxx_record_decl->getNumVBases(); + } + break; + + case clang::Type::Typedef: + count = ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumVirtualBaseClasses(); + break; + + case clang::Type::Elaborated: + count = ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumVirtualBaseClasses(); + break; + + case clang::Type::Paren: + count = ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetNumVirtualBaseClasses(); + break; + + default: + break; + } + return count; +} + +uint32_t +ClangASTType::GetNumFields () const +{ + if (!IsValid()) + return 0; + + uint32_t count = 0; + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const RecordType *record_type = dyn_cast<RecordType>(qual_type.getTypePtr()); + if (record_type) + { + RecordDecl *record_decl = record_type->getDecl(); + if (record_decl) + { + uint32_t field_idx = 0; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) + ++field_idx; + count = field_idx; + } + } + } + break; + + case clang::Type::Typedef: + count = ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumFields(); + break; + + case clang::Type::Elaborated: + count = ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumFields(); + break; + + case clang::Type::Paren: + count = ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetNumFields(); + break; + + case clang::Type::ObjCObjectPointer: + if (GetCompleteType()) + { + const ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + + if (class_interface_decl) + count = class_interface_decl->ivar_size(); + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + count = class_interface_decl->ivar_size(); + } + } + break; + + default: + break; + } + return count; +} + +ClangASTType +ClangASTType::GetDirectBaseClassAtIndex (size_t idx, uint32_t *bit_offset_ptr) const +{ + if (!IsValid()) + return ClangASTType(); + + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + uint32_t curr_idx = 0; + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class, ++curr_idx) + { + if (curr_idx == idx) + { + if (bit_offset_ptr) + { + const ASTRecordLayout &record_layout = m_ast->getASTRecordLayout(cxx_record_decl); + const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + if (base_class->isVirtual()) + *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8; + else + *bit_offset_ptr = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8; + } + return ClangASTType (m_ast, base_class->getType()); + } + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + if (idx == 0 && GetCompleteType()) + { + const ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + if (class_interface_decl) + { + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + if (superclass_interface_decl) + { + if (bit_offset_ptr) + *bit_offset_ptr = 0; + return ClangASTType (m_ast, m_ast->getObjCInterfaceType(superclass_interface_decl)); + } + } + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (idx == 0 && GetCompleteType()) + { + const ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + if (superclass_interface_decl) + { + if (bit_offset_ptr) + *bit_offset_ptr = 0; + return ClangASTType (m_ast, m_ast->getObjCInterfaceType(superclass_interface_decl)); + } + } + } + } + break; + + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetDirectBaseClassAtIndex (idx, bit_offset_ptr); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetDirectBaseClassAtIndex (idx, bit_offset_ptr); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetDirectBaseClassAtIndex (idx, bit_offset_ptr); + + default: + break; + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::GetVirtualBaseClassAtIndex (size_t idx, uint32_t *bit_offset_ptr) const +{ + if (!IsValid()) + return ClangASTType(); + + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + uint32_t curr_idx = 0; + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->vbases_begin(), base_class_end = cxx_record_decl->vbases_end(); + base_class != base_class_end; + ++base_class, ++curr_idx) + { + if (curr_idx == idx) + { + if (bit_offset_ptr) + { + const ASTRecordLayout &record_layout = m_ast->getASTRecordLayout(cxx_record_decl); + const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8; + + } + return ClangASTType (m_ast, base_class->getType()); + } + } + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetVirtualBaseClassAtIndex (idx, bit_offset_ptr); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetVirtualBaseClassAtIndex (idx, bit_offset_ptr); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetVirtualBaseClassAtIndex (idx, bit_offset_ptr); + + default: + break; + } + return ClangASTType(); +} + +static clang_type_t +GetObjCFieldAtIndex (clang::ASTContext *ast, + ObjCInterfaceDecl *class_interface_decl, + size_t idx, + std::string& name, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) +{ + if (class_interface_decl) + { + if (idx < (class_interface_decl->ivar_size())) + { + ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); + uint32_t ivar_idx = 0; + + for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++ivar_idx) + { + if (ivar_idx == idx) + { + const ObjCIvarDecl* ivar_decl = *ivar_pos; + + QualType ivar_qual_type(ivar_decl->getType()); + + name.assign(ivar_decl->getNameAsString()); + + if (bit_offset_ptr) + { + const ASTRecordLayout &interface_layout = ast->getASTObjCInterfaceLayout(class_interface_decl); + *bit_offset_ptr = interface_layout.getFieldOffset (ivar_idx); + } + + const bool is_bitfield = ivar_pos->isBitField(); + + if (bitfield_bit_size_ptr) + { + *bitfield_bit_size_ptr = 0; + + if (is_bitfield && ast) + { + Expr *bitfield_bit_size_expr = ivar_pos->getBitWidth(); + llvm::APSInt bitfield_apsint; + if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *ast)) + { + *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); + } + } + } + if (is_bitfield_ptr) + *is_bitfield_ptr = is_bitfield; + + return ivar_qual_type.getAsOpaquePtr(); + } + } + } + } + return NULL; +} + +ClangASTType +ClangASTType::GetFieldAtIndex (size_t idx, + std::string& name, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) const +{ + if (!IsValid()) + return ClangASTType(); + + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType()) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + uint32_t field_idx = 0; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx) + { + if (idx == field_idx) + { + // Print the member type if requested + // Print the member name and equal sign + name.assign(field->getNameAsString()); + + // Figure out the type byte size (field_type_info.first) and + // alignment (field_type_info.second) from the AST context. + if (bit_offset_ptr) + { + const ASTRecordLayout &record_layout = m_ast->getASTRecordLayout(record_decl); + *bit_offset_ptr = record_layout.getFieldOffset (field_idx); + } + + const bool is_bitfield = field->isBitField(); + + if (bitfield_bit_size_ptr) + { + *bitfield_bit_size_ptr = 0; + + if (is_bitfield) + { + Expr *bitfield_bit_size_expr = field->getBitWidth(); + llvm::APSInt bitfield_apsint; + if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *m_ast)) + { + *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); + } + } + } + if (is_bitfield_ptr) + *is_bitfield_ptr = is_bitfield; + + return ClangASTType (m_ast, field->getType()); + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + if (GetCompleteType()) + { + const ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + return ClangASTType (m_ast, GetObjCFieldAtIndex(m_ast, class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + return ClangASTType (m_ast, GetObjCFieldAtIndex(m_ast, class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); + } + } + break; + + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()). + GetFieldAtIndex (idx, + name, + bit_offset_ptr, + bitfield_bit_size_ptr, + is_bitfield_ptr); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()). + GetFieldAtIndex (idx, + name, + bit_offset_ptr, + bitfield_bit_size_ptr, + is_bitfield_ptr); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()). + GetFieldAtIndex (idx, + name, + bit_offset_ptr, + bitfield_bit_size_ptr, + is_bitfield_ptr); + + default: + break; + } + return ClangASTType(); +} + +uint32_t +ClangASTType::GetIndexOfFieldWithName (const char* name, + ClangASTType* field_clang_type_ptr, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) const +{ + unsigned count = GetNumFields(); + std::string field_name; + for (unsigned index = 0; index < count; index++) + { + ClangASTType field_clang_type (GetFieldAtIndex(index, field_name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); + if (strcmp(field_name.c_str(), name) == 0) + { + if (field_clang_type_ptr) + *field_clang_type_ptr = field_clang_type; + return index; + } + } + return UINT32_MAX; +} + +// If a pointer to a pointee type (the clang_type arg) says that it has no +// children, then we either need to trust it, or override it and return a +// different result. For example, an "int *" has one child that is an integer, +// but a function pointer doesn't have any children. Likewise if a Record type +// claims it has no children, then there really is nothing to show. +uint32_t +ClangASTType::GetNumPointeeChildren () const +{ + if (!IsValid()) + return 0; + + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Builtin: + switch (cast<clang::BuiltinType>(qual_type)->getKind()) + { + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::Void: + case clang::BuiltinType::NullPtr: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLImage1d: + case clang::BuiltinType::OCLImage1dArray: + case clang::BuiltinType::OCLImage1dBuffer: + case clang::BuiltinType::OCLImage2d: + case clang::BuiltinType::OCLImage2dArray: + case clang::BuiltinType::OCLImage3d: + case clang::BuiltinType::OCLSampler: + return 0; + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + case clang::BuiltinType::ObjCSel: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::Half: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::BuiltinFn: + return 1; + } + break; + + case clang::Type::Complex: return 1; + case clang::Type::Pointer: return 1; + case clang::Type::BlockPointer: return 0; // If block pointers don't have debug info, then no children for them + case clang::Type::LValueReference: return 1; + case clang::Type::RValueReference: return 1; + case clang::Type::MemberPointer: return 0; + case clang::Type::ConstantArray: return 0; + case clang::Type::IncompleteArray: return 0; + case clang::Type::VariableArray: return 0; + case clang::Type::DependentSizedArray: return 0; + case clang::Type::DependentSizedExtVector: return 0; + case clang::Type::Vector: return 0; + case clang::Type::ExtVector: return 0; + case clang::Type::FunctionProto: return 0; // When we function pointers, they have no children... + case clang::Type::FunctionNoProto: return 0; // When we function pointers, they have no children... + case clang::Type::UnresolvedUsing: return 0; + case clang::Type::Paren: return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetNumPointeeChildren (); + case clang::Type::Typedef: return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumPointeeChildren (); + case clang::Type::Elaborated: return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumPointeeChildren (); + case clang::Type::TypeOfExpr: return 0; + case clang::Type::TypeOf: return 0; + case clang::Type::Decltype: return 0; + case clang::Type::Record: return 0; + case clang::Type::Enum: return 1; + case clang::Type::TemplateTypeParm: return 1; + case clang::Type::SubstTemplateTypeParm: return 1; + case clang::Type::TemplateSpecialization: return 1; + case clang::Type::InjectedClassName: return 0; + case clang::Type::DependentName: return 1; + case clang::Type::DependentTemplateSpecialization: return 1; + case clang::Type::ObjCObject: return 0; + case clang::Type::ObjCInterface: return 0; + case clang::Type::ObjCObjectPointer: return 1; + default: + break; + } + return 0; +} + + +ClangASTType +ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, + const char *parent_name, + size_t idx, + bool transparent_pointers, + bool omit_empty_base_classes, + bool ignore_array_bounds, + std::string& child_name, + uint32_t &child_byte_size, + int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, + uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, + bool &child_is_deref_of_parent) const +{ + if (!IsValid()) + return ClangASTType(); + + QualType parent_qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass parent_type_class = parent_qual_type->getTypeClass(); + child_bitfield_bit_size = 0; + child_bitfield_bit_offset = 0; + child_is_base_class = false; + + const bool idx_is_valid = idx < GetNumChildren (omit_empty_base_classes); + uint32_t bit_offset; + switch (parent_type_class) + { + case clang::Type::Builtin: + if (idx_is_valid) + { + switch (cast<clang::BuiltinType>(parent_qual_type)->getKind()) + { + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + child_name = "isa"; + child_byte_size = m_ast->getTypeSize(m_ast->ObjCBuiltinClassTy) / CHAR_BIT; + return ClangASTType (m_ast, m_ast->ObjCBuiltinClassTy); + + default: + break; + } + } + break; + + case clang::Type::Record: + if (idx_is_valid && GetCompleteType()) + { + const RecordType *record_type = cast<RecordType>(parent_qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const ASTRecordLayout &record_layout = m_ast->getASTRecordLayout(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + if (cxx_record_decl) + { + // We might have base classes to print out first + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = NULL; + + // Skip empty base classes + if (omit_empty_base_classes) + { + base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + if (ClangASTContext::RecordHasFields(base_class_decl) == false) + continue; + } + + if (idx == child_idx) + { + if (base_class_decl == NULL) + base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + + + if (base_class->isVirtual()) + bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8; + else + bit_offset = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8; + + // Base classes should be a multiple of 8 bits in size + child_byte_offset = bit_offset/8; + ClangASTType base_class_clang_type(m_ast, base_class->getType()); + child_name = base_class_clang_type.GetTypeName(); + uint64_t base_class_clang_type_bit_size = base_class_clang_type.GetBitSize(); + + // Base classes bit sizes should be a multiple of 8 bits in size + assert (base_class_clang_type_bit_size % 8 == 0); + child_byte_size = base_class_clang_type_bit_size / 8; + child_is_base_class = true; + return base_class_clang_type; + } + // We don't increment the child index in the for loop since we might + // be skipping empty base classes + ++child_idx; + } + } + // Make sure index is in range... + uint32_t field_idx = 0; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) + { + if (idx == child_idx) + { + // Print the member type if requested + // Print the member name and equal sign + child_name.assign(field->getNameAsString().c_str()); + + // Figure out the type byte size (field_type_info.first) and + // alignment (field_type_info.second) from the AST context. + ClangASTType field_clang_type (m_ast, field->getType()); + assert(field_idx < record_layout.getFieldCount()); + child_byte_size = field_clang_type.GetByteSize(); + + // Figure out the field offset within the current struct/union/class type + bit_offset = record_layout.getFieldOffset (field_idx); + child_byte_offset = bit_offset / 8; + if (ClangASTContext::FieldIsBitfield (m_ast, *field, child_bitfield_bit_size)) + child_bitfield_bit_offset = bit_offset % 8; + + return field_clang_type; + } + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (idx_is_valid && GetCompleteType()) + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(parent_qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + uint32_t child_idx = 0; + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + + const ASTRecordLayout &interface_layout = m_ast->getASTObjCInterfaceLayout(class_interface_decl); + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + if (superclass_interface_decl) + { + if (omit_empty_base_classes) + { + ClangASTType base_class_clang_type (m_ast, m_ast->getObjCInterfaceType(superclass_interface_decl)); + if (base_class_clang_type.GetNumChildren(omit_empty_base_classes) > 0) + { + if (idx == 0) + { + QualType ivar_qual_type(m_ast->getObjCInterfaceType(superclass_interface_decl)); + + + child_name.assign(superclass_interface_decl->getNameAsString().c_str()); + + std::pair<uint64_t, unsigned> ivar_type_info = m_ast->getTypeInfo(ivar_qual_type.getTypePtr()); + + child_byte_size = ivar_type_info.first / 8; + child_byte_offset = 0; + child_is_base_class = true; + + return ClangASTType (m_ast, ivar_qual_type); + } + + ++child_idx; + } + } + else + ++child_idx; + } + + const uint32_t superclass_idx = child_idx; + + if (idx < (child_idx + class_interface_decl->ivar_size())) + { + ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); + + for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos) + { + if (child_idx == idx) + { + ObjCIvarDecl* ivar_decl = *ivar_pos; + + QualType ivar_qual_type(ivar_decl->getType()); + + child_name.assign(ivar_decl->getNameAsString().c_str()); + + std::pair<uint64_t, unsigned> ivar_type_info = m_ast->getTypeInfo(ivar_qual_type.getTypePtr()); + + child_byte_size = ivar_type_info.first / 8; + + // Figure out the field offset within the current struct/union/class type + // For ObjC objects, we can't trust the bit offset we get from the Clang AST, since + // that doesn't account for the space taken up by unbacked properties, or from + // the changing size of base classes that are newer than this class. + // So if we have a process around that we can ask about this object, do so. + child_byte_offset = LLDB_INVALID_IVAR_OFFSET; + Process *process = NULL; + if (exe_ctx) + process = exe_ctx->GetProcessPtr(); + if (process) + { + ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); + if (objc_runtime != NULL) + { + ClangASTType parent_ast_type (m_ast, parent_qual_type); + child_byte_offset = objc_runtime->GetByteOffsetForIvar (parent_ast_type, ivar_decl->getNameAsString().c_str()); + } + } + + // Setting this to UINT32_MAX to make sure we don't compute it twice... + bit_offset = UINT32_MAX; + + if (child_byte_offset == LLDB_INVALID_IVAR_OFFSET) + { + bit_offset = interface_layout.getFieldOffset (child_idx - superclass_idx); + child_byte_offset = bit_offset / 8; + } + + // Note, the ObjC Ivar Byte offset is just that, it doesn't account for the bit offset + // of a bitfield within its containing object. So regardless of where we get the byte + // offset from, we still need to get the bit offset for bitfields from the layout. + + if (ClangASTContext::FieldIsBitfield (m_ast, ivar_decl, child_bitfield_bit_size)) + { + if (bit_offset == UINT32_MAX) + bit_offset = interface_layout.getFieldOffset (child_idx - superclass_idx); + + child_bitfield_bit_offset = bit_offset % 8; + } + return ClangASTType (m_ast, ivar_qual_type); + } + ++child_idx; + } + } + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + if (idx_is_valid) + { + ClangASTType pointee_clang_type (GetPointeeType()); + + if (transparent_pointers && pointee_clang_type.IsAggregateType()) + { + child_is_deref_of_parent = false; + bool tmp_child_is_deref_of_parent = false; + return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + tmp_child_is_deref_of_parent); + } + else + { + child_is_deref_of_parent = true; + if (parent_name) + { + child_name.assign(1, '*'); + child_name += parent_name; + } + + // We have a pointer to an simple type + if (idx == 0 && pointee_clang_type.GetCompleteType()) + { + child_byte_size = pointee_clang_type.GetByteSize(); + child_byte_offset = 0; + return pointee_clang_type; + } + } + } + break; + + case clang::Type::Vector: + case clang::Type::ExtVector: + if (idx_is_valid) + { + const VectorType *array = cast<VectorType>(parent_qual_type.getTypePtr()); + if (array) + { + ClangASTType element_type (m_ast, array->getElementType()); + if (element_type.GetCompleteType()) + { + char element_name[64]; + ::snprintf (element_name, sizeof (element_name), "[%zu]", idx); + child_name.assign(element_name); + child_byte_size = element_type.GetByteSize(); + child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; + return element_type; + } + } + } + break; + + case clang::Type::ConstantArray: + case clang::Type::IncompleteArray: + if (ignore_array_bounds || idx_is_valid) + { + const ArrayType *array = cast<ArrayType>(parent_qual_type.getTypePtr()); + if (array) + { + ClangASTType element_type (m_ast, array->getElementType()); + if (element_type.GetCompleteType()) + { + char element_name[64]; + ::snprintf (element_name, sizeof (element_name), "[%zu]", idx); + child_name.assign(element_name); + child_byte_size = element_type.GetByteSize(); + child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; + return element_type; + } + } + } + break; + + + case clang::Type::Pointer: + if (idx_is_valid) + { + ClangASTType pointee_clang_type (GetPointeeType()); + + // Don't dereference "void *" pointers + if (pointee_clang_type.IsVoidType()) + return ClangASTType(); + + if (transparent_pointers && pointee_clang_type.IsAggregateType ()) + { + child_is_deref_of_parent = false; + bool tmp_child_is_deref_of_parent = false; + return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + tmp_child_is_deref_of_parent); + } + else + { + child_is_deref_of_parent = true; + + if (parent_name) + { + child_name.assign(1, '*'); + child_name += parent_name; + } + + // We have a pointer to an simple type + if (idx == 0) + { + child_byte_size = pointee_clang_type.GetByteSize(); + child_byte_offset = 0; + return pointee_clang_type; + } + } + } + break; + + case clang::Type::LValueReference: + case clang::Type::RValueReference: + if (idx_is_valid) + { + const ReferenceType *reference_type = cast<ReferenceType>(parent_qual_type.getTypePtr()); + ClangASTType pointee_clang_type (m_ast, reference_type->getPointeeType()); + if (transparent_pointers && pointee_clang_type.IsAggregateType ()) + { + child_is_deref_of_parent = false; + bool tmp_child_is_deref_of_parent = false; + return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + tmp_child_is_deref_of_parent); + } + else + { + if (parent_name) + { + child_name.assign(1, '&'); + child_name += parent_name; + } + + // We have a pointer to an simple type + if (idx == 0) + { + child_byte_size = pointee_clang_type.GetByteSize(); + child_byte_offset = 0; + return pointee_clang_type; + } + } + } + break; + + case clang::Type::Typedef: + { + ClangASTType typedefed_clang_type (m_ast, cast<TypedefType>(parent_qual_type)->getDecl()->getUnderlyingType()); + return typedefed_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + } + break; + + case clang::Type::Elaborated: + { + ClangASTType elaborated_clang_type (m_ast, cast<ElaboratedType>(parent_qual_type)->getNamedType()); + return elaborated_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + } + + case clang::Type::Paren: + { + ClangASTType paren_clang_type (m_ast, llvm::cast<clang::ParenType>(parent_qual_type)->desugar()); + return paren_clang_type.GetChildClangTypeAtIndex (exe_ctx, + parent_name, + idx, + transparent_pointers, + omit_empty_base_classes, + ignore_array_bounds, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset, + child_is_base_class, + child_is_deref_of_parent); + } + + + default: + break; + } + return ClangASTType(); +} + +static inline bool +BaseSpecifierIsEmpty (const CXXBaseSpecifier *b) +{ + return ClangASTContext::RecordHasFields(b->getType()->getAsCXXRecordDecl()) == false; +} + +static uint32_t +GetIndexForRecordBase +( + const RecordDecl *record_decl, + const CXXBaseSpecifier *base_spec, + bool omit_empty_base_classes + ) +{ + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + + // const char *super_name = record_decl->getNameAsCString(); + // const char *base_name = base_spec->getType()->getAs<RecordType>()->getDecl()->getNameAsCString(); + // printf ("GetIndexForRecordChild (%s, %s)\n", super_name, base_name); + // + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + if (omit_empty_base_classes) + { + if (BaseSpecifierIsEmpty (base_class)) + continue; + } + + // printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n", super_name, base_name, + // child_idx, + // base_class->getType()->getAs<RecordType>()->getDecl()->getNameAsCString()); + // + // + if (base_class == base_spec) + return child_idx; + ++child_idx; + } + } + + return UINT32_MAX; +} + + +static uint32_t +GetIndexForRecordChild (const RecordDecl *record_decl, + NamedDecl *canonical_decl, + bool omit_empty_base_classes) +{ + uint32_t child_idx = ClangASTContext::GetNumBaseClasses (dyn_cast<CXXRecordDecl>(record_decl), + omit_empty_base_classes); + + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { + if (field->getCanonicalDecl() == canonical_decl) + return child_idx; + } + + return UINT32_MAX; +} + +// Look for a child member (doesn't include base classes, but it does include +// their members) in the type hierarchy. Returns an index path into "clang_type" +// on how to reach the appropriate member. +// +// class A +// { +// public: +// int m_a; +// int m_b; +// }; +// +// class B +// { +// }; +// +// class C : +// public B, +// public A +// { +// }; +// +// If we have a clang type that describes "class C", and we wanted to looked +// "m_b" in it: +// +// With omit_empty_base_classes == false we would get an integer array back with: +// { 1, 1 } +// The first index 1 is the child index for "class A" within class C +// The second index 1 is the child index for "m_b" within class A +// +// With omit_empty_base_classes == true we would get an integer array back with: +// { 0, 1 } +// The first index 0 is the child index for "class A" within class C (since class B doesn't have any members it doesn't count) +// The second index 1 is the child index for "m_b" within class A + +size_t +ClangASTType::GetIndexOfChildMemberWithName (const char *name, + bool omit_empty_base_classes, + std::vector<uint32_t>& child_indexes) const +{ + if (IsValid() && name && name[0]) + { + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType ()) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + + assert(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + + // Try and find a field that matches NAME + RecordDecl::field_iterator field, field_end; + StringRef name_sref(name); + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { + if (field->getName().equals (name_sref)) + { + // We have to add on the number of base classes to this index! + child_indexes.push_back (child_idx + ClangASTContext::GetNumBaseClasses (cxx_record_decl, omit_empty_base_classes)); + return child_indexes.size(); + } + } + + if (cxx_record_decl) + { + const RecordDecl *parent_record_decl = cxx_record_decl; + + //printf ("parent = %s\n", parent_record_decl->getNameAsCString()); + + //const Decl *root_cdecl = cxx_record_decl->getCanonicalDecl(); + // Didn't find things easily, lets let clang do its thang... + IdentifierInfo & ident_ref = m_ast->Idents.get(name_sref); + DeclarationName decl_name(&ident_ref); + + CXXBasePaths paths; + if (cxx_record_decl->lookupInBases(CXXRecordDecl::FindOrdinaryMember, + decl_name.getAsOpaquePtr(), + paths)) + { + CXXBasePaths::const_paths_iterator path, path_end = paths.end(); + for (path = paths.begin(); path != path_end; ++path) + { + const size_t num_path_elements = path->size(); + for (size_t e=0; e<num_path_elements; ++e) + { + CXXBasePathElement elem = (*path)[e]; + + child_idx = GetIndexForRecordBase (parent_record_decl, elem.Base, omit_empty_base_classes); + if (child_idx == UINT32_MAX) + { + child_indexes.clear(); + return 0; + } + else + { + child_indexes.push_back (child_idx); + parent_record_decl = cast<RecordDecl>(elem.Base->getType()->getAs<RecordType>()->getDecl()); + } + } + for (NamedDecl *path_decl : path->Decls) + { + child_idx = GetIndexForRecordChild (parent_record_decl, path_decl, omit_empty_base_classes); + if (child_idx == UINT32_MAX) + { + child_indexes.clear(); + return 0; + } + else + { + child_indexes.push_back (child_idx); + } + } + } + return child_indexes.size(); + } + } + + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType ()) + { + StringRef name_sref(name); + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + uint32_t child_idx = 0; + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + + for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) + { + const ObjCIvarDecl* ivar_decl = *ivar_pos; + + if (ivar_decl->getName().equals (name_sref)) + { + if ((!omit_empty_base_classes && superclass_interface_decl) || + ( omit_empty_base_classes && ObjCDeclHasIVars (superclass_interface_decl, true))) + ++child_idx; + + child_indexes.push_back (child_idx); + return child_indexes.size(); + } + } + + if (superclass_interface_decl) + { + // The super class index is always zero for ObjC classes, + // so we push it onto the child indexes in case we find + // an ivar in our superclass... + child_indexes.push_back (0); + + ClangASTType superclass_clang_type (m_ast, m_ast->getObjCInterfaceType(superclass_interface_decl)); + if (superclass_clang_type.GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes)) + { + // We did find an ivar in a superclass so just + // return the results! + return child_indexes.size(); + } + + // We didn't find an ivar matching "name" in our + // superclass, pop the superclass zero index that + // we pushed on above. + child_indexes.pop_back(); + } + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + { + ClangASTType objc_object_clang_type (m_ast, cast<ObjCObjectPointerType>(qual_type.getTypePtr())->getPointeeType()); + return objc_object_clang_type.GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + } + break; + + + case clang::Type::ConstantArray: + { + // const ConstantArrayType *array = cast<ConstantArrayType>(parent_qual_type.getTypePtr()); + // const uint64_t element_count = array->getSize().getLimitedValue(); + // + // if (idx < element_count) + // { + // std::pair<uint64_t, unsigned> field_type_info = ast->getTypeInfo(array->getElementType()); + // + // char element_name[32]; + // ::snprintf (element_name, sizeof (element_name), "%s[%u]", parent_name ? parent_name : "", idx); + // + // child_name.assign(element_name); + // assert(field_type_info.first % 8 == 0); + // child_byte_size = field_type_info.first / 8; + // child_byte_offset = idx * child_byte_size; + // return array->getElementType().getAsOpaquePtr(); + // } + } + break; + + // case clang::Type::MemberPointerType: + // { + // MemberPointerType *mem_ptr_type = cast<MemberPointerType>(qual_type.getTypePtr()); + // QualType pointee_type = mem_ptr_type->getPointeeType(); + // + // if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + // { + // return GetIndexOfChildWithName (ast, + // mem_ptr_type->getPointeeType().getAsOpaquePtr(), + // name); + // } + // } + // break; + // + case clang::Type::LValueReference: + case clang::Type::RValueReference: + { + const ReferenceType *reference_type = cast<ReferenceType>(qual_type.getTypePtr()); + QualType pointee_type(reference_type->getPointeeType()); + ClangASTType pointee_clang_type (m_ast, pointee_type); + + if (pointee_clang_type.IsAggregateType ()) + { + return pointee_clang_type.GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + } + } + break; + + case clang::Type::Pointer: + { + ClangASTType pointee_clang_type (GetPointeeType()); + + if (pointee_clang_type.IsAggregateType ()) + { + return pointee_clang_type.GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetIndexOfChildMemberWithName (name, + omit_empty_base_classes, + child_indexes); + + default: + break; + } + } + return 0; +} + + +// Get the index of the child of "clang_type" whose name matches. This function +// doesn't descend into the children, but only looks one level deep and name +// matches can include base class names. + +uint32_t +ClangASTType::GetIndexOfChildWithName (const char *name, bool omit_empty_base_classes) const +{ + if (IsValid() && name && name[0]) + { + QualType qual_type(GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType ()) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + + assert(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + + if (cxx_record_decl) + { + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + // Skip empty base classes + CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + if (omit_empty_base_classes && ClangASTContext::RecordHasFields(base_class_decl) == false) + continue; + + ClangASTType base_class_clang_type (m_ast, base_class->getType()); + std::string base_class_type_name (base_class_clang_type.GetTypeName()); + if (base_class_type_name.compare (name) == 0) + return child_idx; + ++child_idx; + } + } + + // Try and find a field that matches NAME + RecordDecl::field_iterator field, field_end; + StringRef name_sref(name); + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); + field != field_end; + ++field, ++child_idx) + { + if (field->getName().equals (name_sref)) + return child_idx; + } + + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + StringRef name_sref(name); + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + uint32_t child_idx = 0; + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); + ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + + for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) + { + const ObjCIvarDecl* ivar_decl = *ivar_pos; + + if (ivar_decl->getName().equals (name_sref)) + { + if ((!omit_empty_base_classes && superclass_interface_decl) || + ( omit_empty_base_classes && ObjCDeclHasIVars (superclass_interface_decl, true))) + ++child_idx; + + return child_idx; + } + } + + if (superclass_interface_decl) + { + if (superclass_interface_decl->getName().equals (name_sref)) + return 0; + } + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + { + ClangASTType pointee_clang_type (m_ast, cast<ObjCObjectPointerType>(qual_type.getTypePtr())->getPointeeType()); + return pointee_clang_type.GetIndexOfChildWithName (name, omit_empty_base_classes); + } + break; + + case clang::Type::ConstantArray: + { + // const ConstantArrayType *array = cast<ConstantArrayType>(parent_qual_type.getTypePtr()); + // const uint64_t element_count = array->getSize().getLimitedValue(); + // + // if (idx < element_count) + // { + // std::pair<uint64_t, unsigned> field_type_info = ast->getTypeInfo(array->getElementType()); + // + // char element_name[32]; + // ::snprintf (element_name, sizeof (element_name), "%s[%u]", parent_name ? parent_name : "", idx); + // + // child_name.assign(element_name); + // assert(field_type_info.first % 8 == 0); + // child_byte_size = field_type_info.first / 8; + // child_byte_offset = idx * child_byte_size; + // return array->getElementType().getAsOpaquePtr(); + // } + } + break; + + // case clang::Type::MemberPointerType: + // { + // MemberPointerType *mem_ptr_type = cast<MemberPointerType>(qual_type.getTypePtr()); + // QualType pointee_type = mem_ptr_type->getPointeeType(); + // + // if (ClangASTContext::IsAggregateType (pointee_type.getAsOpaquePtr())) + // { + // return GetIndexOfChildWithName (ast, + // mem_ptr_type->getPointeeType().getAsOpaquePtr(), + // name); + // } + // } + // break; + // + case clang::Type::LValueReference: + case clang::Type::RValueReference: + { + const ReferenceType *reference_type = cast<ReferenceType>(qual_type.getTypePtr()); + ClangASTType pointee_type (m_ast, reference_type->getPointeeType()); + + if (pointee_type.IsAggregateType ()) + { + return pointee_type.GetIndexOfChildWithName (name, omit_empty_base_classes); + } + } + break; + + case clang::Type::Pointer: + { + const PointerType *pointer_type = cast<PointerType>(qual_type.getTypePtr()); + ClangASTType pointee_type (m_ast, pointer_type->getPointeeType()); + + if (pointee_type.IsAggregateType ()) + { + return pointee_type.GetIndexOfChildWithName (name, omit_empty_base_classes); + } + else + { + // if (parent_name) + // { + // child_name.assign(1, '*'); + // child_name += parent_name; + // } + // + // // We have a pointer to an simple type + // if (idx == 0) + // { + // std::pair<uint64_t, unsigned> clang_type_info = ast->getTypeInfo(pointee_type); + // assert(clang_type_info.first % 8 == 0); + // child_byte_size = clang_type_info.first / 8; + // child_byte_offset = 0; + // return pointee_type.getAsOpaquePtr(); + // } + } + } + break; + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetIndexOfChildWithName (name, omit_empty_base_classes); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<clang::ParenType>(qual_type)->desugar()).GetIndexOfChildWithName (name, omit_empty_base_classes); + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetIndexOfChildWithName (name, omit_empty_base_classes); + + default: + break; + } + } + return UINT32_MAX; +} + + +size_t +ClangASTType::GetNumTemplateArguments () const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType ()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + const ClassTemplateSpecializationDecl *template_decl = dyn_cast<ClassTemplateSpecializationDecl>(cxx_record_decl); + if (template_decl) + return template_decl->getTemplateArgs().size(); + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumTemplateArguments(); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetNumTemplateArguments(); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).GetNumTemplateArguments(); + + default: + break; + } + } + return 0; +} + +ClangASTType +ClangASTType::GetTemplateArgument (size_t arg_idx, lldb::TemplateArgumentKind &kind) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + if (GetCompleteType ()) + { + const CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + const ClassTemplateSpecializationDecl *template_decl = dyn_cast<ClassTemplateSpecializationDecl>(cxx_record_decl); + if (template_decl && arg_idx < template_decl->getTemplateArgs().size()) + { + const TemplateArgument &template_arg = template_decl->getTemplateArgs()[arg_idx]; + switch (template_arg.getKind()) + { + case clang::TemplateArgument::Null: + kind = eTemplateArgumentKindNull; + return ClangASTType(); + + case clang::TemplateArgument::Type: + kind = eTemplateArgumentKindType; + return ClangASTType(m_ast, template_arg.getAsType()); + + case clang::TemplateArgument::Declaration: + kind = eTemplateArgumentKindDeclaration; + return ClangASTType(); + + case clang::TemplateArgument::Integral: + kind = eTemplateArgumentKindIntegral; + return ClangASTType(m_ast, template_arg.getIntegralType()); + + case clang::TemplateArgument::Template: + kind = eTemplateArgumentKindTemplate; + return ClangASTType(); + + case clang::TemplateArgument::TemplateExpansion: + kind = eTemplateArgumentKindTemplateExpansion; + return ClangASTType(); + + case clang::TemplateArgument::Expression: + kind = eTemplateArgumentKindExpression; + return ClangASTType(); + + case clang::TemplateArgument::Pack: + kind = eTemplateArgumentKindPack; + return ClangASTType(); + + default: + assert (!"Unhandled TemplateArgument::ArgKind"); + break; + } + } + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetTemplateArgument (arg_idx, kind); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetTemplateArgument (arg_idx, kind); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).GetTemplateArgument (arg_idx, kind); + + default: + break; + } + } + kind = eTemplateArgumentKindNull; + return ClangASTType (); +} + +static bool +IsOperator (const char *name, OverloadedOperatorKind &op_kind) +{ + if (name == NULL || name[0] == '\0') + return false; + +#define OPERATOR_PREFIX "operator" +#define OPERATOR_PREFIX_LENGTH (sizeof (OPERATOR_PREFIX) - 1) + + const char *post_op_name = NULL; + + bool no_space = true; + + if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) + return false; + + post_op_name = name + OPERATOR_PREFIX_LENGTH; + + if (post_op_name[0] == ' ') + { + post_op_name++; + no_space = false; + } + +#undef OPERATOR_PREFIX +#undef OPERATOR_PREFIX_LENGTH + + // This is an operator, set the overloaded operator kind to invalid + // in case this is a conversion operator... + op_kind = NUM_OVERLOADED_OPERATORS; + + switch (post_op_name[0]) + { + default: + if (no_space) + return false; + break; + case 'n': + if (no_space) + return false; + if (strcmp (post_op_name, "new") == 0) + op_kind = OO_New; + else if (strcmp (post_op_name, "new[]") == 0) + op_kind = OO_Array_New; + break; + + case 'd': + if (no_space) + return false; + if (strcmp (post_op_name, "delete") == 0) + op_kind = OO_Delete; + else if (strcmp (post_op_name, "delete[]") == 0) + op_kind = OO_Array_Delete; + break; + + case '+': + if (post_op_name[1] == '\0') + op_kind = OO_Plus; + else if (post_op_name[2] == '\0') + { + if (post_op_name[1] == '=') + op_kind = OO_PlusEqual; + else if (post_op_name[1] == '+') + op_kind = OO_PlusPlus; + } + break; + + case '-': + if (post_op_name[1] == '\0') + op_kind = OO_Minus; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_MinusEqual; break; + case '-': op_kind = OO_MinusMinus; break; + case '>': op_kind = OO_Arrow; break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '*') + op_kind = OO_ArrowStar; break; + } + break; + + case '*': + if (post_op_name[1] == '\0') + op_kind = OO_Star; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_StarEqual; + break; + + case '/': + if (post_op_name[1] == '\0') + op_kind = OO_Slash; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_SlashEqual; + break; + + case '%': + if (post_op_name[1] == '\0') + op_kind = OO_Percent; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_PercentEqual; + break; + + + case '^': + if (post_op_name[1] == '\0') + op_kind = OO_Caret; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_CaretEqual; + break; + + case '&': + if (post_op_name[1] == '\0') + op_kind = OO_Amp; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_AmpEqual; break; + case '&': op_kind = OO_AmpAmp; break; + } + } + break; + + case '|': + if (post_op_name[1] == '\0') + op_kind = OO_Pipe; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '=': op_kind = OO_PipeEqual; break; + case '|': op_kind = OO_PipePipe; break; + } + } + break; + + case '~': + if (post_op_name[1] == '\0') + op_kind = OO_Tilde; + break; + + case '!': + if (post_op_name[1] == '\0') + op_kind = OO_Exclaim; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_ExclaimEqual; + break; + + case '=': + if (post_op_name[1] == '\0') + op_kind = OO_Equal; + else if (post_op_name[1] == '=' && post_op_name[2] == '\0') + op_kind = OO_EqualEqual; + break; + + case '<': + if (post_op_name[1] == '\0') + op_kind = OO_Less; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '<': op_kind = OO_LessLess; break; + case '=': op_kind = OO_LessEqual; break; + } + } + else if (post_op_name[3] == '\0') + { + if (post_op_name[2] == '=') + op_kind = OO_LessLessEqual; + } + break; + + case '>': + if (post_op_name[1] == '\0') + op_kind = OO_Greater; + else if (post_op_name[2] == '\0') + { + switch (post_op_name[1]) + { + case '>': op_kind = OO_GreaterGreater; break; + case '=': op_kind = OO_GreaterEqual; break; + } + } + else if (post_op_name[1] == '>' && + post_op_name[2] == '=' && + post_op_name[3] == '\0') + { + op_kind = OO_GreaterGreaterEqual; + } + break; + + case ',': + if (post_op_name[1] == '\0') + op_kind = OO_Comma; + break; + + case '(': + if (post_op_name[1] == ')' && post_op_name[2] == '\0') + op_kind = OO_Call; + break; + + case '[': + if (post_op_name[1] == ']' && post_op_name[2] == '\0') + op_kind = OO_Subscript; + break; + } + + return true; +} + +static inline bool +check_op_param (uint32_t op_kind, bool unary, bool binary, uint32_t num_params) +{ + // Special-case call since it can take any number of operands + if(op_kind == OO_Call) + return true; + + // The parameter count doens't include "this" + if (num_params == 0) + return unary; + if (num_params == 1) + return binary; + else + return false; +} + +clang::RecordDecl * +ClangASTType::GetAsRecordDecl () const +{ + const RecordType *record_type = dyn_cast<RecordType>(GetCanonicalQualType()); + if (record_type) + return record_type->getDecl(); + return NULL; +} + +clang::CXXRecordDecl * +ClangASTType::GetAsCXXRecordDecl () const +{ + return GetCanonicalQualType()->getAsCXXRecordDecl(); +} + +ObjCInterfaceDecl * +ClangASTType::GetAsObjCInterfaceDecl () const +{ + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(GetCanonicalQualType()); + if (objc_class_type) + return objc_class_type->getInterface(); + return NULL; +} + +clang::FieldDecl * +ClangASTType::AddFieldToRecordType (const char *name, + const ClangASTType &field_clang_type, + AccessType access, + uint32_t bitfield_bit_size) +{ + if (!IsValid() || !field_clang_type.IsValid()) + return NULL; + + FieldDecl *field = NULL; + + clang::Expr *bit_width = NULL; + if (bitfield_bit_size != 0) + { + APInt bitfield_bit_size_apint(m_ast->getTypeSize(m_ast->IntTy), bitfield_bit_size); + bit_width = new (*m_ast)IntegerLiteral (*m_ast, bitfield_bit_size_apint, m_ast->IntTy, SourceLocation()); + } + + RecordDecl *record_decl = GetAsRecordDecl (); + if (record_decl) + { + field = FieldDecl::Create (*m_ast, + record_decl, + SourceLocation(), + SourceLocation(), + name ? &m_ast->Idents.get(name) : NULL, // Identifier + field_clang_type.GetQualType(), // Field type + NULL, // TInfo * + bit_width, // BitWidth + false, // Mutable + ICIS_NoInit); // HasInit + + if (!name) + { + // Determine whether this field corresponds to an anonymous + // struct or union. + if (const TagType *TagT = field->getType()->getAs<TagType>()) { + if (RecordDecl *Rec = dyn_cast<RecordDecl>(TagT->getDecl())) + if (!Rec->getDeclName()) { + Rec->setAnonymousStructOrUnion(true); + field->setImplicit(); + + } + } + } + + if (field) + { + field->setAccess (ClangASTContext::ConvertAccessTypeToAccessSpecifier (access)); + + record_decl->addDecl(field); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(field); +#endif + } + } + else + { + ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl (); + + if (class_interface_decl) + { + const bool is_synthesized = false; + + field_clang_type.GetCompleteType(); + + field = ObjCIvarDecl::Create (*m_ast, + class_interface_decl, + SourceLocation(), + SourceLocation(), + name ? &m_ast->Idents.get(name) : NULL, // Identifier + field_clang_type.GetQualType(), // Field type + NULL, // TypeSourceInfo * + ConvertAccessTypeToObjCIvarAccessControl (access), + bit_width, + is_synthesized); + + if (field) + { + class_interface_decl->addDecl(field); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(field); +#endif + } + } + } + return field; +} + +void +ClangASTType::BuildIndirectFields () +{ + RecordDecl *record_decl = GetAsRecordDecl(); + + if (!record_decl) + return; + + typedef llvm::SmallVector <IndirectFieldDecl *, 1> IndirectFieldVector; + + IndirectFieldVector indirect_fields; + RecordDecl::field_iterator field_pos; + RecordDecl::field_iterator field_end_pos = record_decl->field_end(); + RecordDecl::field_iterator last_field_pos = field_end_pos; + for (field_pos = record_decl->field_begin(); field_pos != field_end_pos; last_field_pos = field_pos++) + { + if (field_pos->isAnonymousStructOrUnion()) + { + QualType field_qual_type = field_pos->getType(); + + const RecordType *field_record_type = field_qual_type->getAs<RecordType>(); + + if (!field_record_type) + continue; + + RecordDecl *field_record_decl = field_record_type->getDecl(); + + if (!field_record_decl) + continue; + + for (RecordDecl::decl_iterator di = field_record_decl->decls_begin(), de = field_record_decl->decls_end(); + di != de; + ++di) + { + if (FieldDecl *nested_field_decl = dyn_cast<FieldDecl>(*di)) + { + NamedDecl **chain = new (*m_ast) NamedDecl*[2]; + chain[0] = *field_pos; + chain[1] = nested_field_decl; + IndirectFieldDecl *indirect_field = IndirectFieldDecl::Create(*m_ast, + record_decl, + SourceLocation(), + nested_field_decl->getIdentifier(), + nested_field_decl->getType(), + chain, + 2); + + indirect_field->setImplicit(); + + indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers(field_pos->getAccess(), + nested_field_decl->getAccess())); + + indirect_fields.push_back(indirect_field); + } + else if (IndirectFieldDecl *nested_indirect_field_decl = dyn_cast<IndirectFieldDecl>(*di)) + { + int nested_chain_size = nested_indirect_field_decl->getChainingSize(); + NamedDecl **chain = new (*m_ast) NamedDecl*[nested_chain_size + 1]; + chain[0] = *field_pos; + + int chain_index = 1; + for (IndirectFieldDecl::chain_iterator nci = nested_indirect_field_decl->chain_begin(), + nce = nested_indirect_field_decl->chain_end(); + nci < nce; + ++nci) + { + chain[chain_index] = *nci; + chain_index++; + } + + IndirectFieldDecl *indirect_field = IndirectFieldDecl::Create(*m_ast, + record_decl, + SourceLocation(), + nested_indirect_field_decl->getIdentifier(), + nested_indirect_field_decl->getType(), + chain, + nested_chain_size + 1); + + indirect_field->setImplicit(); + + indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers(field_pos->getAccess(), + nested_indirect_field_decl->getAccess())); + + indirect_fields.push_back(indirect_field); + } + } + } + } + + // Check the last field to see if it has an incomplete array type as its + // last member and if it does, the tell the record decl about it + if (last_field_pos != field_end_pos) + { + if (last_field_pos->getType()->isIncompleteArrayType()) + record_decl->hasFlexibleArrayMember(); + } + + for (IndirectFieldVector::iterator ifi = indirect_fields.begin(), ife = indirect_fields.end(); + ifi < ife; + ++ifi) + { + record_decl->addDecl(*ifi); + } +} + +clang::VarDecl * +ClangASTType::AddVariableToRecordType (const char *name, + const ClangASTType &var_type, + AccessType access) +{ + clang::VarDecl *var_decl = NULL; + + if (!IsValid() || !var_type.IsValid()) + return NULL; + + RecordDecl *record_decl = GetAsRecordDecl (); + if (record_decl) + { + var_decl = VarDecl::Create (*m_ast, // ASTContext & + record_decl, // DeclContext * + SourceLocation(), // SourceLocation StartLoc + SourceLocation(), // SourceLocation IdLoc + name ? &m_ast->Idents.get(name) : NULL, // IdentifierInfo * + var_type.GetQualType(), // Variable QualType + NULL, // TypeSourceInfo * + SC_Static); // StorageClass + if (var_decl) + { + var_decl->setAccess(ClangASTContext::ConvertAccessTypeToAccessSpecifier (access)); + record_decl->addDecl(var_decl); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(var_decl); +#endif + } + } + return var_decl; +} + + +CXXMethodDecl * +ClangASTType::AddMethodToCXXRecordType (const char *name, + const ClangASTType &method_clang_type, + lldb::AccessType access, + bool is_virtual, + bool is_static, + bool is_inline, + bool is_explicit, + bool is_attr_used, + bool is_artificial) +{ + if (!IsValid() || !method_clang_type.IsValid() || name == NULL || name[0] == '\0') + return NULL; + + QualType record_qual_type(GetCanonicalQualType()); + + CXXRecordDecl *cxx_record_decl = record_qual_type->getAsCXXRecordDecl(); + + if (cxx_record_decl == NULL) + return NULL; + + QualType method_qual_type (method_clang_type.GetQualType()); + + CXXMethodDecl *cxx_method_decl = NULL; + + DeclarationName decl_name (&m_ast->Idents.get(name)); + + const clang::FunctionType *function_type = dyn_cast<FunctionType>(method_qual_type.getTypePtr()); + + if (function_type == NULL) + return NULL; + + const FunctionProtoType *method_function_prototype (dyn_cast<FunctionProtoType>(function_type)); + + if (!method_function_prototype) + return NULL; + + unsigned int num_params = method_function_prototype->getNumArgs(); + + CXXDestructorDecl *cxx_dtor_decl(NULL); + CXXConstructorDecl *cxx_ctor_decl(NULL); + + if (is_artificial) + return NULL; // skip everything artificial + + if (name[0] == '~') + { + cxx_dtor_decl = CXXDestructorDecl::Create (*m_ast, + cxx_record_decl, + SourceLocation(), + DeclarationNameInfo (m_ast->DeclarationNames.getCXXDestructorName (m_ast->getCanonicalType (record_qual_type)), SourceLocation()), + method_qual_type, + NULL, + is_inline, + is_artificial); + cxx_method_decl = cxx_dtor_decl; + } + else if (decl_name == cxx_record_decl->getDeclName()) + { + cxx_ctor_decl = CXXConstructorDecl::Create (*m_ast, + cxx_record_decl, + SourceLocation(), + DeclarationNameInfo (m_ast->DeclarationNames.getCXXConstructorName (m_ast->getCanonicalType (record_qual_type)), SourceLocation()), + method_qual_type, + NULL, // TypeSourceInfo * + is_explicit, + is_inline, + is_artificial, + false /*is_constexpr*/); + cxx_method_decl = cxx_ctor_decl; + } + else + { + clang::StorageClass SC = is_static ? SC_Static : SC_None; + OverloadedOperatorKind op_kind = NUM_OVERLOADED_OPERATORS; + + if (IsOperator (name, op_kind)) + { + if (op_kind != NUM_OVERLOADED_OPERATORS) + { + // Check the number of operator parameters. Sometimes we have + // seen bad DWARF that doesn't correctly describe operators and + // if we try to create a methed and add it to the class, clang + // will assert and crash, so we need to make sure things are + // acceptable. + if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount (op_kind, num_params)) + return NULL; + cxx_method_decl = CXXMethodDecl::Create (*m_ast, + cxx_record_decl, + SourceLocation(), + DeclarationNameInfo (m_ast->DeclarationNames.getCXXOperatorName (op_kind), SourceLocation()), + method_qual_type, + NULL, // TypeSourceInfo * + SC, + is_inline, + false /*is_constexpr*/, + SourceLocation()); + } + else if (num_params == 0) + { + // Conversion operators don't take params... + cxx_method_decl = CXXConversionDecl::Create (*m_ast, + cxx_record_decl, + SourceLocation(), + DeclarationNameInfo (m_ast->DeclarationNames.getCXXConversionFunctionName (m_ast->getCanonicalType (function_type->getResultType())), SourceLocation()), + method_qual_type, + NULL, // TypeSourceInfo * + is_inline, + is_explicit, + false /*is_constexpr*/, + SourceLocation()); + } + } + + if (cxx_method_decl == NULL) + { + cxx_method_decl = CXXMethodDecl::Create (*m_ast, + cxx_record_decl, + SourceLocation(), + DeclarationNameInfo (decl_name, SourceLocation()), + method_qual_type, + NULL, // TypeSourceInfo * + SC, + is_inline, + false /*is_constexpr*/, + SourceLocation()); + } + } + + AccessSpecifier access_specifier = ClangASTContext::ConvertAccessTypeToAccessSpecifier (access); + + cxx_method_decl->setAccess (access_specifier); + cxx_method_decl->setVirtualAsWritten (is_virtual); + + if (is_attr_used) + cxx_method_decl->addAttr(::new (*m_ast) UsedAttr(SourceRange(), *m_ast)); + + // Populate the method decl with parameter decls + + llvm::SmallVector<ParmVarDecl *, 12> params; + + for (unsigned param_index = 0; + param_index < num_params; + ++param_index) + { + params.push_back (ParmVarDecl::Create (*m_ast, + cxx_method_decl, + SourceLocation(), + SourceLocation(), + NULL, // anonymous + method_function_prototype->getArgType(param_index), + NULL, + SC_None, + NULL)); + } + + cxx_method_decl->setParams (ArrayRef<ParmVarDecl*>(params)); + + cxx_record_decl->addDecl (cxx_method_decl); + + // Sometimes the debug info will mention a constructor (default/copy/move), + // destructor, or assignment operator (copy/move) but there won't be any + // version of this in the code. So we check if the function was artificially + // generated and if it is trivial and this lets the compiler/backend know + // that it can inline the IR for these when it needs to and we can avoid a + // "missing function" error when running expressions. + + if (is_artificial) + { + if (cxx_ctor_decl && + ((cxx_ctor_decl->isDefaultConstructor() && cxx_record_decl->hasTrivialDefaultConstructor ()) || + (cxx_ctor_decl->isCopyConstructor() && cxx_record_decl->hasTrivialCopyConstructor ()) || + (cxx_ctor_decl->isMoveConstructor() && cxx_record_decl->hasTrivialMoveConstructor ()) )) + { + cxx_ctor_decl->setDefaulted(); + cxx_ctor_decl->setTrivial(true); + } + else if (cxx_dtor_decl) + { + if (cxx_record_decl->hasTrivialDestructor()) + { + cxx_dtor_decl->setDefaulted(); + cxx_dtor_decl->setTrivial(true); + } + } + else if ((cxx_method_decl->isCopyAssignmentOperator() && cxx_record_decl->hasTrivialCopyAssignment()) || + (cxx_method_decl->isMoveAssignmentOperator() && cxx_record_decl->hasTrivialMoveAssignment())) + { + cxx_method_decl->setDefaulted(); + cxx_method_decl->setTrivial(true); + } + } + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(cxx_method_decl); +#endif + + // printf ("decl->isPolymorphic() = %i\n", cxx_record_decl->isPolymorphic()); + // printf ("decl->isAggregate() = %i\n", cxx_record_decl->isAggregate()); + // printf ("decl->isPOD() = %i\n", cxx_record_decl->isPOD()); + // printf ("decl->isEmpty() = %i\n", cxx_record_decl->isEmpty()); + // printf ("decl->isAbstract() = %i\n", cxx_record_decl->isAbstract()); + // printf ("decl->hasTrivialConstructor() = %i\n", cxx_record_decl->hasTrivialConstructor()); + // printf ("decl->hasTrivialCopyConstructor() = %i\n", cxx_record_decl->hasTrivialCopyConstructor()); + // printf ("decl->hasTrivialCopyAssignment() = %i\n", cxx_record_decl->hasTrivialCopyAssignment()); + // printf ("decl->hasTrivialDestructor() = %i\n", cxx_record_decl->hasTrivialDestructor()); + return cxx_method_decl; +} + + +#pragma mark C++ Base Classes + +CXXBaseSpecifier * +ClangASTType::CreateBaseClassSpecifier (AccessType access, bool is_virtual, bool base_of_class) +{ + if (IsValid()) + return new CXXBaseSpecifier (SourceRange(), + is_virtual, + base_of_class, + ClangASTContext::ConvertAccessTypeToAccessSpecifier (access), + m_ast->getTrivialTypeSourceInfo (GetQualType()), + SourceLocation()); + return NULL; +} + +void +ClangASTType::DeleteBaseClassSpecifiers (CXXBaseSpecifier **base_classes, unsigned num_base_classes) +{ + for (unsigned i=0; i<num_base_classes; ++i) + { + delete base_classes[i]; + base_classes[i] = NULL; + } +} + +bool +ClangASTType::SetBaseClassesForClassType (CXXBaseSpecifier const * const *base_classes, + unsigned num_base_classes) +{ + if (IsValid()) + { + CXXRecordDecl *cxx_record_decl = GetAsCXXRecordDecl(); + if (cxx_record_decl) + { + cxx_record_decl->setBases(base_classes, num_base_classes); + return true; + } + } + return false; +} + +bool +ClangASTType::SetObjCSuperClass (const ClangASTType &superclass_clang_type) +{ + if (IsValid() && superclass_clang_type.IsValid()) + { + ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl (); + ObjCInterfaceDecl *super_interface_decl = superclass_clang_type.GetAsObjCInterfaceDecl (); + if (class_interface_decl && super_interface_decl) + { + class_interface_decl->setSuperClass(super_interface_decl); + return true; + } + } + return false; +} + +bool +ClangASTType::AddObjCClassProperty (const char *property_name, + const ClangASTType &property_clang_type, + ObjCIvarDecl *ivar_decl, + const char *property_setter_name, + const char *property_getter_name, + uint32_t property_attributes, + ClangASTMetadata *metadata) +{ + if (!IsValid() || !property_clang_type.IsValid() || property_name == NULL || property_name[0] == '\0') + return false; + + ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl (); + + if (class_interface_decl) + { + ClangASTType property_clang_type_to_access; + + if (property_clang_type.IsValid()) + property_clang_type_to_access = property_clang_type; + else if (ivar_decl) + property_clang_type_to_access = ClangASTType (m_ast, ivar_decl->getType()); + + if (class_interface_decl && property_clang_type_to_access.IsValid()) + { + clang::TypeSourceInfo *prop_type_source; + if (ivar_decl) + prop_type_source = m_ast->getTrivialTypeSourceInfo (ivar_decl->getType()); + else + prop_type_source = m_ast->getTrivialTypeSourceInfo (property_clang_type.GetQualType()); + + ObjCPropertyDecl *property_decl = ObjCPropertyDecl::Create (*m_ast, + class_interface_decl, + SourceLocation(), // Source Location + &m_ast->Idents.get(property_name), + SourceLocation(), //Source Location for AT + SourceLocation(), //Source location for ( + prop_type_source); + + if (property_decl) + { + if (metadata) + ClangASTContext::SetMetadata(m_ast, property_decl, *metadata); + + class_interface_decl->addDecl (property_decl); + + Selector setter_sel, getter_sel; + + if (property_setter_name != NULL) + { + std::string property_setter_no_colon(property_setter_name, strlen(property_setter_name) - 1); + clang::IdentifierInfo *setter_ident = &m_ast->Idents.get(property_setter_no_colon.c_str()); + setter_sel = m_ast->Selectors.getSelector(1, &setter_ident); + } + else if (!(property_attributes & DW_APPLE_PROPERTY_readonly)) + { + std::string setter_sel_string("set"); + setter_sel_string.push_back(::toupper(property_name[0])); + setter_sel_string.append(&property_name[1]); + clang::IdentifierInfo *setter_ident = &m_ast->Idents.get(setter_sel_string.c_str()); + setter_sel = m_ast->Selectors.getSelector(1, &setter_ident); + } + property_decl->setSetterName(setter_sel); + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_setter); + + if (property_getter_name != NULL) + { + clang::IdentifierInfo *getter_ident = &m_ast->Idents.get(property_getter_name); + getter_sel = m_ast->Selectors.getSelector(0, &getter_ident); + } + else + { + clang::IdentifierInfo *getter_ident = &m_ast->Idents.get(property_name); + getter_sel = m_ast->Selectors.getSelector(0, &getter_ident); + } + property_decl->setGetterName(getter_sel); + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_getter); + + if (ivar_decl) + property_decl->setPropertyIvarDecl (ivar_decl); + + if (property_attributes & DW_APPLE_PROPERTY_readonly) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readonly); + if (property_attributes & DW_APPLE_PROPERTY_readwrite) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_readwrite); + if (property_attributes & DW_APPLE_PROPERTY_assign) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_assign); + if (property_attributes & DW_APPLE_PROPERTY_retain) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_retain); + if (property_attributes & DW_APPLE_PROPERTY_copy) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_copy); + if (property_attributes & DW_APPLE_PROPERTY_nonatomic) + property_decl->setPropertyAttributes (clang::ObjCPropertyDecl::OBJC_PR_nonatomic); + + if (!getter_sel.isNull() && !class_interface_decl->lookupInstanceMethod(getter_sel)) + { + const bool isInstance = true; + const bool isVariadic = false; + const bool isSynthesized = false; + const bool isImplicitlyDeclared = true; + const bool isDefined = false; + const ObjCMethodDecl::ImplementationControl impControl = ObjCMethodDecl::None; + const bool HasRelatedResultType = false; + + ObjCMethodDecl *getter = ObjCMethodDecl::Create (*m_ast, + SourceLocation(), + SourceLocation(), + getter_sel, + property_clang_type_to_access.GetQualType(), + NULL, + class_interface_decl, + isInstance, + isVariadic, + isSynthesized, + isImplicitlyDeclared, + isDefined, + impControl, + HasRelatedResultType); + + if (getter && metadata) + ClangASTContext::SetMetadata(m_ast, getter, *metadata); + + getter->setMethodParams(*m_ast, ArrayRef<ParmVarDecl*>(), ArrayRef<SourceLocation>()); + + class_interface_decl->addDecl(getter); + } + + if (!setter_sel.isNull() && !class_interface_decl->lookupInstanceMethod(setter_sel)) + { + QualType result_type = m_ast->VoidTy; + + const bool isInstance = true; + const bool isVariadic = false; + const bool isSynthesized = false; + const bool isImplicitlyDeclared = true; + const bool isDefined = false; + const ObjCMethodDecl::ImplementationControl impControl = ObjCMethodDecl::None; + const bool HasRelatedResultType = false; + + ObjCMethodDecl *setter = ObjCMethodDecl::Create (*m_ast, + SourceLocation(), + SourceLocation(), + setter_sel, + result_type, + NULL, + class_interface_decl, + isInstance, + isVariadic, + isSynthesized, + isImplicitlyDeclared, + isDefined, + impControl, + HasRelatedResultType); + + if (setter && metadata) + ClangASTContext::SetMetadata(m_ast, setter, *metadata); + + llvm::SmallVector<ParmVarDecl *, 1> params; + + params.push_back (ParmVarDecl::Create (*m_ast, + setter, + SourceLocation(), + SourceLocation(), + NULL, // anonymous + property_clang_type_to_access.GetQualType(), + NULL, + SC_Auto, + NULL)); + + setter->setMethodParams(*m_ast, ArrayRef<ParmVarDecl*>(params), ArrayRef<SourceLocation>()); + + class_interface_decl->addDecl(setter); + } + + return true; + } + } + } + return false; +} + +bool +ClangASTType::IsObjCClassTypeAndHasIVars (bool check_superclass) const +{ + ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl (); + if (class_interface_decl) + return ObjCDeclHasIVars (class_interface_decl, check_superclass); + return false; +} + + +ObjCMethodDecl * +ClangASTType::AddMethodToObjCObjectType (const char *name, // the full symbol name as seen in the symbol table ("-[NString stringWithCString:]") + const ClangASTType &method_clang_type, + lldb::AccessType access, + bool is_artificial) +{ + if (!IsValid() || !method_clang_type.IsValid()) + return NULL; + + ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(); + + if (class_interface_decl == NULL) + return NULL; + + const char *selector_start = ::strchr (name, ' '); + if (selector_start == NULL) + return NULL; + + selector_start++; + llvm::SmallVector<IdentifierInfo *, 12> selector_idents; + + size_t len = 0; + const char *start; + //printf ("name = '%s'\n", name); + + unsigned num_selectors_with_args = 0; + for (start = selector_start; + start && *start != '\0' && *start != ']'; + start += len) + { + len = ::strcspn(start, ":]"); + bool has_arg = (start[len] == ':'); + if (has_arg) + ++num_selectors_with_args; + selector_idents.push_back (&m_ast->Idents.get (StringRef (start, len))); + if (has_arg) + len += 1; + } + + + if (selector_idents.size() == 0) + return 0; + + clang::Selector method_selector = m_ast->Selectors.getSelector (num_selectors_with_args ? selector_idents.size() : 0, + selector_idents.data()); + + QualType method_qual_type (method_clang_type.GetQualType()); + + // Populate the method decl with parameter decls + const clang::Type *method_type(method_qual_type.getTypePtr()); + + if (method_type == NULL) + return NULL; + + const FunctionProtoType *method_function_prototype (dyn_cast<FunctionProtoType>(method_type)); + + if (!method_function_prototype) + return NULL; + + + bool is_variadic = false; + bool is_synthesized = false; + bool is_defined = false; + ObjCMethodDecl::ImplementationControl imp_control = ObjCMethodDecl::None; + + const unsigned num_args = method_function_prototype->getNumArgs(); + + if (num_args != num_selectors_with_args) + return NULL; // some debug information is corrupt. We are not going to deal with it. + + ObjCMethodDecl *objc_method_decl = ObjCMethodDecl::Create (*m_ast, + SourceLocation(), // beginLoc, + SourceLocation(), // endLoc, + method_selector, + method_function_prototype->getResultType(), + NULL, // TypeSourceInfo *ResultTInfo, + GetDeclContextForType (), + name[0] == '-', + is_variadic, + is_synthesized, + true, // is_implicitly_declared; we force this to true because we don't have source locations + is_defined, + imp_control, + false /*has_related_result_type*/); + + + if (objc_method_decl == NULL) + return NULL; + + if (num_args > 0) + { + llvm::SmallVector<ParmVarDecl *, 12> params; + + for (unsigned param_index = 0; param_index < num_args; ++param_index) + { + params.push_back (ParmVarDecl::Create (*m_ast, + objc_method_decl, + SourceLocation(), + SourceLocation(), + NULL, // anonymous + method_function_prototype->getArgType(param_index), + NULL, + SC_Auto, + NULL)); + } + + objc_method_decl->setMethodParams(*m_ast, ArrayRef<ParmVarDecl*>(params), ArrayRef<SourceLocation>()); + } + + class_interface_decl->addDecl (objc_method_decl); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(objc_method_decl); +#endif + + return objc_method_decl; +} + + +clang::DeclContext * +ClangASTType::GetDeclContextForType () const +{ + if (!IsValid()) + return NULL; + + QualType qual_type(GetCanonicalQualType()); + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::UnaryTransform: break; + case clang::Type::FunctionNoProto: break; + case clang::Type::FunctionProto: break; + case clang::Type::IncompleteArray: break; + case clang::Type::VariableArray: break; + case clang::Type::ConstantArray: break; + case clang::Type::DependentSizedArray: break; + case clang::Type::ExtVector: break; + case clang::Type::DependentSizedExtVector: break; + case clang::Type::Vector: break; + case clang::Type::Builtin: break; + case clang::Type::BlockPointer: break; + case clang::Type::Pointer: break; + case clang::Type::LValueReference: break; + case clang::Type::RValueReference: break; + case clang::Type::MemberPointer: break; + case clang::Type::Complex: break; + case clang::Type::ObjCObject: break; + case clang::Type::ObjCInterface: return cast<ObjCObjectType>(qual_type.getTypePtr())->getInterface(); + case clang::Type::ObjCObjectPointer: return ClangASTType (m_ast, cast<ObjCObjectPointerType>(qual_type.getTypePtr())->getPointeeType()).GetDeclContextForType(); + case clang::Type::Record: return cast<RecordType>(qual_type)->getDecl(); + case clang::Type::Enum: return cast<EnumType>(qual_type)->getDecl(); + case clang::Type::Typedef: return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetDeclContextForType(); + case clang::Type::Elaborated: return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).GetDeclContextForType(); + case clang::Type::Paren: return ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).GetDeclContextForType(); + case clang::Type::TypeOfExpr: break; + case clang::Type::TypeOf: break; + case clang::Type::Decltype: break; + //case clang::Type::QualifiedName: break; + case clang::Type::TemplateSpecialization: break; + case clang::Type::DependentTemplateSpecialization: break; + case clang::Type::TemplateTypeParm: break; + case clang::Type::SubstTemplateTypeParm: break; + case clang::Type::SubstTemplateTypeParmPack:break; + case clang::Type::PackExpansion: break; + case clang::Type::UnresolvedUsing: break; + case clang::Type::Attributed: break; + case clang::Type::Auto: break; + case clang::Type::InjectedClassName: break; + case clang::Type::DependentName: break; + case clang::Type::Atomic: break; + } + // No DeclContext in this type... + return NULL; +} + +bool +ClangASTType::SetDefaultAccessForRecordFields (int default_accessibility, + int *assigned_accessibilities, + size_t num_assigned_accessibilities) +{ + if (IsValid()) + { + RecordDecl *record_decl = GetAsRecordDecl(); + if (record_decl) + { + uint32_t field_idx; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(), field_idx = 0; + field != field_end; + ++field, ++field_idx) + { + // If no accessibility was assigned, assign the correct one + if (field_idx < num_assigned_accessibilities && assigned_accessibilities[field_idx] == clang::AS_none) + field->setAccess ((AccessSpecifier)default_accessibility); + } + return true; + } + } + return false; +} + + +bool +ClangASTType::SetHasExternalStorage (bool has_extern) +{ + if (!IsValid()) + return false; + + QualType qual_type (GetCanonicalQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Record: + { + CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + if (cxx_record_decl) + { + cxx_record_decl->setHasExternalLexicalStorage (has_extern); + cxx_record_decl->setHasExternalVisibleStorage (has_extern); + return true; + } + } + break; + + case clang::Type::Enum: + { + EnumDecl *enum_decl = cast<EnumType>(qual_type)->getDecl(); + if (enum_decl) + { + enum_decl->setHasExternalLexicalStorage (has_extern); + enum_decl->setHasExternalVisibleStorage (has_extern); + return true; + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + { + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + + if (class_interface_decl) + { + class_interface_decl->setHasExternalLexicalStorage (has_extern); + class_interface_decl->setHasExternalVisibleStorage (has_extern); + return true; + } + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType()).SetHasExternalStorage (has_extern); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).SetHasExternalStorage (has_extern); + + case clang::Type::Paren: + return ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).SetHasExternalStorage (has_extern); + + default: + break; + } + return false; +} + +bool +ClangASTType::SetTagTypeKind (int kind) const +{ + if (IsValid()) + { + QualType tag_qual_type(GetQualType()); + const clang::Type *clang_type = tag_qual_type.getTypePtr(); + if (clang_type) + { + const TagType *tag_type = dyn_cast<TagType>(clang_type); + if (tag_type) + { + TagDecl *tag_decl = dyn_cast<TagDecl>(tag_type->getDecl()); + if (tag_decl) + { + tag_decl->setTagKind ((TagDecl::TagKind)kind); + return true; + } + } + } + } + return false; +} + + +#pragma mark TagDecl + +bool +ClangASTType::StartTagDeclarationDefinition () +{ + if (IsValid()) + { + QualType qual_type (GetQualType()); + const clang::Type *t = qual_type.getTypePtr(); + if (t) + { + const TagType *tag_type = dyn_cast<TagType>(t); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + { + tag_decl->startDefinition(); + return true; + } + } + + const ObjCObjectType *object_type = dyn_cast<ObjCObjectType>(t); + if (object_type) + { + ObjCInterfaceDecl *interface_decl = object_type->getInterface(); + if (interface_decl) + { + interface_decl->startDefinition(); + return true; + } + } + } + } + return false; +} + +bool +ClangASTType::CompleteTagDeclarationDefinition () +{ + if (IsValid()) + { + QualType qual_type (GetQualType()); + + CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + + if (cxx_record_decl) + { + cxx_record_decl->completeDefinition(); + + return true; + } + + const EnumType *enum_type = dyn_cast<EnumType>(qual_type.getTypePtr()); + + if (enum_type) + { + EnumDecl *enum_decl = enum_type->getDecl(); + + if (enum_decl) + { + /// TODO This really needs to be fixed. + + unsigned NumPositiveBits = 1; + unsigned NumNegativeBits = 0; + + QualType promotion_qual_type; + // If the enum integer type is less than an integer in bit width, + // then we must promote it to an integer size. + if (m_ast->getTypeSize(enum_decl->getIntegerType()) < m_ast->getTypeSize(m_ast->IntTy)) + { + if (enum_decl->getIntegerType()->isSignedIntegerType()) + promotion_qual_type = m_ast->IntTy; + else + promotion_qual_type = m_ast->UnsignedIntTy; + } + else + promotion_qual_type = enum_decl->getIntegerType(); + + enum_decl->completeDefinition(enum_decl->getIntegerType(), promotion_qual_type, NumPositiveBits, NumNegativeBits); + return true; + } + } + } + return false; +} + + + + + + + +bool +ClangASTType::AddEnumerationValueToEnumerationType (const ClangASTType &enumerator_clang_type, + const Declaration &decl, + const char *name, + int64_t enum_value, + uint32_t enum_value_bit_size) +{ + if (IsValid() && enumerator_clang_type.IsValid() && name && name[0]) + { + QualType enum_qual_type (GetCanonicalQualType()); + + bool is_signed = false; + enumerator_clang_type.IsIntegerType (is_signed); + const clang::Type *clang_type = enum_qual_type.getTypePtr(); + if (clang_type) + { + const EnumType *enum_type = dyn_cast<EnumType>(clang_type); + + if (enum_type) + { + llvm::APSInt enum_llvm_apsint(enum_value_bit_size, is_signed); + enum_llvm_apsint = enum_value; + EnumConstantDecl *enumerator_decl = + EnumConstantDecl::Create (*m_ast, + enum_type->getDecl(), + SourceLocation(), + name ? &m_ast->Idents.get(name) : NULL, // Identifier + enumerator_clang_type.GetQualType(), + NULL, + enum_llvm_apsint); + + if (enumerator_decl) + { + enum_type->getDecl()->addDecl(enumerator_decl); + +#ifdef LLDB_CONFIGURATION_DEBUG + VerifyDecl(enumerator_decl); +#endif + + return true; + } + } + } + } + return false; +} + + +ClangASTType +ClangASTType::GetEnumerationIntegerType () const +{ + QualType enum_qual_type (GetCanonicalQualType()); + const clang::Type *clang_type = enum_qual_type.getTypePtr(); + if (clang_type) + { + const EnumType *enum_type = dyn_cast<EnumType>(clang_type); + if (enum_type) + { + EnumDecl *enum_decl = enum_type->getDecl(); + if (enum_decl) + return ClangASTType (m_ast, enum_decl->getIntegerType()); + } + } + return ClangASTType(); +} + +ClangASTType +ClangASTType::CreateMemberPointerType (const ClangASTType &pointee_type) const +{ + if (IsValid() && pointee_type.IsValid()) + { + return ClangASTType (m_ast, m_ast->getMemberPointerType (pointee_type.GetQualType(), + GetQualType().getTypePtr())); + } + return ClangASTType(); +} + + +size_t +ClangASTType::ConvertStringToFloatValue (const char *s, uint8_t *dst, size_t dst_size) const +{ + if (IsValid()) + { + QualType qual_type (GetCanonicalQualType()); + uint32_t count = 0; + bool is_complex = false; + if (IsFloatingPointType (count, is_complex)) + { + // TODO: handle complex and vector types + if (count != 1) + return false; + + StringRef s_sref(s); + APFloat ap_float(m_ast->getFloatTypeSemantics(qual_type), s_sref); + + const uint64_t bit_size = m_ast->getTypeSize (qual_type); + const uint64_t byte_size = bit_size / 8; + if (dst_size >= byte_size) + { + if (bit_size == sizeof(float)*8) + { + float float32 = ap_float.convertToFloat(); + ::memcpy (dst, &float32, byte_size); + return byte_size; + } + else if (bit_size >= 64) + { + llvm::APInt ap_int(ap_float.bitcastToAPInt()); + ::memcpy (dst, ap_int.getRawData(), byte_size); + return byte_size; + } + } + } + } + return 0; +} + + + +//---------------------------------------------------------------------- +// Dumping types +//---------------------------------------------------------------------- +#define DEPTH_INCREMENT 2 + +void +ClangASTType::DumpValue (ExecutionContext *exe_ctx, + Stream *s, + lldb::Format format, + const lldb_private::DataExtractor &data, + lldb::offset_t data_byte_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + bool show_types, + bool show_summary, + bool verbose, + uint32_t depth) +{ + if (!IsValid()) + return; + + QualType qual_type(GetQualType()); + switch (qual_type->getTypeClass()) + { + case clang::Type::Record: + if (GetCompleteType ()) + { + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + uint32_t field_bit_offset = 0; + uint32_t field_byte_offset = 0; + const ASTRecordLayout &record_layout = m_ast->getASTRecordLayout(record_decl); + uint32_t child_idx = 0; + + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + if (cxx_record_decl) + { + // We might have base classes to print out first + CXXRecordDecl::base_class_const_iterator base_class, base_class_end; + for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); + base_class != base_class_end; + ++base_class) + { + const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(base_class->getType()->getAs<RecordType>()->getDecl()); + + // Skip empty base classes + if (verbose == false && ClangASTContext::RecordHasFields(base_class_decl) == false) + continue; + + if (base_class->isVirtual()) + field_bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8; + else + field_bit_offset = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8; + field_byte_offset = field_bit_offset / 8; + assert (field_bit_offset % 8 == 0); + if (child_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + QualType base_class_qual_type = base_class->getType(); + std::string base_class_type_name(base_class_qual_type.getAsString()); + + // Indent and print the base class type name + s->Printf("\n%*s%s ", depth + DEPTH_INCREMENT, "", base_class_type_name.c_str()); + + std::pair<uint64_t, unsigned> base_class_type_info = m_ast->getTypeInfo(base_class_qual_type); + + // Dump the value of the member + ClangASTType base_clang_type(m_ast, base_class_qual_type); + base_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + base_clang_type.GetFormat(), // The format with which to display the member + data, // Data buffer containing all bytes for this type + data_byte_offset + field_byte_offset,// Offset into "data" where to grab value from + base_class_type_info.first / 8, // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + + ++child_idx; + } + } + uint32_t field_idx = 0; + RecordDecl::field_iterator field, field_end; + for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) + { + // Print the starting squiggly bracket (if this is the + // first member) or comman (for member 2 and beyong) for + // the struct/union/class member. + if (child_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent + s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); + + QualType field_type = field->getType(); + // Print the member type if requested + // Figure out the type byte size (field_type_info.first) and + // alignment (field_type_info.second) from the AST context. + std::pair<uint64_t, unsigned> field_type_info = m_ast->getTypeInfo(field_type); + assert(field_idx < record_layout.getFieldCount()); + // Figure out the field offset within the current struct/union/class type + field_bit_offset = record_layout.getFieldOffset (field_idx); + field_byte_offset = field_bit_offset / 8; + uint32_t field_bitfield_bit_size = 0; + uint32_t field_bitfield_bit_offset = 0; + if (ClangASTContext::FieldIsBitfield (m_ast, *field, field_bitfield_bit_size)) + field_bitfield_bit_offset = field_bit_offset % 8; + + if (show_types) + { + std::string field_type_name(field_type.getAsString()); + if (field_bitfield_bit_size > 0) + s->Printf("(%s:%u) ", field_type_name.c_str(), field_bitfield_bit_size); + else + s->Printf("(%s) ", field_type_name.c_str()); + } + // Print the member name and equal sign + s->Printf("%s = ", field->getNameAsString().c_str()); + + + // Dump the value of the member + ClangASTType field_clang_type (m_ast, field_type); + field_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + field_clang_type.GetFormat(), // The format with which to display the member + data, // Data buffer containing all bytes for this type + data_byte_offset + field_byte_offset,// Offset into "data" where to grab value from + field_type_info.first / 8, // Size of this type in bytes + field_bitfield_bit_size, // Bitfield bit size + field_bitfield_bit_offset, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (child_idx > 0) + s->Printf("\n%*s}", depth, ""); + } + return; + + case clang::Type::Enum: + if (GetCompleteType ()) + { + const EnumType *enum_type = cast<EnumType>(qual_type.getTypePtr()); + const EnumDecl *enum_decl = enum_type->getDecl(); + assert(enum_decl); + EnumDecl::enumerator_iterator enum_pos, enum_end_pos; + lldb::offset_t offset = data_byte_offset; + const int64_t enum_value = data.GetMaxU64Bitfield(&offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset); + for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) + { + if (enum_pos->getInitVal() == enum_value) + { + s->Printf("%s", enum_pos->getNameAsString().c_str()); + return; + } + } + // If we have gotten here we didn't get find the enumerator in the + // enum decl, so just print the integer. + s->Printf("%" PRIi64, enum_value); + } + return; + + case clang::Type::ConstantArray: + { + const ConstantArrayType *array = cast<ConstantArrayType>(qual_type.getTypePtr()); + bool is_array_of_characters = false; + QualType element_qual_type = array->getElementType(); + + const clang::Type *canonical_type = element_qual_type->getCanonicalTypeInternal().getTypePtr(); + if (canonical_type) + is_array_of_characters = canonical_type->isCharType(); + + const uint64_t element_count = array->getSize().getLimitedValue(); + + std::pair<uint64_t, unsigned> field_type_info = m_ast->getTypeInfo(element_qual_type); + + uint32_t element_idx = 0; + uint32_t element_offset = 0; + uint64_t element_byte_size = field_type_info.first / 8; + uint32_t element_stride = element_byte_size; + + if (is_array_of_characters) + { + s->PutChar('"'); + data.Dump(s, data_byte_offset, lldb::eFormatChar, element_byte_size, element_count, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('"'); + return; + } + else + { + ClangASTType element_clang_type(m_ast, element_qual_type); + lldb::Format element_format = element_clang_type.GetFormat(); + + for (element_idx = 0; element_idx < element_count; ++element_idx) + { + // Print the starting squiggly bracket (if this is the + // first member) or comman (for member 2 and beyong) for + // the struct/union/class member. + if (element_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent and print the index + s->Printf("\n%*s[%u] ", depth + DEPTH_INCREMENT, "", element_idx); + + // Figure out the field offset within the current struct/union/class type + element_offset = element_idx * element_stride; + + // Dump the value of the member + element_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + element_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset + element_offset,// Offset into "data" where to grab value from + element_byte_size, // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (element_idx > 0) + s->Printf("\n%*s}", depth, ""); + } + } + return; + + case clang::Type::Typedef: + { + QualType typedef_qual_type = cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType(); + + ClangASTType typedef_clang_type (m_ast, typedef_qual_type); + lldb::Format typedef_format = typedef_clang_type.GetFormat(); + std::pair<uint64_t, unsigned> typedef_type_info = m_ast->getTypeInfo(typedef_qual_type); + uint64_t typedef_byte_size = typedef_type_info.first / 8; + + return typedef_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + typedef_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset, // Offset into "data" where to grab value from + typedef_byte_size, // Size of this type in bytes + bitfield_bit_size, // Bitfield bit size + bitfield_bit_offset,// Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth); // Scope depth for any types that have children + } + break; + + case clang::Type::Elaborated: + { + QualType elaborated_qual_type = cast<ElaboratedType>(qual_type)->getNamedType(); + ClangASTType elaborated_clang_type (m_ast, elaborated_qual_type); + lldb::Format elaborated_format = elaborated_clang_type.GetFormat(); + std::pair<uint64_t, unsigned> elaborated_type_info = m_ast->getTypeInfo(elaborated_qual_type); + uint64_t elaborated_byte_size = elaborated_type_info.first / 8; + + return elaborated_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + elaborated_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset, // Offset into "data" where to grab value from + elaborated_byte_size, // Size of this type in bytes + bitfield_bit_size, // Bitfield bit size + bitfield_bit_offset,// Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth); // Scope depth for any types that have children + } + break; + + case clang::Type::Paren: + { + QualType desugar_qual_type = cast<ParenType>(qual_type)->desugar(); + ClangASTType desugar_clang_type (m_ast, desugar_qual_type); + + lldb::Format desugar_format = desugar_clang_type.GetFormat(); + std::pair<uint64_t, unsigned> desugar_type_info = m_ast->getTypeInfo(desugar_qual_type); + uint64_t desugar_byte_size = desugar_type_info.first / 8; + + return desugar_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + desugar_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset, // Offset into "data" where to grab value from + desugar_byte_size, // Size of this type in bytes + bitfield_bit_size, // Bitfield bit size + bitfield_bit_offset,// Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth); // Scope depth for any types that have children + } + break; + + default: + // We are down the a scalar type that we just need to display. + data.Dump(s, + data_byte_offset, + format, + data_byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + bitfield_bit_size, + bitfield_bit_offset); + + if (show_summary) + DumpSummary (exe_ctx, s, data, data_byte_offset, data_byte_size); + break; + } +} + + + + +bool +ClangASTType::DumpTypeValue (Stream *s, + lldb::Format format, + const lldb_private::DataExtractor &data, + lldb::offset_t byte_offset, + size_t byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope) +{ + if (!IsValid()) + return false; + if (IsAggregateType()) + { + return false; + } + else + { + QualType qual_type(GetQualType()); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::Typedef: + { + QualType typedef_qual_type = cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType(); + ClangASTType typedef_clang_type (m_ast, typedef_qual_type); + if (format == eFormatDefault) + format = typedef_clang_type.GetFormat(); + std::pair<uint64_t, unsigned> typedef_type_info = m_ast->getTypeInfo(typedef_qual_type); + uint64_t typedef_byte_size = typedef_type_info.first / 8; + + return typedef_clang_type.DumpTypeValue (s, + format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + byte_offset, // Offset into "data" where to grab value from + typedef_byte_size, // Size of this type in bytes + bitfield_bit_size, // Size in bits of a bitfield value, if zero don't treat as a bitfield + bitfield_bit_offset, // Offset in bits of a bitfield value if bitfield_bit_size != 0 + exe_scope); + } + break; + + case clang::Type::Enum: + // If our format is enum or default, show the enumeration value as + // its enumeration string value, else just display it as requested. + if ((format == eFormatEnum || format == eFormatDefault) && GetCompleteType ()) + { + const EnumType *enum_type = cast<EnumType>(qual_type.getTypePtr()); + const EnumDecl *enum_decl = enum_type->getDecl(); + assert(enum_decl); + EnumDecl::enumerator_iterator enum_pos, enum_end_pos; + const bool is_signed = qual_type->isSignedIntegerOrEnumerationType(); + lldb::offset_t offset = byte_offset; + if (is_signed) + { + const int64_t enum_svalue = data.GetMaxS64Bitfield (&offset, byte_size, bitfield_bit_size, bitfield_bit_offset); + for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) + { + if (enum_pos->getInitVal().getSExtValue() == enum_svalue) + { + s->PutCString (enum_pos->getNameAsString().c_str()); + return true; + } + } + // If we have gotten here we didn't get find the enumerator in the + // enum decl, so just print the integer. + s->Printf("%" PRIi64, enum_svalue); + } + else + { + const uint64_t enum_uvalue = data.GetMaxU64Bitfield (&offset, byte_size, bitfield_bit_size, bitfield_bit_offset); + for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) + { + if (enum_pos->getInitVal().getZExtValue() == enum_uvalue) + { + s->PutCString (enum_pos->getNameAsString().c_str()); + return true; + } + } + // If we have gotten here we didn't get find the enumerator in the + // enum decl, so just print the integer. + s->Printf("%" PRIu64, enum_uvalue); + } + return true; + } + // format was not enum, just fall through and dump the value as requested.... + + default: + // We are down the a scalar type that we just need to display. + { + uint32_t item_count = 1; + // A few formats, we might need to modify our size and count for depending + // on how we are trying to display the value... + switch (format) + { + default: + case eFormatBoolean: + case eFormatBinary: + case eFormatComplex: + case eFormatCString: // NULL terminated C strings + case eFormatDecimal: + case eFormatEnum: + case eFormatHex: + case eFormatHexUppercase: + case eFormatFloat: + case eFormatOctal: + case eFormatOSType: + case eFormatUnsigned: + case eFormatPointer: + case eFormatVectorOfChar: + case eFormatVectorOfSInt8: + case eFormatVectorOfUInt8: + case eFormatVectorOfSInt16: + case eFormatVectorOfUInt16: + case eFormatVectorOfSInt32: + case eFormatVectorOfUInt32: + case eFormatVectorOfSInt64: + case eFormatVectorOfUInt64: + case eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + case eFormatVectorOfUInt128: + break; + + case eFormatChar: + case eFormatCharPrintable: + case eFormatCharArray: + case eFormatBytes: + case eFormatBytesWithASCII: + item_count = byte_size; + byte_size = 1; + break; + + case eFormatUnicode16: + item_count = byte_size / 2; + byte_size = 2; + break; + + case eFormatUnicode32: + item_count = byte_size / 4; + byte_size = 4; + break; + } + return data.Dump (s, + byte_offset, + format, + byte_size, + item_count, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + bitfield_bit_size, + bitfield_bit_offset, + exe_scope); + } + break; + } + } + return 0; +} + + + +void +ClangASTType::DumpSummary (ExecutionContext *exe_ctx, + Stream *s, + const lldb_private::DataExtractor &data, + lldb::offset_t data_byte_offset, + size_t data_byte_size) +{ + uint32_t length = 0; + if (IsCStringType (length)) + { + if (exe_ctx) + { + Process *process = exe_ctx->GetProcessPtr(); + if (process) + { + lldb::offset_t offset = data_byte_offset; + lldb::addr_t pointer_addresss = data.GetMaxU64(&offset, data_byte_size); + std::vector<uint8_t> buf; + if (length > 0) + buf.resize (length); + else + buf.resize (256); + + lldb_private::DataExtractor cstr_data(&buf.front(), buf.size(), process->GetByteOrder(), 4); + buf.back() = '\0'; + size_t bytes_read; + size_t total_cstr_len = 0; + Error error; + while ((bytes_read = process->ReadMemory (pointer_addresss, &buf.front(), buf.size(), error)) > 0) + { + const size_t len = strlen((const char *)&buf.front()); + if (len == 0) + break; + if (total_cstr_len == 0) + s->PutCString (" \""); + cstr_data.Dump(s, 0, lldb::eFormatChar, 1, len, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + total_cstr_len += len; + if (len < buf.size()) + break; + pointer_addresss += total_cstr_len; + } + if (total_cstr_len > 0) + s->PutChar ('"'); + } + } + } +} + +void +ClangASTType::DumpTypeDescription () const +{ + StreamFile s (stdout, false); + DumpTypeDescription (&s); + ClangASTMetadata *metadata = ClangASTContext::GetMetadata (m_ast, m_type); + if (metadata) + { + metadata->Dump (&s); + } +} + +void +ClangASTType::DumpTypeDescription (Stream *s) const +{ + if (IsValid()) + { + QualType qual_type(GetQualType()); + + SmallVector<char, 1024> buf; + raw_svector_ostream llvm_ostrm (buf); + + const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + switch (type_class) + { + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + { + GetCompleteType (); + + const ObjCObjectType *objc_class_type = dyn_cast<ObjCObjectType>(qual_type.getTypePtr()); + assert (objc_class_type); + if (objc_class_type) + { + ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + if (class_interface_decl) + { + PrintingPolicy policy = m_ast->getPrintingPolicy(); + class_interface_decl->print(llvm_ostrm, policy, s->GetIndentLevel()); + } + } + } + break; + + case clang::Type::Typedef: + { + const TypedefType *typedef_type = qual_type->getAs<TypedefType>(); + if (typedef_type) + { + const TypedefNameDecl *typedef_decl = typedef_type->getDecl(); + std::string clang_typedef_name (typedef_decl->getQualifiedNameAsString()); + if (!clang_typedef_name.empty()) + { + s->PutCString ("typedef "); + s->PutCString (clang_typedef_name.c_str()); + } + } + } + break; + + case clang::Type::Elaborated: + ClangASTType (m_ast, cast<ElaboratedType>(qual_type)->getNamedType()).DumpTypeDescription(s); + return; + + case clang::Type::Paren: + ClangASTType (m_ast, cast<ParenType>(qual_type)->desugar()).DumpTypeDescription(s); + return; + + case clang::Type::Record: + { + GetCompleteType (); + + const RecordType *record_type = cast<RecordType>(qual_type.getTypePtr()); + const RecordDecl *record_decl = record_type->getDecl(); + const CXXRecordDecl *cxx_record_decl = dyn_cast<CXXRecordDecl>(record_decl); + + if (cxx_record_decl) + cxx_record_decl->print(llvm_ostrm, m_ast->getPrintingPolicy(), s->GetIndentLevel()); + else + record_decl->print(llvm_ostrm, m_ast->getPrintingPolicy(), s->GetIndentLevel()); + } + break; + + default: + { + const TagType *tag_type = dyn_cast<TagType>(qual_type.getTypePtr()); + if (tag_type) + { + TagDecl *tag_decl = tag_type->getDecl(); + if (tag_decl) + tag_decl->print(llvm_ostrm, 0); + } + else + { + std::string clang_type_name(qual_type.getAsString()); + if (!clang_type_name.empty()) + s->PutCString (clang_type_name.c_str()); + } + } + } + + llvm_ostrm.flush(); + if (buf.size() > 0) + { + s->Write (buf.data(), buf.size()); + } + } +} + +bool +ClangASTType::GetValueAsScalar (const lldb_private::DataExtractor &data, + lldb::offset_t data_byte_offset, + size_t data_byte_size, + Scalar &value) const +{ + if (!IsValid()) + return false; + + if (IsAggregateType ()) + { + return false; // Aggregate types don't have scalar values + } + else + { + uint64_t count = 0; + lldb::Encoding encoding = GetEncoding (count); + + if (encoding == lldb::eEncodingInvalid || count != 1) + return false; + + const uint64_t byte_size = GetByteSize(); + lldb::offset_t offset = data_byte_offset; + switch (encoding) + { + case lldb::eEncodingInvalid: + break; + case lldb::eEncodingVector: + break; + case lldb::eEncodingUint: + if (byte_size <= sizeof(unsigned long long)) + { + uint64_t uval64 = data.GetMaxU64 (&offset, byte_size); + if (byte_size <= sizeof(unsigned int)) + { + value = (unsigned int)uval64; + return true; + } + else if (byte_size <= sizeof(unsigned long)) + { + value = (unsigned long)uval64; + return true; + } + else if (byte_size <= sizeof(unsigned long long)) + { + value = (unsigned long long )uval64; + return true; + } + else + value.Clear(); + } + break; + + case lldb::eEncodingSint: + if (byte_size <= sizeof(long long)) + { + int64_t sval64 = data.GetMaxS64 (&offset, byte_size); + if (byte_size <= sizeof(int)) + { + value = (int)sval64; + return true; + } + else if (byte_size <= sizeof(long)) + { + value = (long)sval64; + return true; + } + else if (byte_size <= sizeof(long long)) + { + value = (long long )sval64; + return true; + } + else + value.Clear(); + } + break; + + case lldb::eEncodingIEEE754: + if (byte_size <= sizeof(long double)) + { + uint32_t u32; + uint64_t u64; + if (byte_size == sizeof(float)) + { + if (sizeof(float) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((float *)&u32); + return true; + } + else if (sizeof(float) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((float *)&u64); + return true; + } + } + else + if (byte_size == sizeof(double)) + { + if (sizeof(double) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((double *)&u32); + return true; + } + else if (sizeof(double) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((double *)&u64); + return true; + } + } + else + if (byte_size == sizeof(long double)) + { + if (sizeof(long double) == sizeof(uint32_t)) + { + u32 = data.GetU32(&offset); + value = *((long double *)&u32); + return true; + } + else if (sizeof(long double) == sizeof(uint64_t)) + { + u64 = data.GetU64(&offset); + value = *((long double *)&u64); + return true; + } + } + } + break; + } + } + return false; +} + +bool +ClangASTType::SetValueFromScalar (const Scalar &value, Stream &strm) +{ + // Aggregate types don't have scalar values + if (!IsAggregateType ()) + { + strm.GetFlags().Set(Stream::eBinary); + uint64_t count = 0; + lldb::Encoding encoding = GetEncoding (count); + + if (encoding == lldb::eEncodingInvalid || count != 1) + return false; + + const uint64_t bit_width = GetBitSize(); + // This function doesn't currently handle non-byte aligned assignments + if ((bit_width % 8) != 0) + return false; + + const uint64_t byte_size = (bit_width + 7 ) / 8; + switch (encoding) + { + case lldb::eEncodingInvalid: + break; + case lldb::eEncodingVector: + break; + case lldb::eEncodingUint: + switch (byte_size) + { + case 1: strm.PutHex8(value.UInt()); return true; + case 2: strm.PutHex16(value.UInt()); return true; + case 4: strm.PutHex32(value.UInt()); return true; + case 8: strm.PutHex64(value.ULongLong()); return true; + default: + break; + } + break; + + case lldb::eEncodingSint: + switch (byte_size) + { + case 1: strm.PutHex8(value.SInt()); return true; + case 2: strm.PutHex16(value.SInt()); return true; + case 4: strm.PutHex32(value.SInt()); return true; + case 8: strm.PutHex64(value.SLongLong()); return true; + default: + break; + } + break; + + case lldb::eEncodingIEEE754: + if (byte_size <= sizeof(long double)) + { + if (byte_size == sizeof(float)) + { + strm.PutFloat(value.Float()); + return true; + } + else + if (byte_size == sizeof(double)) + { + strm.PutDouble(value.Double()); + return true; + } + else + if (byte_size == sizeof(long double)) + { + strm.PutDouble(value.LongDouble()); + return true; + } + } + break; + } + } + return false; +} + +bool +ClangASTType::ReadFromMemory (lldb_private::ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + lldb_private::DataExtractor &data) +{ + if (!IsValid()) + return false; + + // Can't convert a file address to anything valid without more + // context (which Module it came from) + if (address_type == eAddressTypeFile) + return false; + + if (!GetCompleteType()) + return false; + + const uint64_t byte_size = GetByteSize(); + if (data.GetByteSize() < byte_size) + { + lldb::DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = (uint8_t*)data.PeekData(0, byte_size); + if (dst != NULL) + { + if (address_type == eAddressTypeHost) + { + if (addr == 0) + return false; + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + addr, byte_size); + return true; + } + else + { + Process *process = NULL; + if (exe_ctx) + process = exe_ctx->GetProcessPtr(); + if (process) + { + Error error; + return process->ReadMemory(addr, dst, byte_size, error) == byte_size; + } + } + } + return false; +} + +bool +ClangASTType::WriteToMemory (lldb_private::ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + StreamString &new_value) +{ + if (!IsValid()) + return false; + + // Can't convert a file address to anything valid without more + // context (which Module it came from) + if (address_type == eAddressTypeFile) + return false; + + if (!GetCompleteType()) + return false; + + const uint64_t byte_size = GetByteSize(); + + if (byte_size > 0) + { + if (address_type == eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy ((void *)addr, new_value.GetData(), byte_size); + return true; + } + else + { + Process *process = NULL; + if (exe_ctx) + process = exe_ctx->GetProcessPtr(); + if (process) + { + Error error; + return process->WriteMemory(addr, new_value.GetData(), byte_size, error) == byte_size; + } + } + } + return false; +} + + +//CXXRecordDecl * +//ClangASTType::GetAsCXXRecordDecl (lldb::clang_type_t opaque_clang_qual_type) +//{ +// if (opaque_clang_qual_type) +// return QualType::getFromOpaquePtr(opaque_clang_qual_type)->getAsCXXRecordDecl(); +// return NULL; +//} + +bool +lldb_private::operator == (const lldb_private::ClangASTType &lhs, const lldb_private::ClangASTType &rhs) +{ + return lhs.GetASTContext() == rhs.GetASTContext() && lhs.GetOpaqueQualType() == rhs.GetOpaqueQualType(); +} + + +bool +lldb_private::operator != (const lldb_private::ClangASTType &lhs, const lldb_private::ClangASTType &rhs) +{ + return lhs.GetASTContext() != rhs.GetASTContext() || lhs.GetOpaqueQualType() != rhs.GetOpaqueQualType(); +} + + diff --git a/source/Symbol/ClangExternalASTSourceCallbacks.cpp b/source/Symbol/ClangExternalASTSourceCallbacks.cpp new file mode 100644 index 000000000000..b2328d6e5b42 --- /dev/null +++ b/source/Symbol/ClangExternalASTSourceCallbacks.cpp @@ -0,0 +1,163 @@ +//===-- ClangExternalASTSourceCallbacks.cpp ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/ClangExternalASTSourceCallbacks.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Clang headers like to use NDEBUG inside of them to enable/disable debug +// releated features using "#ifndef NDEBUG" preprocessor blocks to do one thing +// or another. This is bad because it means that if clang was built in release +// mode, it assumes that you are building in release mode which is not always +// the case. You can end up with functions that are defined as empty in header +// files when NDEBUG is not defined, and this can cause link errors with the +// clang .a files that you have since you might be missing functions in the .a +// file. So we have to define NDEBUG when including clang headers to avoid any +// mismatches. This is covered by rdar://problem/8691220 + +#if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF) +#define LLDB_DEFINED_NDEBUG_FOR_CLANG +#define NDEBUG +// Need to include assert.h so it is as clang would expect it to be (disabled) +#include <assert.h> +#endif + +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclarationName.h" + +#ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG +#undef NDEBUG +#undef LLDB_DEFINED_NDEBUG_FOR_CLANG +// Need to re-include assert.h so it is as _we_ would expect it to be (enabled) +#include <assert.h> +#endif + +#include "lldb/Core/Log.h" + +using namespace clang; +using namespace lldb_private; + +bool +ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName +( + const clang::DeclContext *decl_ctx, + clang::DeclarationName clang_decl_name +) +{ + if (m_callback_find_by_name) + { + llvm::SmallVector <clang::NamedDecl *, 3> results; + + m_callback_find_by_name (m_callback_baton, decl_ctx, clang_decl_name, &results); + + SetExternalVisibleDeclsForName(decl_ctx, clang_decl_name, results); + + return (results.size() != 0); + } + + std::string decl_name (clang_decl_name.getAsString()); + + switch (clang_decl_name.getNameKind()) { + // Normal identifiers. + case clang::DeclarationName::Identifier: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"Identifier\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + if (clang_decl_name.getAsIdentifierInfo()->getBuiltinID() != 0) + { + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + break; + + case clang::DeclarationName::ObjCZeroArgSelector: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"ObjCZeroArgSelector\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::ObjCOneArgSelector: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"ObjCOneArgSelector\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::ObjCMultiArgSelector: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"ObjCMultiArgSelector\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::CXXConstructorName: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXConstructorName\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + + case clang::DeclarationName::CXXDestructorName: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXDestructorName\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::CXXConversionFunctionName: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXConversionFunctionName\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::CXXOperatorName: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXOperatorName\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::CXXLiteralOperatorName: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXLiteralOperatorName\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + case clang::DeclarationName::CXXUsingDirective: + //printf ("ClangExternalASTSourceCallbacks::FindExternalVisibleDeclsByName(decl_ctx = %p, decl_name = { kind = \"CXXUsingDirective\", name = \"%s\")\n", decl_ctx, decl_name.c_str()); + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + } + + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; +} + +void +ClangExternalASTSourceCallbacks::CompleteType (TagDecl *tag_decl) +{ + if (m_callback_tag_decl) + m_callback_tag_decl (m_callback_baton, tag_decl); +} + +void +ClangExternalASTSourceCallbacks::CompleteType (ObjCInterfaceDecl *objc_decl) +{ + if (m_callback_objc_decl) + m_callback_objc_decl (m_callback_baton, objc_decl); +} + +bool +ClangExternalASTSourceCallbacks::layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &VirtualBaseOffsets) +{ + if (m_callback_layout_record_type) + return m_callback_layout_record_type(m_callback_baton, + Record, + Size, + Alignment, + FieldOffsets, + BaseOffsets, + VirtualBaseOffsets); + + return false; +} + diff --git a/source/Symbol/ClangExternalASTSourceCommon.cpp b/source/Symbol/ClangExternalASTSourceCommon.cpp new file mode 100644 index 000000000000..697dc7eec493 --- /dev/null +++ b/source/Symbol/ClangExternalASTSourceCommon.cpp @@ -0,0 +1,89 @@ +//===-- ClangExternalASTSourceCommon.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +#define ClangExternalASTSourceCommon_MAGIC (0x00112233aabbccddull) + +uint64_t g_TotalSizeOfMetadata = 0; + +ClangExternalASTSourceCommon::ClangExternalASTSourceCommon() : clang::ExternalASTSource() +{ + m_magic = ClangExternalASTSourceCommon_MAGIC; + + g_TotalSizeOfMetadata += m_metadata.size(); +} + +ClangExternalASTSourceCommon::~ClangExternalASTSourceCommon() +{ + g_TotalSizeOfMetadata -= m_metadata.size(); +} + +ClangASTMetadata * +ClangExternalASTSourceCommon::GetMetadata (const void *object) +{ + assert (m_magic == ClangExternalASTSourceCommon_MAGIC); + + if (HasMetadata (object)) + return &m_metadata[object]; + else + return NULL; +} + +void +ClangExternalASTSourceCommon::SetMetadata (const void *object, ClangASTMetadata &metadata) +{ + assert (m_magic == ClangExternalASTSourceCommon_MAGIC); + + uint64_t orig_size = m_metadata.size(); + m_metadata[object] = metadata; + uint64_t new_size = m_metadata.size(); + g_TotalSizeOfMetadata += (new_size - orig_size); +} + +bool +ClangExternalASTSourceCommon::HasMetadata (const void *object) +{ + assert (m_magic == ClangExternalASTSourceCommon_MAGIC); + + return m_metadata.find(object) != m_metadata.end(); +} + +void +ClangASTMetadata::Dump (Stream *s) +{ + lldb::user_id_t uid = GetUserID (); + + if (uid != LLDB_INVALID_UID) + { + s->Printf ("uid=0x%" PRIx64, uid); + } + + uint64_t isa_ptr = GetISAPtr (); + if (isa_ptr != 0) + { + s->Printf ("isa_ptr=0x%" PRIx64, isa_ptr); + } + + const char *obj_ptr_name = GetObjectPtrName(); + if (obj_ptr_name) + { + s->Printf ("obj_ptr_name=\"%s\" ", obj_ptr_name); + } + + if (m_is_dynamic_cxx) + { + s->Printf ("is_dynamic_cxx=%i ", m_is_dynamic_cxx); + } + s->EOL(); +} + diff --git a/source/Symbol/ClangNamespaceDecl.cpp b/source/Symbol/ClangNamespaceDecl.cpp new file mode 100644 index 000000000000..568b0263f0fe --- /dev/null +++ b/source/Symbol/ClangNamespaceDecl.cpp @@ -0,0 +1,25 @@ +//===-- ClangNamespaceDecl.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/ClangNamespaceDecl.h" + +#include "clang/AST/Decl.h" + +namespace lldb_private { + +std::string +ClangNamespaceDecl::GetQualifiedName () const +{ + if (m_namespace_decl) + return m_namespace_decl->getQualifiedNameAsString(); + return std::string(); +} + + +} diff --git a/source/Symbol/CompileUnit.cpp b/source/Symbol/CompileUnit.cpp new file mode 100644 index 000000000000..751b09ec5a6b --- /dev/null +++ b/source/Symbol/CompileUnit.cpp @@ -0,0 +1,464 @@ +//===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Language.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +using namespace lldb; +using namespace lldb_private; + +CompileUnit::CompileUnit (const lldb::ModuleSP &module_sp, void *user_data, const char *pathname, const lldb::user_id_t cu_sym_id, lldb::LanguageType language) : + ModuleChild(module_sp), + FileSpec (pathname, false), + UserID(cu_sym_id), + m_user_data (user_data), + m_language (language), + m_flags (0), + m_functions (), + m_support_files (), + m_line_table_ap (), + m_variables() +{ + if (language != eLanguageTypeUnknown) + m_flags.Set(flagsParsedLanguage); + assert(module_sp); +} + +CompileUnit::CompileUnit (const lldb::ModuleSP &module_sp, void *user_data, const FileSpec &fspec, const lldb::user_id_t cu_sym_id, lldb::LanguageType language) : + ModuleChild(module_sp), + FileSpec (fspec), + UserID(cu_sym_id), + m_user_data (user_data), + m_language (language), + m_flags (0), + m_functions (), + m_support_files (), + m_line_table_ap (), + m_variables() +{ + if (language != eLanguageTypeUnknown) + m_flags.Set(flagsParsedLanguage); + assert(module_sp); +} + +CompileUnit::~CompileUnit () +{ +} + +void +CompileUnit::CalculateSymbolContext(SymbolContext* sc) +{ + sc->comp_unit = this; + GetModule()->CalculateSymbolContext(sc); +} + +ModuleSP +CompileUnit::CalculateSymbolContextModule () +{ + return GetModule(); +} + +CompileUnit * +CompileUnit::CalculateSymbolContextCompileUnit () +{ + return this; +} + +void +CompileUnit::DumpSymbolContext(Stream *s) +{ + GetModule()->DumpSymbolContext(s); + s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID()); +} + + +void +CompileUnit::GetDescription(Stream *s, lldb::DescriptionLevel level) const +{ + Language language(m_language); + *s << "id = " << (const UserID&)*this << ", file = \"" << (const FileSpec&)*this << "\", language = \"" << language << '"'; +} + + +//---------------------------------------------------------------------- +// Dump the current contents of this object. No functions that cause on +// demand parsing of functions, globals, statics are called, so this +// is a good function to call to get an idea of the current contents of +// the CompileUnit object. +//---------------------------------------------------------------------- +void +CompileUnit::Dump(Stream *s, bool show_context) const +{ + s->Printf("%p: ", this); + s->Indent(); + *s << "CompileUnit" << (const UserID&)*this + << ", language = \"" << (const Language&)*this + << "\", file = '" << (const FileSpec&)*this << "'\n"; + +// m_types.Dump(s); + + if (m_variables.get()) + { + s->IndentMore(); + m_variables->Dump(s, show_context); + s->IndentLess(); + } + + if (!m_functions.empty()) + { + s->IndentMore(); + std::vector<FunctionSP>::const_iterator pos; + std::vector<FunctionSP>::const_iterator end = m_functions.end(); + for (pos = m_functions.begin(); pos != end; ++pos) + { + (*pos)->Dump(s, show_context); + } + + s->IndentLess(); + s->EOL(); + } +} + +//---------------------------------------------------------------------- +// Add a function to this compile unit +//---------------------------------------------------------------------- +void +CompileUnit::AddFunction(FunctionSP& funcSP) +{ + // TODO: order these by address + m_functions.push_back(funcSP); +} + +FunctionSP +CompileUnit::GetFunctionAtIndex (size_t idx) +{ + FunctionSP funcSP; + if (idx < m_functions.size()) + funcSP = m_functions[idx]; + return funcSP; +} + +//---------------------------------------------------------------------- +// Find functions using the a Mangled::Tokens token list. This +// function currently implements an interative approach designed to find +// all instances of certain functions. It isn't designed to the the +// quickest way to lookup functions as it will need to iterate through +// all functions and see if they match, though it does provide a powerful +// and context sensitive way to search for all functions with a certain +// name, all functions in a namespace, or all functions of a template +// type. See Mangled::Tokens::Parse() comments for more information. +// +// The function prototype will need to change to return a list of +// results. It was originally used to help debug the Mangled class +// and the Mangled::Tokens::MatchesQuery() function and it currently +// will print out a list of matching results for the functions that +// are currently in this compile unit. +// +// A FindFunctions method should be called prior to this that takes +// a regular function name (const char * or ConstString as a parameter) +// before resorting to this slower but more complete function. The +// other FindFunctions method should be able to take advantage of any +// accelerator tables available in the debug information (which is +// parsed by the SymbolFile parser plug-ins and registered with each +// Module). +//---------------------------------------------------------------------- +//void +//CompileUnit::FindFunctions(const Mangled::Tokens& tokens) +//{ +// if (!m_functions.empty()) +// { +// Stream s(stdout); +// std::vector<FunctionSP>::const_iterator pos; +// std::vector<FunctionSP>::const_iterator end = m_functions.end(); +// for (pos = m_functions.begin(); pos != end; ++pos) +// { +// const ConstString& demangled = (*pos)->Mangled().Demangled(); +// if (demangled) +// { +// const Mangled::Tokens& func_tokens = (*pos)->Mangled().GetTokens(); +// if (func_tokens.MatchesQuery (tokens)) +// s << "demangled MATCH found: " << demangled << "\n"; +// } +// } +// } +//} + +FunctionSP +CompileUnit::FindFunctionByUID (lldb::user_id_t func_uid) +{ + FunctionSP funcSP; + if (!m_functions.empty()) + { + std::vector<FunctionSP>::const_iterator pos; + std::vector<FunctionSP>::const_iterator end = m_functions.end(); + for (pos = m_functions.begin(); pos != end; ++pos) + { + if ((*pos)->GetID() == func_uid) + { + funcSP = *pos; + break; + } + } + } + return funcSP; +} + + +lldb::LanguageType +CompileUnit::GetLanguage() +{ + if (m_language == eLanguageTypeUnknown) + { + if (m_flags.IsClear(flagsParsedLanguage)) + { + m_flags.Set(flagsParsedLanguage); + SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + m_language = symbol_vendor->ParseCompileUnitLanguage(sc); + } + } + } + return m_language; +} + +LineTable* +CompileUnit::GetLineTable() +{ + if (m_line_table_ap.get() == NULL) + { + if (m_flags.IsClear(flagsParsedLineTable)) + { + m_flags.Set(flagsParsedLineTable); + SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + symbol_vendor->ParseCompileUnitLineTable(sc); + } + } + } + return m_line_table_ap.get(); +} + +void +CompileUnit::SetLineTable(LineTable* line_table) +{ + if (line_table == NULL) + m_flags.Clear(flagsParsedLineTable); + else + m_flags.Set(flagsParsedLineTable); + m_line_table_ap.reset(line_table); +} + +VariableListSP +CompileUnit::GetVariableList(bool can_create) +{ + if (m_variables.get() == NULL && can_create) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + assert(sc.module_sp); + sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc); + } + + return m_variables; +} + +uint32_t +CompileUnit::FindLineEntry (uint32_t start_idx, uint32_t line, const FileSpec* file_spec_ptr, bool exact, LineEntry *line_entry_ptr) +{ + uint32_t file_idx = 0; + + if (file_spec_ptr) + { + file_idx = GetSupportFiles().FindFileIndex (1, *file_spec_ptr, true); + if (file_idx == UINT32_MAX) + return UINT32_MAX; + } + else + { + // All the line table entries actually point to the version of the Compile + // Unit that is in the support files (the one at 0 was artifically added.) + // So prefer the one further on in the support files if it exists... + FileSpecList &support_files = GetSupportFiles(); + const bool full = true; + file_idx = support_files.FindFileIndex (1, support_files.GetFileSpecAtIndex(0), full); + if (file_idx == UINT32_MAX) + file_idx = 0; + } + LineTable *line_table = GetLineTable(); + if (line_table) + return line_table->FindLineEntryIndexByFileIndex (start_idx, file_idx, line, exact, line_entry_ptr); + return UINT32_MAX; +} + + + + +uint32_t +CompileUnit::ResolveSymbolContext +( + const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + bool exact, + uint32_t resolve_scope, + SymbolContextList &sc_list +) +{ + // First find all of the file indexes that match our "file_spec". If + // "file_spec" has an empty directory, then only compare the basenames + // when finding file indexes + std::vector<uint32_t> file_indexes; + const bool full_match = file_spec.GetDirectory(); + bool file_spec_matches_cu_file_spec = FileSpec::Equal(file_spec, *this, full_match); + + // If we are not looking for inlined functions and our file spec doesn't + // match then we are done... + if (file_spec_matches_cu_file_spec == false && check_inlines == false) + return 0; + + uint32_t file_idx = GetSupportFiles().FindFileIndex (1, file_spec, true); + while (file_idx != UINT32_MAX) + { + file_indexes.push_back (file_idx); + file_idx = GetSupportFiles().FindFileIndex (file_idx + 1, file_spec, true); + } + + const size_t num_file_indexes = file_indexes.size(); + if (num_file_indexes == 0) + return 0; + + const uint32_t prev_size = sc_list.GetSize(); + + SymbolContext sc(GetModule()); + sc.comp_unit = this; + + + if (line != 0) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != NULL) + { + uint32_t found_line; + uint32_t line_idx; + + if (num_file_indexes == 1) + { + // We only have a single support file that matches, so use + // the line table function that searches for a line entries + // that match a single support file index + LineEntry line_entry; + line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_indexes.front(), line, exact, &line_entry); + + // If "exact == true", then "found_line" will be the same + // as "line". If "exact == false", the "found_line" will be the + // closest line entry with a line number greater than "line" and + // we will use this for our subsequent line exact matches below. + found_line = line_entry.line; + + while (line_idx != UINT32_MAX) + { + // If they only asked for the line entry, then we're done, we can just copy that over. + // But if they wanted more than just the line number, fill it in. + if (resolve_scope == eSymbolContextLineEntry) + { + sc.line_entry = line_entry; + } + else + { + line_entry.range.GetBaseAddress().CalculateSymbolContext(&sc, resolve_scope); + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_indexes.front(), found_line, true, &line_entry); + } + } + else + { + // We found multiple support files that match "file_spec" so use + // the line table function that searches for a line entries + // that match a multiple support file indexes. + LineEntry line_entry; + line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_indexes, line, exact, &line_entry); + + // If "exact == true", then "found_line" will be the same + // as "line". If "exact == false", the "found_line" will be the + // closest line entry with a line number greater than "line" and + // we will use this for our subsequent line exact matches below. + found_line = line_entry.line; + + while (line_idx != UINT32_MAX) + { + if (resolve_scope == eSymbolContextLineEntry) + { + sc.line_entry = line_entry; + } + else + { + line_entry.range.GetBaseAddress().CalculateSymbolContext(&sc, resolve_scope); + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_indexes, found_line, true, &line_entry); + } + } + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + return sc_list.GetSize() - prev_size; +} + +void +CompileUnit::SetVariableList(VariableListSP &variables) +{ + m_variables = variables; +} + +FileSpecList& +CompileUnit::GetSupportFiles () +{ + if (m_support_files.GetSize() == 0) + { + if (m_flags.IsClear(flagsParsedSupportFiles)) + { + m_flags.Set(flagsParsedSupportFiles); + SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + symbol_vendor->ParseCompileUnitSupportFiles(sc, m_support_files); + } + } + } + return m_support_files; +} + +void * +CompileUnit::GetUserData () const +{ + return m_user_data; +} + + diff --git a/source/Symbol/DWARFCallFrameInfo.cpp b/source/Symbol/DWARFCallFrameInfo.cpp new file mode 100644 index 000000000000..e8f99a980116 --- /dev/null +++ b/source/Symbol/DWARFCallFrameInfo.cpp @@ -0,0 +1,815 @@ +//===-- DWARFCallFrameInfo.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +#include <list> + +#include "lldb/Core/Log.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile& objfile, SectionSP& section_sp, lldb::RegisterKind reg_kind, bool is_eh_frame) : + m_objfile (objfile), + m_section_sp (section_sp), + m_reg_kind (reg_kind), // The flavor of registers that the CFI data uses (enum RegisterKind) + m_flags (), + m_cie_map (), + m_cfi_data (), + m_cfi_data_initialized (false), + m_fde_index (), + m_fde_index_initialized (false), + m_is_eh_frame (is_eh_frame) +{ +} + +DWARFCallFrameInfo::~DWARFCallFrameInfo() +{ +} + + +bool +DWARFCallFrameInfo::GetUnwindPlan (Address addr, UnwindPlan& unwind_plan) +{ + FDEEntryMap::Entry fde_entry; + + // Make sure that the Address we're searching for is the same object file + // as this DWARFCallFrameInfo, we only store File offsets in m_fde_index. + ModuleSP module_sp = addr.GetModule(); + if (module_sp.get() == NULL || module_sp->GetObjectFile() == NULL || module_sp->GetObjectFile() != &m_objfile) + return false; + + if (GetFDEEntryByFileAddress (addr.GetFileAddress(), fde_entry) == false) + return false; + return FDEToUnwindPlan (fde_entry.data, addr, unwind_plan); +} + +bool +DWARFCallFrameInfo::GetAddressRange (Address addr, AddressRange &range) +{ + + // Make sure that the Address we're searching for is the same object file + // as this DWARFCallFrameInfo, we only store File offsets in m_fde_index. + ModuleSP module_sp = addr.GetModule(); + if (module_sp.get() == NULL || module_sp->GetObjectFile() == NULL || module_sp->GetObjectFile() != &m_objfile) + return false; + + if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) + return false; + GetFDEIndex(); + FDEEntryMap::Entry *fde_entry = m_fde_index.FindEntryThatContains (addr.GetFileAddress()); + if (!fde_entry) + return false; + + range = AddressRange(fde_entry->base, fde_entry->size, m_objfile.GetSectionList()); + return true; +} + +bool +DWARFCallFrameInfo::GetFDEEntryByFileAddress (addr_t file_addr, FDEEntryMap::Entry &fde_entry) +{ + if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) + return false; + + GetFDEIndex(); + + if (m_fde_index.IsEmpty()) + return false; + + FDEEntryMap::Entry *fde = m_fde_index.FindEntryThatContains (file_addr); + + if (fde == NULL) + return false; + + fde_entry = *fde; + return true; +} + +void +DWARFCallFrameInfo::GetFunctionAddressAndSizeVector (FunctionAddressAndSizeVector &function_info) +{ + GetFDEIndex(); + const size_t count = m_fde_index.GetSize(); + function_info.Clear(); + if (count > 0) + function_info.Reserve(count); + for (size_t i = 0; i < count; ++i) + { + const FDEEntryMap::Entry *func_offset_data_entry = m_fde_index.GetEntryAtIndex (i); + if (func_offset_data_entry) + { + FunctionAddressAndSizeVector::Entry function_offset_entry (func_offset_data_entry->base, func_offset_data_entry->size); + function_info.Append (function_offset_entry); + } + } +} + +const DWARFCallFrameInfo::CIE* +DWARFCallFrameInfo::GetCIE(dw_offset_t cie_offset) +{ + cie_map_t::iterator pos = m_cie_map.find(cie_offset); + + if (pos != m_cie_map.end()) + { + // Parse and cache the CIE + if (pos->second.get() == NULL) + pos->second = ParseCIE (cie_offset); + + return pos->second.get(); + } + return NULL; +} + +DWARFCallFrameInfo::CIESP +DWARFCallFrameInfo::ParseCIE (const dw_offset_t cie_offset) +{ + CIESP cie_sp(new CIE(cie_offset)); + lldb::offset_t offset = cie_offset; + if (m_cfi_data_initialized == false) + GetCFIData(); + const uint32_t length = m_cfi_data.GetU32(&offset); + const dw_offset_t cie_id = m_cfi_data.GetU32(&offset); + const dw_offset_t end_offset = cie_offset + length + 4; + if (length > 0 && ((!m_is_eh_frame && cie_id == UINT32_MAX) || (m_is_eh_frame && cie_id == 0ul))) + { + size_t i; + // cie.offset = cie_offset; + // cie.length = length; + // cie.cieID = cieID; + cie_sp->ptr_encoding = DW_EH_PE_absptr; // default + cie_sp->version = m_cfi_data.GetU8(&offset); + + for (i=0; i<CFI_AUG_MAX_SIZE; ++i) + { + cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset); + if (cie_sp->augmentation[i] == '\0') + { + // Zero out remaining bytes in augmentation string + for (size_t j = i+1; j<CFI_AUG_MAX_SIZE; ++j) + cie_sp->augmentation[j] = '\0'; + + break; + } + } + + if (i == CFI_AUG_MAX_SIZE && cie_sp->augmentation[CFI_AUG_MAX_SIZE-1] != '\0') + { + Host::SystemLog (Host::eSystemLogError, "CIE parse error: CIE augmentation string was too large for the fixed sized buffer of %d bytes.\n", CFI_AUG_MAX_SIZE); + return cie_sp; + } + cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset); + cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset); + cie_sp->return_addr_reg_num = m_cfi_data.GetU8(&offset); + + if (cie_sp->augmentation[0]) + { + // Get the length of the eh_frame augmentation data + // which starts with a ULEB128 length in bytes + const size_t aug_data_len = (size_t)m_cfi_data.GetULEB128(&offset); + const size_t aug_data_end = offset + aug_data_len; + const size_t aug_str_len = strlen(cie_sp->augmentation); + // A 'z' may be present as the first character of the string. + // If present, the Augmentation Data field shall be present. + // The contents of the Augmentation Data shall be intepreted + // according to other characters in the Augmentation String. + if (cie_sp->augmentation[0] == 'z') + { + // Extract the Augmentation Data + size_t aug_str_idx = 0; + for (aug_str_idx = 1; aug_str_idx < aug_str_len; aug_str_idx++) + { + char aug = cie_sp->augmentation[aug_str_idx]; + switch (aug) + { + case 'L': + // Indicates the presence of one argument in the + // Augmentation Data of the CIE, and a corresponding + // argument in the Augmentation Data of the FDE. The + // argument in the Augmentation Data of the CIE is + // 1-byte and represents the pointer encoding used + // for the argument in the Augmentation Data of the + // FDE, which is the address of a language-specific + // data area (LSDA). The size of the LSDA pointer is + // specified by the pointer encoding used. + m_cfi_data.GetU8(&offset); + break; + + case 'P': + // Indicates the presence of two arguments in the + // Augmentation Data of the cie_sp-> The first argument + // is 1-byte and represents the pointer encoding + // used for the second argument, which is the + // address of a personality routine handler. The + // size of the personality routine pointer is + // specified by the pointer encoding used. + { + uint8_t arg_ptr_encoding = m_cfi_data.GetU8(&offset); + m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); + } + break; + + case 'R': + // A 'R' may be present at any position after the + // first character of the string. The Augmentation + // Data shall include a 1 byte argument that + // represents the pointer encoding for the address + // pointers used in the FDE. + // Example: 0x1B == DW_EH_PE_pcrel | DW_EH_PE_sdata4 + cie_sp->ptr_encoding = m_cfi_data.GetU8(&offset); + break; + } + } + } + else if (strcmp(cie_sp->augmentation, "eh") == 0) + { + // If the Augmentation string has the value "eh", then + // the EH Data field shall be present + } + + // Set the offset to be the end of the augmentation data just in case + // we didn't understand any of the data. + offset = (uint32_t)aug_data_end; + } + + if (end_offset > offset) + { + cie_sp->inst_offset = offset; + cie_sp->inst_length = end_offset - offset; + } + while (offset < end_offset) + { + uint8_t inst = m_cfi_data.GetU8(&offset); + uint8_t primary_opcode = inst & 0xC0; + uint8_t extended_opcode = inst & 0x3F; + + if (extended_opcode == DW_CFA_def_cfa) + { + // Takes two unsigned LEB128 operands representing a register + // number and a (non-factored) offset. The required action + // is to define the current CFA rule to use the provided + // register and offset. + uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + int op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); + cie_sp->initial_row.SetCFARegister (reg_num); + cie_sp->initial_row.SetCFAOffset (op_offset); + continue; + } + if (primary_opcode == DW_CFA_offset) + { + // 0x80 - high 2 bits are 0x2, lower 6 bits are register. + // Takes two arguments: an unsigned LEB128 constant representing a + // factored offset and a register number. The required action is to + // change the rule for the register indicated by the register number + // to be an offset(N) rule with a value of + // (N = factored offset * data_align). + uint32_t reg_num = extended_opcode; + int op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * cie_sp->data_align; + UnwindPlan::Row::RegisterLocation reg_location; + reg_location.SetAtCFAPlusOffset(op_offset); + cie_sp->initial_row.SetRegisterInfo (reg_num, reg_location); + continue; + } + if (extended_opcode == DW_CFA_nop) + { + continue; + } + break; // Stop if we hit an unrecognized opcode + } + } + + return cie_sp; +} + +void +DWARFCallFrameInfo::GetCFIData() +{ + if (m_cfi_data_initialized == false) + { + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log) + m_objfile.GetModule()->LogMessage(log, "Reading EH frame info"); + m_objfile.ReadSectionData (m_section_sp.get(), m_cfi_data); + m_cfi_data_initialized = true; + } +} +// Scan through the eh_frame or debug_frame section looking for FDEs and noting the start/end addresses +// of the functions and a pointer back to the function's FDE for later expansion. +// Internalize CIEs as we come across them. + +void +DWARFCallFrameInfo::GetFDEIndex () +{ + if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) + return; + + if (m_fde_index_initialized) + return; + + Mutex::Locker locker(m_fde_index_mutex); + + if (m_fde_index_initialized) // if two threads hit the locker + return; + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s - %s", __PRETTY_FUNCTION__, m_objfile.GetFileSpec().GetFilename().AsCString("")); + + lldb::offset_t offset = 0; + if (m_cfi_data_initialized == false) + GetCFIData(); + while (m_cfi_data.ValidOffsetForDataOfSize (offset, 8)) + { + const dw_offset_t current_entry = offset; + uint32_t len = m_cfi_data.GetU32 (&offset); + dw_offset_t next_entry = current_entry + len + 4; + dw_offset_t cie_id = m_cfi_data.GetU32 (&offset); + + if (cie_id == 0 || cie_id == UINT32_MAX) + { + m_cie_map[current_entry] = ParseCIE (current_entry); + offset = next_entry; + continue; + } + + const dw_offset_t cie_offset = current_entry + 4 - cie_id; + const CIE *cie = GetCIE (cie_offset); + if (cie) + { + const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); + const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; + const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; + + lldb::addr_t addr = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); + lldb::addr_t length = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); + FDEEntryMap::Entry fde (addr, length, current_entry); + m_fde_index.Append(fde); + } + else + { + Host::SystemLog (Host::eSystemLogError, + "error: unable to find CIE at 0x%8.8x for cie_id = 0x%8.8x for entry at 0x%8.8x.\n", + cie_offset, + cie_id, + current_entry); + } + offset = next_entry; + } + m_fde_index.Sort(); + m_fde_index_initialized = true; +} + +bool +DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr, UnwindPlan& unwind_plan) +{ + lldb::offset_t offset = dwarf_offset; + lldb::offset_t current_entry = offset; + + if (m_section_sp.get() == NULL || m_section_sp->IsEncrypted()) + return false; + + if (m_cfi_data_initialized == false) + GetCFIData(); + + uint32_t length = m_cfi_data.GetU32 (&offset); + dw_offset_t cie_offset = m_cfi_data.GetU32 (&offset); + + assert (cie_offset != 0 && cie_offset != UINT32_MAX); + + // Translate the CIE_id from the eh_frame format, which + // is relative to the FDE offset, into a __eh_frame section + // offset + if (m_is_eh_frame) + { + unwind_plan.SetSourceName ("eh_frame CFI"); + cie_offset = current_entry + 4 - cie_offset; + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + } + else + { + unwind_plan.SetSourceName ("DWARF CFI"); + // In theory the debug_frame info should be valid at all call sites + // ("asynchronous unwind info" as it is sometimes called) but in practice + // gcc et al all emit call frame info for the prologue and call sites, but + // not for the epilogue or all the other locations during the function reliably. + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + } + unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); + + const CIE *cie = GetCIE (cie_offset); + assert (cie != NULL); + + const dw_offset_t end_offset = current_entry + length + 4; + + const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); + const lldb::addr_t text_addr = LLDB_INVALID_ADDRESS; + const lldb::addr_t data_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t range_base = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding, pc_rel_addr, text_addr, data_addr); + lldb::addr_t range_len = m_cfi_data.GetGNUEHPointer(&offset, cie->ptr_encoding & DW_EH_PE_MASK_ENCODING, pc_rel_addr, text_addr, data_addr); + AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList()); + range.SetByteSize (range_len); + + if (cie->augmentation[0] == 'z') + { + uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + offset += aug_data_len; + } + + uint32_t reg_num = 0; + int32_t op_offset = 0; + uint32_t code_align = cie->code_align; + int32_t data_align = cie->data_align; + + unwind_plan.SetPlanValidAddressRange (range); + UnwindPlan::Row *cie_initial_row = new UnwindPlan::Row; + *cie_initial_row = cie->initial_row; + UnwindPlan::RowSP row(cie_initial_row); + + unwind_plan.SetRegisterKind (m_reg_kind); + unwind_plan.SetReturnAddressRegister (cie->return_addr_reg_num); + + UnwindPlan::Row::RegisterLocation reg_location; + while (m_cfi_data.ValidOffset(offset) && offset < end_offset) + { + uint8_t inst = m_cfi_data.GetU8(&offset); + uint8_t primary_opcode = inst & 0xC0; + uint8_t extended_opcode = inst & 0x3F; + + if (primary_opcode) + { + switch (primary_opcode) + { + case DW_CFA_advance_loc : // (Row Creation Instruction) + { // 0x40 - high 2 bits are 0x1, lower 6 bits are delta + // takes a single argument that represents a constant delta. The + // required action is to create a new table row with a location + // value that is computed by taking the current entry's location + // value and adding (delta * code_align). All other + // values in the new row are initially identical to the current row. + unwind_plan.AppendRow(row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + row->SlideOffset(extended_opcode * code_align); + } + break; + + case DW_CFA_offset : + { // 0x80 - high 2 bits are 0x2, lower 6 bits are register + // takes two arguments: an unsigned LEB128 constant representing a + // factored offset and a register number. The required action is to + // change the rule for the register indicated by the register number + // to be an offset(N) rule with a value of + // (N = factored offset * data_align). + reg_num = extended_opcode; + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_restore : + { // 0xC0 - high 2 bits are 0x3, lower 6 bits are register + // takes a single argument that represents a register number. The + // required action is to change the rule for the indicated register + // to the rule assigned it by the initial_instructions in the CIE. + reg_num = extended_opcode; + // We only keep enough register locations around to + // unwind what is in our thread, and these are organized + // by the register index in that state, so we need to convert our + // GCC register number from the EH frame info, to a register index + + if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) + row->SetRegisterInfo (reg_num, reg_location); + } + break; + } + } + else + { + switch (extended_opcode) + { + case DW_CFA_nop : // 0x0 + break; + + case DW_CFA_set_loc : // 0x1 (Row Creation Instruction) + { + // DW_CFA_set_loc takes a single argument that represents an address. + // The required action is to create a new table row using the + // specified address as the location. All other values in the new row + // are initially identical to the current row. The new location value + // should always be greater than the current one. + unwind_plan.AppendRow(row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + row->SetOffset(m_cfi_data.GetPointer(&offset) - startaddr.GetFileAddress()); + } + break; + + case DW_CFA_advance_loc1 : // 0x2 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + unwind_plan.AppendRow(row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + row->SlideOffset (m_cfi_data.GetU8(&offset) * code_align); + } + break; + + case DW_CFA_advance_loc2 : // 0x3 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + unwind_plan.AppendRow(row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + row->SlideOffset (m_cfi_data.GetU16(&offset) * code_align); + } + break; + + case DW_CFA_advance_loc4 : // 0x4 (Row Creation Instruction) + { + // takes a single uword argument that represents a constant delta. + // This instruction is identical to DW_CFA_advance_loc except for the + // encoding and size of the delta argument. + unwind_plan.AppendRow(row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + row->SlideOffset (m_cfi_data.GetU32(&offset) * code_align); + } + break; + + case DW_CFA_offset_extended : // 0x5 + { + // takes two unsigned LEB128 arguments representing a register number + // and a factored offset. This instruction is identical to DW_CFA_offset + // except for the encoding and size of the register argument. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_restore_extended : // 0x6 + { + // takes a single unsigned LEB128 argument that represents a register + // number. This instruction is identical to DW_CFA_restore except for + // the encoding and size of the register argument. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + if (unwind_plan.IsValidRowIndex(0) && unwind_plan.GetRowAtIndex(0)->GetRegisterInfo(reg_num, reg_location)) + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_undefined : // 0x7 + { + // takes a single unsigned LEB128 argument that represents a register + // number. The required action is to set the rule for the specified + // register to undefined. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetUndefined(); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_same_value : // 0x8 + { + // takes a single unsigned LEB128 argument that represents a register + // number. The required action is to set the rule for the specified + // register to same value. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetSame(); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_register : // 0x9 + { + // takes two unsigned LEB128 arguments representing register numbers. + // The required action is to set the rule for the first register to be + // the second register. + + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t other_reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + reg_location.SetInRegister(other_reg_num); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_remember_state : // 0xA + { + // These instructions define a stack of information. Encountering the + // DW_CFA_remember_state instruction means to save the rules for every + // register on the current row on the stack. Encountering the + // DW_CFA_restore_state instruction means to pop the set of rules off + // the stack and place them in the current row. (This operation is + // useful for compilers that move epilogue code into the body of a + // function.) + unwind_plan.AppendRow (row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset (newrow); + } + break; + + case DW_CFA_restore_state : // 0xB + // These instructions define a stack of information. Encountering the + // DW_CFA_remember_state instruction means to save the rules for every + // register on the current row on the stack. Encountering the + // DW_CFA_restore_state instruction means to pop the set of rules off + // the stack and place them in the current row. (This operation is + // useful for compilers that move epilogue code into the body of a + // function.) + { + row = unwind_plan.GetRowAtIndex(unwind_plan.GetRowCount() - 1); + } + break; + + case DW_CFA_def_cfa : // 0xC (CFA Definition Instruction) + { + // Takes two unsigned LEB128 operands representing a register + // number and a (non-factored) offset. The required action + // is to define the current CFA rule to use the provided + // register and offset. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); + row->SetCFARegister (reg_num); + row->SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_register : // 0xD (CFA Definition Instruction) + { + // takes a single unsigned LEB128 argument representing a register + // number. The required action is to define the current CFA rule to + // use the provided register (but to keep the old offset). + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + row->SetCFARegister (reg_num); + } + break; + + case DW_CFA_def_cfa_offset : // 0xE (CFA Definition Instruction) + { + // Takes a single unsigned LEB128 operand representing a + // (non-factored) offset. The required action is to define + // the current CFA rule to use the provided offset (but + // to keep the old register). + op_offset = (int32_t)m_cfi_data.GetULEB128(&offset); + row->SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_expression : // 0xF (CFA Definition Instruction) + { + size_t block_len = (size_t)m_cfi_data.GetULEB128(&offset); + offset += (uint32_t)block_len; + } + break; + + case DW_CFA_expression : // 0x10 + { + // Takes two operands: an unsigned LEB128 value representing + // a register number, and a DW_FORM_block value representing a DWARF + // expression. The required action is to change the rule for the + // register indicated by the register number to be an expression(E) + // rule where E is the DWARF expression. That is, the DWARF + // expression computes the address. The value of the CFA is + // pushed on the DWARF evaluation stack prior to execution of + // the DWARF expression. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + const uint8_t *block_data = (uint8_t *)m_cfi_data.GetData(&offset, block_len); + + reg_location.SetAtDWARFExpression(block_data, block_len); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_offset_extended_sf : // 0x11 + { + // takes two operands: an unsigned LEB128 value representing a + // register number and a signed LEB128 factored offset. This + // instruction is identical to DW_CFA_offset_extended except + //that the second operand is signed and factored. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + reg_location.SetAtCFAPlusOffset(op_offset); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_def_cfa_sf : // 0x12 (CFA Definition Instruction) + { + // Takes two operands: an unsigned LEB128 value representing + // a register number and a signed LEB128 factored offset. + // This instruction is identical to DW_CFA_def_cfa except + // that the second operand is signed and factored. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + row->SetCFARegister (reg_num); + row->SetCFAOffset (op_offset); + } + break; + + case DW_CFA_def_cfa_offset_sf : // 0x13 (CFA Definition Instruction) + { + // takes a signed LEB128 operand representing a factored + // offset. This instruction is identical to DW_CFA_def_cfa_offset + // except that the operand is signed and factored. + op_offset = (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + row->SetCFAOffset (op_offset); + } + break; + + case DW_CFA_val_expression : // 0x16 + { + // takes two operands: an unsigned LEB128 value representing a register + // number, and a DW_FORM_block value representing a DWARF expression. + // The required action is to change the rule for the register indicated + // by the register number to be a val_expression(E) rule where E is the + // DWARF expression. That is, the DWARF expression computes the value of + // the given register. The value of the CFA is pushed on the DWARF + // evaluation stack prior to execution of the DWARF expression. + reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + uint32_t block_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + const uint8_t* block_data = (uint8_t*)m_cfi_data.GetData(&offset, block_len); +//#if defined(__i386__) || defined(__x86_64__) +// // The EH frame info for EIP and RIP contains code that looks for traps to +// // be a specific type and increments the PC. +// // For i386: +// // DW_CFA_val_expression where: +// // eip = DW_OP_breg6(+28), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x34), +// // DW_OP_deref, DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref, +// // DW_OP_dup, DW_OP_lit3, DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, +// // DW_OP_and, DW_OP_plus +// // This basically does a: +// // eip = ucontenxt.mcontext32->gpr.eip; +// // if (ucontenxt.mcontext32->exc.trapno != 3 && ucontenxt.mcontext32->exc.trapno != 4) +// // eip++; +// // +// // For x86_64: +// // DW_CFA_val_expression where: +// // rip = DW_OP_breg3(+48), DW_OP_deref, DW_OP_dup, DW_OP_plus_uconst(0x90), DW_OP_deref, +// // DW_OP_swap, DW_OP_plus_uconst(0), DW_OP_deref_size(4), DW_OP_dup, DW_OP_lit3, +// // DW_OP_ne, DW_OP_swap, DW_OP_lit4, DW_OP_ne, DW_OP_and, DW_OP_plus +// // This basically does a: +// // rip = ucontenxt.mcontext64->gpr.rip; +// // if (ucontenxt.mcontext64->exc.trapno != 3 && ucontenxt.mcontext64->exc.trapno != 4) +// // rip++; +// // The trap comparisons and increments are not needed as it hoses up the unwound PC which +// // is expected to point at least past the instruction that causes the fault/trap. So we +// // take it out by trimming the expression right at the first "DW_OP_swap" opcodes +// if (block_data != NULL && thread->GetPCRegNum(Thread::GCC) == reg_num) +// { +// if (thread->Is64Bit()) +// { +// if (block_len > 9 && block_data[8] == DW_OP_swap && block_data[9] == DW_OP_plus_uconst) +// block_len = 8; +// } +// else +// { +// if (block_len > 8 && block_data[7] == DW_OP_swap && block_data[8] == DW_OP_plus_uconst) +// block_len = 7; +// } +// } +//#endif + reg_location.SetIsDWARFExpression(block_data, block_len); + row->SetRegisterInfo (reg_num, reg_location); + } + break; + + case DW_CFA_val_offset : // 0x14 + case DW_CFA_val_offset_sf : // 0x15 + default: + break; + } + } + } + unwind_plan.AppendRow(row); + + return true; +} diff --git a/source/Symbol/Declaration.cpp b/source/Symbol/Declaration.cpp new file mode 100644 index 000000000000..3943f02c5474 --- /dev/null +++ b/source/Symbol/Declaration.cpp @@ -0,0 +1,117 @@ +//===-- Declaration.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/Declaration.h" +#include "lldb/Core/Stream.h" + +using namespace lldb_private; + +void +Declaration::Dump(Stream *s, bool show_fullpaths) const +{ + if (m_file) + { + *s << ", decl = "; + if (show_fullpaths) + *s << m_file; + else + *s << m_file.GetFilename(); + if (m_line > 0) + s->Printf(":%u", m_line); +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (m_column > 0) + s->Printf(":%u", m_column); +#endif + } + else + { + if (m_line > 0) + { + s->Printf(", line = %u", m_line); +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (m_column > 0) + s->Printf(":%u", m_column); +#endif + } +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + else if (m_column > 0) + s->Printf(", column = %u", m_column); +#endif + } +} + +bool +Declaration::DumpStopContext (Stream *s, bool show_fullpaths) const +{ + if (m_file) + { + if (show_fullpaths || s->GetVerbose()) + *s << m_file; + else + m_file.GetFilename().Dump(s); + + if (m_line > 0) + s->Printf(":%u", m_line); +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (m_column > 0) + s->Printf(":%u", m_column); +#endif + return true; + } + else if (m_line > 0) + { + s->Printf(" line %u", m_line); +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (m_column > 0) + s->Printf(":%u", m_column); +#endif + return true; + } + return false; +} + +size_t +Declaration::MemorySize() const +{ + return sizeof(Declaration); +} + +int +Declaration::Compare(const Declaration& a, const Declaration& b) +{ + int result = FileSpec::Compare(a.m_file, b.m_file, true); + if (result) + return result; + if (a.m_line < b.m_line) + return -1; + else if (a.m_line > b.m_line) + return 1; +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (a.m_column < b.m_column) + return -1; + else if (a.m_column > b.m_column) + return 1; +#endif + return 0; +} + +bool +lldb_private::operator == (const Declaration &lhs, const Declaration &rhs) +{ +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + if (lhs.GetColumn () == rhs.GetColumn ()) + if (lhs.GetLine () == rhs.GetLine ()) + return lhs.GetFile() == rhs.GetFile(); +#else + if (lhs.GetLine () == rhs.GetLine ()) + return lhs.GetFile() == rhs.GetFile(); +#endif + return false; +} + diff --git a/source/Symbol/FuncUnwinders.cpp b/source/Symbol/FuncUnwinders.cpp new file mode 100644 index 000000000000..3de06179ee07 --- /dev/null +++ b/source/Symbol/FuncUnwinders.cpp @@ -0,0 +1,247 @@ +//===-- FuncUnwinders.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Address.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Symbol/UnwindTable.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnwindAssembly.h" + +using namespace lldb; +using namespace lldb_private; + + +FuncUnwinders::FuncUnwinders +( + UnwindTable& unwind_table, + UnwindAssembly *assembly_profiler, + AddressRange range +) : + m_unwind_table(unwind_table), + m_assembly_profiler(assembly_profiler), + m_range(range), + m_mutex (Mutex::eMutexTypeNormal), + m_unwind_plan_call_site_sp (), + m_unwind_plan_non_call_site_sp (), + m_unwind_plan_fast_sp (), + m_unwind_plan_arch_default_sp (), + m_tried_unwind_at_call_site (false), + m_tried_unwind_at_non_call_site (false), + m_tried_unwind_fast (false), + m_tried_unwind_arch_default (false), + m_tried_unwind_arch_default_at_func_entry (false), + m_first_non_prologue_insn() +{ +} + +FuncUnwinders::~FuncUnwinders () +{ +} + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanAtCallSite (int current_offset) +{ + // Lock the mutex to ensure we can always give out the most appropriate + // information. We want to make sure if someone requests a call site unwind + // plan, that they get one and don't run into a race condition where one + // thread has started to create the unwind plan and has put it into + // m_unwind_plan_call_site_sp, and have another thread enter this function + // and return the partially filled in m_unwind_plan_call_site_sp pointer. + // We also want to make sure that we lock out other unwind plans from + // being accessed until this one is done creating itself in case someone + // had some code like: + // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) + // if (best_unwind_plan == NULL) + // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + Mutex::Locker locker (m_mutex); + if (m_tried_unwind_at_call_site == false && m_unwind_plan_call_site_sp.get() == NULL) + { + m_tried_unwind_at_call_site = true; + // We have cases (e.g. with _sigtramp on Mac OS X) where the hand-written eh_frame unwind info for a + // function does not cover the entire range of the function and so the FDE only lists a subset of the + // address range. If we try to look up the unwind info by the starting address of the function + // (i.e. m_range.GetBaseAddress()) we may not find the eh_frame FDE. We need to use the actual byte offset + // into the function when looking it up. + + if (m_range.GetBaseAddress().IsValid()) + { + Address current_pc (m_range.GetBaseAddress ()); + if (current_offset != -1) + current_pc.SetOffset (current_pc.GetOffset() + current_offset); + + DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); + if (eh_frame) + { + m_unwind_plan_call_site_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_call_site_sp)) + m_unwind_plan_call_site_sp.reset(); + } + } + } + return m_unwind_plan_call_site_sp; +} + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanAtNonCallSite (Thread& thread) +{ + // Lock the mutex to ensure we can always give out the most appropriate + // information. We want to make sure if someone requests an unwind + // plan, that they get one and don't run into a race condition where one + // thread has started to create the unwind plan and has put it into + // the unique pointer member variable, and have another thread enter this function + // and return the partially filled pointer contained in the unique pointer. + // We also want to make sure that we lock out other unwind plans from + // being accessed until this one is done creating itself in case someone + // had some code like: + // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) + // if (best_unwind_plan == NULL) + // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + Mutex::Locker locker (m_mutex); + if (m_tried_unwind_at_non_call_site == false && m_unwind_plan_non_call_site_sp.get() == NULL) + { + m_tried_unwind_at_non_call_site = true; + m_unwind_plan_non_call_site_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!m_assembly_profiler->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *m_unwind_plan_non_call_site_sp)) + m_unwind_plan_non_call_site_sp.reset(); + } + return m_unwind_plan_non_call_site_sp; +} + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread) +{ + // Lock the mutex to ensure we can always give out the most appropriate + // information. We want to make sure if someone requests an unwind + // plan, that they get one and don't run into a race condition where one + // thread has started to create the unwind plan and has put it into + // the unique pointer member variable, and have another thread enter this function + // and return the partially filled pointer contained in the unique pointer. + // We also want to make sure that we lock out other unwind plans from + // being accessed until this one is done creating itself in case someone + // had some code like: + // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) + // if (best_unwind_plan == NULL) + // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + Mutex::Locker locker (m_mutex); + if (m_tried_unwind_fast == false && m_unwind_plan_fast_sp.get() == NULL) + { + m_tried_unwind_fast = true; + m_unwind_plan_fast_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!m_assembly_profiler->GetFastUnwindPlan (m_range, thread, *m_unwind_plan_fast_sp)) + m_unwind_plan_fast_sp.reset(); + } + return m_unwind_plan_fast_sp; +} + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanArchitectureDefault (Thread& thread) +{ + // Lock the mutex to ensure we can always give out the most appropriate + // information. We want to make sure if someone requests an unwind + // plan, that they get one and don't run into a race condition where one + // thread has started to create the unwind plan and has put it into + // the unique pointer member variable, and have another thread enter this function + // and return the partially filled pointer contained in the unique pointer. + // We also want to make sure that we lock out other unwind plans from + // being accessed until this one is done creating itself in case someone + // had some code like: + // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) + // if (best_unwind_plan == NULL) + // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + Mutex::Locker locker (m_mutex); + if (m_tried_unwind_arch_default == false && m_unwind_plan_arch_default_sp.get() == NULL) + { + m_tried_unwind_arch_default = true; + Address current_pc; + ProcessSP process_sp (thread.CalculateProcess()); + if (process_sp) + { + ABI *abi = process_sp->GetABI().get(); + if (abi) + { + m_unwind_plan_arch_default_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (m_unwind_plan_arch_default_sp) + abi->CreateDefaultUnwindPlan(*m_unwind_plan_arch_default_sp); + } + } + } + + return m_unwind_plan_arch_default_sp; +} + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanArchitectureDefaultAtFunctionEntry (Thread& thread) +{ + // Lock the mutex to ensure we can always give out the most appropriate + // information. We want to make sure if someone requests an unwind + // plan, that they get one and don't run into a race condition where one + // thread has started to create the unwind plan and has put it into + // the unique pointer member variable, and have another thread enter this function + // and return the partially filled pointer contained in the unique pointer. + // We also want to make sure that we lock out other unwind plans from + // being accessed until this one is done creating itself in case someone + // had some code like: + // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) + // if (best_unwind_plan == NULL) + // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + Mutex::Locker locker (m_mutex); + if (m_tried_unwind_arch_default_at_func_entry == false && m_unwind_plan_arch_default_at_func_entry_sp.get() == NULL) + { + m_tried_unwind_arch_default_at_func_entry = true; + Address current_pc; + ProcessSP process_sp (thread.CalculateProcess()); + if (process_sp) + { + ABI *abi = process_sp->GetABI().get(); + if (abi) + { + m_unwind_plan_arch_default_at_func_entry_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (m_unwind_plan_arch_default_at_func_entry_sp) + abi->CreateFunctionEntryUnwindPlan(*m_unwind_plan_arch_default_at_func_entry_sp); + } + } + } + + return m_unwind_plan_arch_default_sp; +} + + +Address& +FuncUnwinders::GetFirstNonPrologueInsn (Target& target) +{ + if (m_first_non_prologue_insn.IsValid()) + return m_first_non_prologue_insn; + ExecutionContext exe_ctx (target.shared_from_this(), false); + m_assembly_profiler->FirstNonPrologueInsn (m_range, exe_ctx, m_first_non_prologue_insn); + return m_first_non_prologue_insn; +} + +const Address& +FuncUnwinders::GetFunctionStartAddress () const +{ + return m_range.GetBaseAddress(); +} + +void +FuncUnwinders::InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& thread) +{ + UnwindPlanSP arch_default = GetUnwindPlanArchitectureDefault (thread); + if (arch_default && m_tried_unwind_at_call_site) + { + m_unwind_plan_call_site_sp = arch_default; + } +} diff --git a/source/Symbol/Function.cpp b/source/Symbol/Function.cpp new file mode 100644 index 000000000000..31334a6df8d7 --- /dev/null +++ b/source/Symbol/Function.cpp @@ -0,0 +1,572 @@ +//===-- Function.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/Function.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "llvm/Support/Casting.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Basic function information is contained in the FunctionInfo class. +// It is designed to contain the name, linkage name, and declaration +// location. +//---------------------------------------------------------------------- +FunctionInfo::FunctionInfo (const char *name, const Declaration *decl_ptr) : + m_name(name), + m_declaration(decl_ptr) +{ +} + + +FunctionInfo::FunctionInfo (const ConstString& name, const Declaration *decl_ptr) : + m_name(name), + m_declaration(decl_ptr) +{ +} + + +FunctionInfo::~FunctionInfo() +{ +} + +void +FunctionInfo::Dump(Stream *s, bool show_fullpaths) const +{ + if (m_name) + *s << ", name = \"" << m_name << "\""; + m_declaration.Dump(s, show_fullpaths); +} + + +int +FunctionInfo::Compare(const FunctionInfo& a, const FunctionInfo& b) +{ + int result = ConstString::Compare(a.GetName(), b.GetName()); + if (result) + return result; + + return Declaration::Compare(a.m_declaration, b.m_declaration); +} + + +Declaration& +FunctionInfo::GetDeclaration() +{ + return m_declaration; +} + +const Declaration& +FunctionInfo::GetDeclaration() const +{ + return m_declaration; +} + +const ConstString& +FunctionInfo::GetName() const +{ + return m_name; +} + +size_t +FunctionInfo::MemorySize() const +{ + return m_name.MemorySize() + m_declaration.MemorySize(); +} + + +InlineFunctionInfo::InlineFunctionInfo +( + const char *name, + const char *mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr +) : + FunctionInfo(name, decl_ptr), + m_mangled(ConstString(mangled), true), + m_call_decl (call_decl_ptr) +{ +} + +InlineFunctionInfo::InlineFunctionInfo +( + const ConstString& name, + const Mangled &mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr +) : + FunctionInfo(name, decl_ptr), + m_mangled(mangled), + m_call_decl (call_decl_ptr) +{ +} + +InlineFunctionInfo::~InlineFunctionInfo() +{ +} + +int +InlineFunctionInfo::Compare(const InlineFunctionInfo& a, const InlineFunctionInfo& b) +{ + + int result = FunctionInfo::Compare(a, b); + if (result) + return result; + // only compare the mangled names if both have them + return Mangled::Compare(a.m_mangled, a.m_mangled); +} + +void +InlineFunctionInfo::Dump(Stream *s, bool show_fullpaths) const +{ + FunctionInfo::Dump(s, show_fullpaths); + if (m_mangled) + m_mangled.Dump(s); +} + +void +InlineFunctionInfo::DumpStopContext (Stream *s) const +{ +// s->Indent("[inlined] "); + s->Indent(); + if (m_mangled) + s->PutCString (m_mangled.GetName().AsCString()); + else + s->PutCString (m_name.AsCString()); +} + + +const ConstString & +InlineFunctionInfo::GetName () const +{ + if (m_mangled) + return m_mangled.GetName(); + return m_name; +} + + +Declaration & +InlineFunctionInfo::GetCallSite () +{ + return m_call_decl; +} + +const Declaration & +InlineFunctionInfo::GetCallSite () const +{ + return m_call_decl; +} + + +Mangled& +InlineFunctionInfo::GetMangled() +{ + return m_mangled; +} + +const Mangled& +InlineFunctionInfo::GetMangled() const +{ + return m_mangled; +} + +size_t +InlineFunctionInfo::MemorySize() const +{ + return FunctionInfo::MemorySize() + m_mangled.MemorySize(); +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +Function::Function +( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t type_uid, + const Mangled &mangled, + Type * type, + const AddressRange& range +) : + UserID (func_uid), + m_comp_unit (comp_unit), + m_type_uid (type_uid), + m_type (type), + m_mangled (mangled), + m_block (func_uid), + m_range (range), + m_frame_base (), + m_flags (), + m_prologue_byte_size (0) +{ + m_block.SetParentScope(this); + assert(comp_unit != NULL); +} + +Function::Function +( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t type_uid, + const char *mangled, + Type *type, + const AddressRange &range +) : + UserID (func_uid), + m_comp_unit (comp_unit), + m_type_uid (type_uid), + m_type (type), + m_mangled (ConstString(mangled), true), + m_block (func_uid), + m_range (range), + m_frame_base (), + m_flags (), + m_prologue_byte_size (0) +{ + m_block.SetParentScope(this); + assert(comp_unit != NULL); +} + + +Function::~Function() +{ +} + +void +Function::GetStartLineSourceInfo (FileSpec &source_file, uint32_t &line_no) +{ + line_no = 0; + source_file.Clear(); + + if (m_comp_unit == NULL) + return; + + if (m_type != NULL && m_type->GetDeclaration().GetLine() != 0) + { + source_file = m_type->GetDeclaration().GetFile(); + line_no = m_type->GetDeclaration().GetLine(); + } + else + { + LineTable *line_table = m_comp_unit->GetLineTable(); + if (line_table == NULL) + return; + + LineEntry line_entry; + if (line_table->FindLineEntryByAddress (GetAddressRange().GetBaseAddress(), line_entry, NULL)) + { + line_no = line_entry.line; + source_file = line_entry.file; + } + } +} + +void +Function::GetEndLineSourceInfo (FileSpec &source_file, uint32_t &line_no) +{ + line_no = 0; + source_file.Clear(); + + // The -1 is kind of cheesy, but I want to get the last line entry for the given function, not the + // first entry of the next. + Address scratch_addr(GetAddressRange().GetBaseAddress()); + scratch_addr.SetOffset (scratch_addr.GetOffset() + GetAddressRange().GetByteSize() - 1); + + LineTable *line_table = m_comp_unit->GetLineTable(); + if (line_table == NULL) + return; + + LineEntry line_entry; + if (line_table->FindLineEntryByAddress (scratch_addr, line_entry, NULL)) + { + line_no = line_entry.line; + source_file = line_entry.file; + } +} + +Block & +Function::GetBlock (bool can_create) +{ + if (!m_block.BlockInfoHasBeenParsed() && can_create) + { + SymbolContext sc; + CalculateSymbolContext(&sc); + if (sc.module_sp) + { + sc.module_sp->GetSymbolVendor()->ParseFunctionBlocks(sc); + } + else + { + Host::SystemLog (Host::eSystemLogError, + "error: unable to find module shared pointer for function '%s' in %s\n", + GetName().GetCString(), + m_comp_unit->GetPath().c_str()); + } + m_block.SetBlockInfoHasBeenParsed (true, true); + } + return m_block; +} + +CompileUnit* +Function::GetCompileUnit() +{ + return m_comp_unit; +} + +const CompileUnit* +Function::GetCompileUnit() const +{ + return m_comp_unit; +} + + +void +Function::GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) +{ + Type* func_type = GetType(); + const char *name = func_type ? func_type->GetName().AsCString() : "<unknown>"; + + *s << "id = " << (const UserID&)*this << ", name = \"" << name << "\", range = "; + + Address::DumpStyle fallback_style; + if (level == eDescriptionLevelVerbose) + fallback_style = Address::DumpStyleModuleWithFileAddress; + else + fallback_style = Address::DumpStyleFileAddress; + GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress, fallback_style); +} + +void +Function::Dump(Stream *s, bool show_context) const +{ + s->Printf("%p: ", this); + s->Indent(); + *s << "Function" << (const UserID&)*this; + + m_mangled.Dump(s); + + if (m_type) + { + s->Printf(", type = %p", m_type); + } + else if (m_type_uid != LLDB_INVALID_UID) + { + s->Printf(", type_uid = 0x%8.8" PRIx64, m_type_uid); + } + + s->EOL(); + // Dump the root object + if (m_block.BlockInfoHasBeenParsed ()) + m_block.Dump(s, m_range.GetBaseAddress().GetFileAddress(), INT_MAX, show_context); +} + + +void +Function::CalculateSymbolContext(SymbolContext* sc) +{ + sc->function = this; + m_comp_unit->CalculateSymbolContext(sc); +} + +ModuleSP +Function::CalculateSymbolContextModule () +{ + SectionSP section_sp (m_range.GetBaseAddress().GetSection()); + if (section_sp) + return section_sp->GetModule(); + + return this->GetCompileUnit()->GetModule(); +} + +CompileUnit * +Function::CalculateSymbolContextCompileUnit () +{ + return this->GetCompileUnit(); +} + +Function * +Function::CalculateSymbolContextFunction () +{ + return this; +} + +//Symbol * +//Function::CalculateSymbolContextSymbol () +//{ +// return // TODO: find the symbol for the function??? +//} + + +void +Function::DumpSymbolContext(Stream *s) +{ + m_comp_unit->DumpSymbolContext(s); + s->Printf(", Function{0x%8.8" PRIx64 "}", GetID()); +} + +size_t +Function::MemorySize () const +{ + size_t mem_size = sizeof(Function) + m_block.MemorySize(); + return mem_size; +} + +clang::DeclContext * +Function::GetClangDeclContext() +{ + SymbolContext sc; + + CalculateSymbolContext (&sc); + + if (!sc.module_sp) + return NULL; + + SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor(); + + if (!sym_vendor) + return NULL; + + SymbolFile *sym_file = sym_vendor->GetSymbolFile(); + + if (!sym_file) + return NULL; + + return sym_file->GetClangDeclContextForTypeUID (sc, m_uid); +} + +Type* +Function::GetType() +{ + if (m_type == NULL) + { + SymbolContext sc; + + CalculateSymbolContext (&sc); + + if (!sc.module_sp) + return NULL; + + SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor(); + + if (sym_vendor == NULL) + return NULL; + + SymbolFile *sym_file = sym_vendor->GetSymbolFile(); + + if (sym_file == NULL) + return NULL; + + m_type = sym_file->ResolveTypeUID(m_type_uid); + } + return m_type; +} + +const Type* +Function::GetType() const +{ + return m_type; +} + +ClangASTType +Function::GetClangType() +{ + Type *function_type = GetType(); + if (function_type) + return function_type->GetClangFullType(); + return ClangASTType(); +} + +uint32_t +Function::GetPrologueByteSize () +{ + if (m_prologue_byte_size == 0 && m_flags.IsClear(flagsCalculatedPrologueSize)) + { + m_flags.Set(flagsCalculatedPrologueSize); + LineTable* line_table = m_comp_unit->GetLineTable (); + if (line_table) + { + LineEntry first_line_entry; + uint32_t first_line_entry_idx = UINT32_MAX; + if (line_table->FindLineEntryByAddress(GetAddressRange().GetBaseAddress(), first_line_entry, &first_line_entry_idx)) + { + // Make sure the first line entry isn't already the end of the prologue + addr_t prologue_end_file_addr = LLDB_INVALID_ADDRESS; + if (first_line_entry.is_prologue_end) + { + prologue_end_file_addr = first_line_entry.range.GetBaseAddress().GetFileAddress(); + } + else + { + // Check the first few instructions and look for one that has + // is_prologue_end set to true. + const uint32_t last_line_entry_idx = first_line_entry_idx + 6; + for (uint32_t idx = first_line_entry_idx + 1; idx < last_line_entry_idx; ++idx) + { + LineEntry line_entry; + if (line_table->GetLineEntryAtIndex (idx, line_entry)) + { + if (line_entry.is_prologue_end) + { + prologue_end_file_addr = line_entry.range.GetBaseAddress().GetFileAddress(); + break; + } + } + } + } + + // If we didn't find the end of the prologue in the line tables, + // then just use the end address of the first line table entry + if (prologue_end_file_addr == LLDB_INVALID_ADDRESS) + { + // Check the first few instructions and look for one that has + // a line number that's different than the first entry. + const uint32_t last_line_entry_idx = first_line_entry_idx + 6; + for (uint32_t idx = first_line_entry_idx + 1; idx < last_line_entry_idx; ++idx) + { + LineEntry line_entry; + if (line_table->GetLineEntryAtIndex (idx, line_entry)) + { + if (line_entry.line != first_line_entry.line) + { + prologue_end_file_addr = line_entry.range.GetBaseAddress().GetFileAddress(); + break; + } + } + } + + if (prologue_end_file_addr == LLDB_INVALID_ADDRESS) + { + prologue_end_file_addr = first_line_entry.range.GetBaseAddress().GetFileAddress() + first_line_entry.range.GetByteSize(); + } + } + const addr_t func_start_file_addr = m_range.GetBaseAddress().GetFileAddress(); + const addr_t func_end_file_addr = func_start_file_addr + m_range.GetByteSize(); + + // Verify that this prologue end file address in the function's + // address range just to be sure + if (func_start_file_addr < prologue_end_file_addr && prologue_end_file_addr < func_end_file_addr) + { + m_prologue_byte_size = prologue_end_file_addr - func_start_file_addr; + } + } + } + } + return m_prologue_byte_size; +} + + + diff --git a/source/Symbol/LineEntry.cpp b/source/Symbol/LineEntry.cpp new file mode 100644 index 000000000000..10dc552ea01a --- /dev/null +++ b/source/Symbol/LineEntry.cpp @@ -0,0 +1,246 @@ +//===-- LineEntry.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +LineEntry::LineEntry() : + range(), + file(), + line(0), + column(0), + is_start_of_statement(0), + is_start_of_basic_block(0), + is_prologue_end(0), + is_epilogue_begin(0), + is_terminal_entry(0) +{ +} + +LineEntry::LineEntry +( + const lldb::SectionSP §ion_sp, + lldb::addr_t section_offset, + lldb::addr_t byte_size, + const FileSpec &_file, + uint32_t _line, + uint16_t _column, + bool _is_start_of_statement, + bool _is_start_of_basic_block, + bool _is_prologue_end, + bool _is_epilogue_begin, + bool _is_terminal_entry +) : + range(section_sp, section_offset, byte_size), + file(_file), + line(_line), + column(_column), + is_start_of_statement(_is_start_of_statement), + is_start_of_basic_block(_is_start_of_basic_block), + is_prologue_end(_is_prologue_end), + is_epilogue_begin(_is_epilogue_begin), + is_terminal_entry(_is_terminal_entry) +{ +} + +void +LineEntry::Clear() +{ + range.Clear(); + file.Clear(); + line = 0; + column = 0; + is_start_of_statement = 0; + is_start_of_basic_block = 0; + is_prologue_end = 0; + is_epilogue_begin = 0; + is_terminal_entry = 0; +} + + +bool +LineEntry::IsValid() const +{ + return range.GetBaseAddress().IsValid() && line != 0; +} + +bool +LineEntry::DumpStopContext(Stream *s, bool show_fullpaths) const +{ + bool result = false; + if (file) + { + if (show_fullpaths) + file.Dump (s); + else + file.GetFilename().Dump (s); + + if (line) + s->PutChar(':'); + result = true; + } + if (line) + s->Printf ("%u", line); + else + result = false; + + return result; +} + +bool +LineEntry::Dump +( + Stream *s, + Target *target, + bool show_file, + Address::DumpStyle style, + Address::DumpStyle fallback_style, + bool show_range +) const +{ + if (show_range) + { + // Show address range + if (!range.Dump(s, target, style, fallback_style)) + return false; + } + else + { + // Show address only + if (!range.GetBaseAddress().Dump(s, + target, + style, + fallback_style)) + return false; + } + if (show_file) + *s << ", file = " << file; + if (line) + s->Printf(", line = %u", line); + if (column) + s->Printf(", column = %u", column); + if (is_start_of_statement) + *s << ", is_start_of_statement = TRUE"; + + if (is_start_of_basic_block) + *s << ", is_start_of_basic_block = TRUE"; + + if (is_prologue_end) + *s << ", is_prologue_end = TRUE"; + + if (is_epilogue_begin) + *s << ", is_epilogue_begin = TRUE"; + + if (is_terminal_entry) + *s << ", is_terminal_entry = TRUE"; + return true; +} + +bool +LineEntry::GetDescription (Stream *s, lldb::DescriptionLevel level, CompileUnit* cu, Target *target, bool show_address_only) const +{ + + if (level == lldb::eDescriptionLevelBrief || level == lldb::eDescriptionLevelFull) + { + if (show_address_only) + { + range.GetBaseAddress().Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + } + else + { + range.Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + } + + *s << ": " << file; + + if (line) + { + s->Printf(":%u", line); + if (column) + s->Printf(":%u", column); + } + + + if (level == lldb::eDescriptionLevelFull) + { + if (is_start_of_statement) + *s << ", is_start_of_statement = TRUE"; + + if (is_start_of_basic_block) + *s << ", is_start_of_basic_block = TRUE"; + + if (is_prologue_end) + *s << ", is_prologue_end = TRUE"; + + if (is_epilogue_begin) + *s << ", is_epilogue_begin = TRUE"; + + if (is_terminal_entry) + *s << ", is_terminal_entry = TRUE"; + } + else + { + if (is_terminal_entry) + s->EOL(); + } + } + else + { + return Dump (s, target, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true); + } + return true; +} + + +bool +lldb_private::operator< (const LineEntry& a, const LineEntry& b) +{ + return LineEntry::Compare (a, b) < 0; +} + +int +LineEntry::Compare (const LineEntry& a, const LineEntry& b) +{ + int result = Address::CompareFileAddress (a.range.GetBaseAddress(), b.range.GetBaseAddress()); + if (result != 0) + return result; + + const lldb::addr_t a_byte_size = a.range.GetByteSize(); + const lldb::addr_t b_byte_size = b.range.GetByteSize(); + + if (a_byte_size < b_byte_size) + return -1; + if (a_byte_size > b_byte_size) + return +1; + + // Check for an end sequence entry mismatch after we have determined + // that the address values are equal. If one of the items is an end + // sequence, we don't care about the line, file, or column info. + if (a.is_terminal_entry > b.is_terminal_entry) + return -1; + if (a.is_terminal_entry < b.is_terminal_entry) + return +1; + + if (a.line < b.line) + return -1; + if (a.line > b.line) + return +1; + + if (a.column < b.column) + return -1; + if (a.column > b.column) + return +1; + + return FileSpec::Compare (a.file, b.file, true); +} + diff --git a/source/Symbol/LineTable.cpp b/source/Symbol/LineTable.cpp new file mode 100644 index 000000000000..a4aa35ddb318 --- /dev/null +++ b/source/Symbol/LineTable.cpp @@ -0,0 +1,588 @@ +//===-- LineTable.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// LineTable constructor +//---------------------------------------------------------------------- +LineTable::LineTable(CompileUnit* comp_unit) : + m_comp_unit(comp_unit), + m_entries() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +LineTable::~LineTable() +{ +} + +void +LineTable::InsertLineEntry +( + lldb::addr_t file_addr, + uint32_t line, + uint16_t column, + uint16_t file_idx, + bool is_start_of_statement, + bool is_start_of_basic_block, + bool is_prologue_end, + bool is_epilogue_begin, + bool is_terminal_entry +) +{ + Entry entry(file_addr, line, column, file_idx, is_start_of_statement, is_start_of_basic_block, is_prologue_end, is_epilogue_begin, is_terminal_entry); + + entry_collection::iterator begin_pos = m_entries.begin(); + entry_collection::iterator end_pos = m_entries.end(); + LineTable::Entry::LessThanBinaryPredicate less_than_bp(this); + entry_collection::iterator pos = upper_bound(begin_pos, end_pos, entry, less_than_bp); + +// Stream s(stdout); +// s << "\n\nBefore:\n"; +// Dump (&s, Address::DumpStyleFileAddress); + m_entries.insert(pos, entry); +// s << "After:\n"; +// Dump (&s, Address::DumpStyleFileAddress); +} + +LineSequence::LineSequence() +{ +} + +void +LineTable::LineSequenceImpl::Clear() +{ + m_entries.clear(); +} + +LineSequence* LineTable::CreateLineSequenceContainer () +{ + return new LineTable::LineSequenceImpl(); +} + +void +LineTable::AppendLineEntryToSequence +( + LineSequence* sequence, + lldb::addr_t file_addr, + uint32_t line, + uint16_t column, + uint16_t file_idx, + bool is_start_of_statement, + bool is_start_of_basic_block, + bool is_prologue_end, + bool is_epilogue_begin, + bool is_terminal_entry +) +{ + assert(sequence != NULL); + LineSequenceImpl* seq = reinterpret_cast<LineSequenceImpl*>(sequence); + Entry entry(file_addr, line, column, file_idx, is_start_of_statement, is_start_of_basic_block, is_prologue_end, is_epilogue_begin, is_terminal_entry); + seq->m_entries.push_back (entry); +} + +void +LineTable::InsertSequence (LineSequence* sequence) +{ + assert(sequence != NULL); + LineSequenceImpl* seq = reinterpret_cast<LineSequenceImpl*>(sequence); + if (seq->m_entries.empty()) + return; + Entry& entry = seq->m_entries.front(); + + // If the first entry address in this sequence is greater than or equal to + // the address of the last item in our entry collection, just append. + if (m_entries.empty() || !Entry::EntryAddressLessThan(entry, m_entries.back())) + { + m_entries.insert(m_entries.end(), + seq->m_entries.begin(), + seq->m_entries.end()); + return; + } + + // Otherwise, find where this belongs in the collection + entry_collection::iterator begin_pos = m_entries.begin(); + entry_collection::iterator end_pos = m_entries.end(); + LineTable::Entry::LessThanBinaryPredicate less_than_bp(this); + entry_collection::iterator pos = upper_bound(begin_pos, end_pos, entry, less_than_bp); +#ifdef LLDB_CONFIGURATION_DEBUG + // If we aren't inserting at the beginning, the previous entry should + // terminate a sequence. + if (pos != begin_pos) + { + entry_collection::iterator prev_pos = pos - 1; + assert(prev_pos->is_terminal_entry); + } +#endif + m_entries.insert(pos, seq->m_entries.begin(), seq->m_entries.end()); +} + +//---------------------------------------------------------------------- +LineTable::Entry::LessThanBinaryPredicate::LessThanBinaryPredicate(LineTable *line_table) : + m_line_table (line_table) +{ +} + +bool +LineTable::Entry::LessThanBinaryPredicate::operator() (const LineTable::Entry& a, const LineTable::Entry& b) const +{ + #define LT_COMPARE(a,b) if (a != b) return a < b + LT_COMPARE (a.file_addr, b.file_addr); + // b and a reversed on purpose below. + LT_COMPARE (b.is_terminal_entry, a.is_terminal_entry); + LT_COMPARE (a.line, b.line); + LT_COMPARE (a.column, b.column); + LT_COMPARE (a.is_start_of_statement, b.is_start_of_statement); + LT_COMPARE (a.is_start_of_basic_block, b.is_start_of_basic_block); + // b and a reversed on purpose below. + LT_COMPARE (b.is_prologue_end, a.is_prologue_end); + LT_COMPARE (a.is_epilogue_begin, b.is_epilogue_begin); + LT_COMPARE (a.file_idx, b.file_idx); + return false; + #undef LT_COMPARE +} + + + +uint32_t +LineTable::GetSize() const +{ + return m_entries.size(); +} + +bool +LineTable::GetLineEntryAtIndex(uint32_t idx, LineEntry& line_entry) +{ + if (idx < m_entries.size()) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + return true; + } + line_entry.Clear(); + return false; +} + +bool +LineTable::FindLineEntryByAddress (const Address &so_addr, LineEntry& line_entry, uint32_t *index_ptr) +{ + if (index_ptr != NULL ) + *index_ptr = UINT32_MAX; + + bool success = false; + + if (so_addr.GetModule().get() == m_comp_unit->GetModule().get()) + { + Entry search_entry; + search_entry.file_addr = so_addr.GetFileAddress(); + if (search_entry.file_addr != LLDB_INVALID_ADDRESS) + { + entry_collection::const_iterator begin_pos = m_entries.begin(); + entry_collection::const_iterator end_pos = m_entries.end(); + entry_collection::const_iterator pos = lower_bound(begin_pos, end_pos, search_entry, Entry::EntryAddressLessThan); + if (pos != end_pos) + { + if (pos != begin_pos) + { + if (pos->file_addr != search_entry.file_addr) + --pos; + else if (pos->file_addr == search_entry.file_addr) + { + // If this is a termination entry, it should't match since + // entries with the "is_terminal_entry" member set to true + // are termination entries that define the range for the + // previous entry. + if (pos->is_terminal_entry) + { + // The matching entry is a terminal entry, so we skip + // ahead to the next entry to see if there is another + // entry following this one whose section/offset matches. + ++pos; + if (pos != end_pos) + { + if (pos->file_addr != search_entry.file_addr) + pos = end_pos; + } + } + + if (pos != end_pos) + { + // While in the same section/offset backup to find the first + // line entry that matches the address in case there are + // multiple + while (pos != begin_pos) + { + entry_collection::const_iterator prev_pos = pos - 1; + if (prev_pos->file_addr == search_entry.file_addr && + prev_pos->is_terminal_entry == false) + --pos; + else + break; + } + } + } + + } + + // Make sure we have a valid match and that the match isn't a terminating + // entry for a previous line... + if (pos != end_pos && pos->is_terminal_entry == false) + { + uint32_t match_idx = std::distance (begin_pos, pos); + success = ConvertEntryAtIndexToLineEntry(match_idx, line_entry); + if (index_ptr != NULL && success) + *index_ptr = match_idx; + } + } + } + } + return success; +} + + +bool +LineTable::ConvertEntryAtIndexToLineEntry (uint32_t idx, LineEntry &line_entry) +{ + if (idx < m_entries.size()) + { + const Entry& entry = m_entries[idx]; + ModuleSP module_sp (m_comp_unit->GetModule()); + if (module_sp && module_sp->ResolveFileAddress(entry.file_addr, line_entry.range.GetBaseAddress())) + { + if (!entry.is_terminal_entry && idx + 1 < m_entries.size()) + line_entry.range.SetByteSize(m_entries[idx+1].file_addr - entry.file_addr); + else + line_entry.range.SetByteSize(0); + + line_entry.file = m_comp_unit->GetSupportFiles().GetFileSpecAtIndex (entry.file_idx); + line_entry.line = entry.line; + line_entry.column = entry.column; + line_entry.is_start_of_statement = entry.is_start_of_statement; + line_entry.is_start_of_basic_block = entry.is_start_of_basic_block; + line_entry.is_prologue_end = entry.is_prologue_end; + line_entry.is_epilogue_begin = entry.is_epilogue_begin; + line_entry.is_terminal_entry = entry.is_terminal_entry; + return true; + } + } + return false; +} + +uint32_t +LineTable::FindLineEntryIndexByFileIndex +( + uint32_t start_idx, + const std::vector<uint32_t> &file_indexes, + uint32_t line, + bool exact, + LineEntry* line_entry_ptr +) +{ + + const size_t count = m_entries.size(); + std::vector<uint32_t>::const_iterator begin_pos = file_indexes.begin(); + std::vector<uint32_t>::const_iterator end_pos = file_indexes.end(); + size_t best_match = UINT32_MAX; + + for (size_t idx = start_idx; idx < count; ++idx) + { + // Skip line table rows that terminate the previous row (is_terminal_entry is non-zero) + if (m_entries[idx].is_terminal_entry) + continue; + + if (find (begin_pos, end_pos, m_entries[idx].file_idx) == end_pos) + continue; + + // Exact match always wins. Otherwise try to find the closest line > the desired + // line. + // FIXME: Maybe want to find the line closest before and the line closest after and + // if they're not in the same function, don't return a match. + + if (m_entries[idx].line < line) + { + continue; + } + else if (m_entries[idx].line == line) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (idx, *line_entry_ptr); + return idx; + } + else if (!exact) + { + if (best_match == UINT32_MAX) + best_match = idx; + else if (m_entries[idx].line < m_entries[best_match].line) + best_match = idx; + } + } + + if (best_match != UINT32_MAX) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (best_match, *line_entry_ptr); + return best_match; + } + return UINT32_MAX; +} + +uint32_t +LineTable::FindLineEntryIndexByFileIndex (uint32_t start_idx, uint32_t file_idx, uint32_t line, bool exact, LineEntry* line_entry_ptr) +{ + const size_t count = m_entries.size(); + size_t best_match = UINT32_MAX; + + for (size_t idx = start_idx; idx < count; ++idx) + { + // Skip line table rows that terminate the previous row (is_terminal_entry is non-zero) + if (m_entries[idx].is_terminal_entry) + continue; + + if (m_entries[idx].file_idx != file_idx) + continue; + + // Exact match always wins. Otherwise try to find the closest line > the desired + // line. + // FIXME: Maybe want to find the line closest before and the line closest after and + // if they're not in the same function, don't return a match. + + if (m_entries[idx].line < line) + { + continue; + } + else if (m_entries[idx].line == line) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (idx, *line_entry_ptr); + return idx; + } + else if (!exact) + { + if (best_match == UINT32_MAX) + best_match = idx; + else if (m_entries[idx].line < m_entries[best_match].line) + best_match = idx; + } + } + + if (best_match != UINT32_MAX) + { + if (line_entry_ptr) + ConvertEntryAtIndexToLineEntry (best_match, *line_entry_ptr); + return best_match; + } + return UINT32_MAX; +} + +size_t +LineTable::FineLineEntriesForFileIndex (uint32_t file_idx, + bool append, + SymbolContextList &sc_list) +{ + + if (!append) + sc_list.Clear(); + + size_t num_added = 0; + const size_t count = m_entries.size(); + if (count > 0) + { + SymbolContext sc (m_comp_unit); + + for (size_t idx = 0; idx < count; ++idx) + { + // Skip line table rows that terminate the previous row (is_terminal_entry is non-zero) + if (m_entries[idx].is_terminal_entry) + continue; + + if (m_entries[idx].file_idx == file_idx) + { + if (ConvertEntryAtIndexToLineEntry (idx, sc.line_entry)) + { + ++num_added; + sc_list.Append(sc); + } + } + } + } + return num_added; +} + + +void +LineTable::Dump (Stream *s, Target *target, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_line_ranges) +{ + const size_t count = m_entries.size(); + LineEntry line_entry; + FileSpec prev_file; + for (size_t idx = 0; idx < count; ++idx) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + line_entry.Dump (s, target, prev_file != line_entry.file, style, fallback_style, show_line_ranges); + s->EOL(); + prev_file = line_entry.file; + } +} + + +void +LineTable::GetDescription (Stream *s, Target *target, DescriptionLevel level) +{ + const size_t count = m_entries.size(); + LineEntry line_entry; + for (size_t idx = 0; idx < count; ++idx) + { + ConvertEntryAtIndexToLineEntry (idx, line_entry); + line_entry.GetDescription (s, level, m_comp_unit, target, true); + s->EOL(); + } +} + +size_t +LineTable::GetContiguousFileAddressRanges (FileAddressRanges &file_ranges, bool append) +{ + if (!append) + file_ranges.Clear(); + const size_t initial_count = file_ranges.GetSize(); + + const size_t count = m_entries.size(); + LineEntry line_entry; + FileAddressRanges::Entry range (LLDB_INVALID_ADDRESS, 0); + for (size_t idx = 0; idx < count; ++idx) + { + const Entry& entry = m_entries[idx]; + + if (entry.is_terminal_entry) + { + if (range.GetRangeBase() != LLDB_INVALID_ADDRESS) + { + range.SetRangeEnd(entry.file_addr); + file_ranges.Append(range); + range.Clear(LLDB_INVALID_ADDRESS); + } + } + else if (range.GetRangeBase() == LLDB_INVALID_ADDRESS) + { + range.SetRangeBase(entry.file_addr); + } + } + return file_ranges.GetSize() - initial_count; +} + +LineTable * +LineTable::LinkLineTable (const FileRangeMap &file_range_map) +{ + std::unique_ptr<LineTable> line_table_ap (new LineTable (m_comp_unit)); + LineSequenceImpl sequence; + const size_t count = m_entries.size(); + LineEntry line_entry; + const FileRangeMap::Entry *file_range_entry = NULL; + const FileRangeMap::Entry *prev_file_range_entry = NULL; + lldb::addr_t prev_file_addr = LLDB_INVALID_ADDRESS; + bool prev_entry_was_linked = false; + bool range_changed = false; + for (size_t idx = 0; idx < count; ++idx) + { + const Entry& entry = m_entries[idx]; + + const bool end_sequence = entry.is_terminal_entry; + const lldb::addr_t lookup_file_addr = entry.file_addr - (end_sequence ? 1 : 0); + if (file_range_entry == NULL || !file_range_entry->Contains(lookup_file_addr)) + { + prev_file_range_entry = file_range_entry; + file_range_entry = file_range_map.FindEntryThatContains(lookup_file_addr); + range_changed = true; + } + + lldb::addr_t prev_end_entry_linked_file_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t entry_linked_file_addr = LLDB_INVALID_ADDRESS; + + bool terminate_previous_entry = false; + if (file_range_entry) + { + entry_linked_file_addr = entry.file_addr - file_range_entry->GetRangeBase() + file_range_entry->data; + // Determine if we need to terminate the previous entry when the previous + // entry was not contguous with this one after being linked. + if (range_changed && prev_file_range_entry) + { + prev_end_entry_linked_file_addr = std::min<lldb::addr_t>(entry.file_addr, prev_file_range_entry->GetRangeEnd()) - prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data; + if (prev_end_entry_linked_file_addr != entry_linked_file_addr) + terminate_previous_entry = prev_entry_was_linked; + } + } + else if (prev_entry_was_linked) + { + // This entry doesn't have a remapping and it needs to be removed. + // Watch out in case we need to terminate a previous entry needs to + // be terminated now that one line entry in a sequence is not longer valid. + if (!entry.is_terminal_entry && + !sequence.m_entries.empty() && + !sequence.m_entries.back().is_terminal_entry) + { + terminate_previous_entry = true; + } + } + + if (terminate_previous_entry && !sequence.m_entries.empty()) + { + assert (prev_file_addr != LLDB_INVALID_ADDRESS); + sequence.m_entries.push_back(sequence.m_entries.back()); + if (prev_end_entry_linked_file_addr == LLDB_INVALID_ADDRESS) + prev_end_entry_linked_file_addr = std::min<lldb::addr_t>(entry.file_addr,prev_file_range_entry->GetRangeEnd()) - prev_file_range_entry->GetRangeBase() + prev_file_range_entry->data; + sequence.m_entries.back().file_addr = prev_end_entry_linked_file_addr; + sequence.m_entries.back().is_terminal_entry = true; + + // Append the sequence since we just terminated the previous one + line_table_ap->InsertSequence (&sequence); + sequence.Clear(); + prev_entry_was_linked = false; + } + + // Now link the current entry + if (file_range_entry) + { + // This entry has an address remapping and it needs to have its address relinked + sequence.m_entries.push_back(entry); + sequence.m_entries.back().file_addr = entry_linked_file_addr; + } + + // If we have items in the sequence and the last entry is a terminal entry, + // insert this sequence into our new line table. + if (!sequence.m_entries.empty() && sequence.m_entries.back().is_terminal_entry) + { + line_table_ap->InsertSequence (&sequence); + sequence.Clear(); + prev_entry_was_linked = false; + } + else + { + prev_entry_was_linked = file_range_entry != NULL; + } + prev_file_addr = entry.file_addr; + range_changed = false; + } + if (line_table_ap->m_entries.empty()) + return NULL; + return line_table_ap.release(); +} + + + + diff --git a/source/Symbol/ObjectFile.cpp b/source/Symbol/ObjectFile.cpp new file mode 100644 index 000000000000..0c6159122cfb --- /dev/null +++ b/source/Symbol/ObjectFile.cpp @@ -0,0 +1,621 @@ +//===-- ObjectFile.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/ObjectContainer.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/Process.h" +#include "Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h" + +using namespace lldb; +using namespace lldb_private; + +ObjectFileSP +ObjectFile::FindPlugin (const lldb::ModuleSP &module_sp, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t file_size, + DataBufferSP &data_sp, + lldb::offset_t &data_offset) +{ + ObjectFileSP object_file_sp; + + if (module_sp) + { + Timer scoped_timer (__PRETTY_FUNCTION__, + "ObjectFile::FindPlugin (module = %s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + file, (uint64_t) file_offset, (uint64_t) file_size); + if (file) + { + FileSpec archive_file; + ObjectContainerCreateInstance create_object_container_callback; + + const bool file_exists = file->Exists(); + if (!data_sp) + { + // We have an object name which most likely means we have + // a .o file in a static archive (.a file). Try and see if + // we have a cached archive first without reading any data + // first + if (file_exists && module_sp->GetObjectName()) + { + for (uint32_t idx = 0; (create_object_container_callback = PluginManager::GetObjectContainerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr<ObjectContainer> object_container_ap(create_object_container_callback(module_sp, data_sp, data_offset, file, file_offset, file_size)); + + if (object_container_ap.get()) + object_file_sp = object_container_ap->GetObjectFile(file); + + if (object_file_sp.get()) + return object_file_sp; + } + } + // Ok, we didn't find any containers that have a named object, now + // lets read the first 512 bytes from the file so the object file + // and object container plug-ins can use these bytes to see if they + // can parse this file. + if (file_size > 0) + { + data_sp = file->ReadFileContents(file_offset, std::min<size_t>(512, file_size)); + data_offset = 0; + } + } + + if (!data_sp || data_sp->GetByteSize() == 0) + { + // Check for archive file with format "/path/to/archive.a(object.o)" + char path_with_object[PATH_MAX*2]; + module_sp->GetFileSpec().GetPath(path_with_object, sizeof(path_with_object)); + + ConstString archive_object; + const bool must_exist = true; + if (ObjectFile::SplitArchivePathWithObject (path_with_object, archive_file, archive_object, must_exist)) + { + file_size = archive_file.GetByteSize(); + if (file_size > 0) + { + file = &archive_file; + module_sp->SetFileSpecAndObjectName (archive_file, archive_object); + // Check if this is a object container by iterating through all object + // container plugin instances and then trying to get an object file + // from the container plugins since we had a name. Also, don't read + // ANY data in case there is data cached in the container plug-ins + // (like BSD archives caching the contained objects within an file). + for (uint32_t idx = 0; (create_object_container_callback = PluginManager::GetObjectContainerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr<ObjectContainer> object_container_ap(create_object_container_callback(module_sp, data_sp, data_offset, file, file_offset, file_size)); + + if (object_container_ap.get()) + object_file_sp = object_container_ap->GetObjectFile(file); + + if (object_file_sp.get()) + return object_file_sp; + } + // We failed to find any cached object files in the container + // plug-ins, so lets read the first 512 bytes and try again below... + data_sp = archive_file.ReadFileContents(file_offset, 512); + } + } + } + + if (data_sp && data_sp->GetByteSize() > 0) + { + // Check if this is a normal object file by iterating through + // all object file plugin instances. + ObjectFileCreateInstance create_object_file_callback; + for (uint32_t idx = 0; (create_object_file_callback = PluginManager::GetObjectFileCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + object_file_sp.reset (create_object_file_callback(module_sp, data_sp, data_offset, file, file_offset, file_size)); + if (object_file_sp.get()) + return object_file_sp; + } + + // Check if this is a object container by iterating through + // all object container plugin instances and then trying to get + // an object file from the container. + for (uint32_t idx = 0; (create_object_container_callback = PluginManager::GetObjectContainerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr<ObjectContainer> object_container_ap(create_object_container_callback(module_sp, data_sp, data_offset, file, file_offset, file_size)); + + if (object_container_ap.get()) + object_file_sp = object_container_ap->GetObjectFile(file); + + if (object_file_sp.get()) + return object_file_sp; + } + } + } + } + // We didn't find it, so clear our shared pointer in case it + // contains anything and return an empty shared pointer + object_file_sp.reset(); + return object_file_sp; +} + +ObjectFileSP +ObjectFile::FindPlugin (const lldb::ModuleSP &module_sp, + const ProcessSP &process_sp, + lldb::addr_t header_addr, + DataBufferSP &data_sp) +{ + ObjectFileSP object_file_sp; + + if (module_sp) + { + Timer scoped_timer (__PRETTY_FUNCTION__, + "ObjectFile::FindPlugin (module = %s, process = %p, header_addr = 0x%" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + process_sp.get(), header_addr); + uint32_t idx; + + // Check if this is a normal object file by iterating through + // all object file plugin instances. + ObjectFileCreateMemoryInstance create_callback; + for (idx = 0; (create_callback = PluginManager::GetObjectFileCreateMemoryCallbackAtIndex(idx)) != NULL; ++idx) + { + object_file_sp.reset (create_callback(module_sp, data_sp, process_sp, header_addr)); + if (object_file_sp.get()) + return object_file_sp; + } + + } + // We didn't find it, so clear our shared pointer in case it + // contains anything and return an empty shared pointer + object_file_sp.reset(); + return object_file_sp; +} + +size_t +ObjectFile::GetModuleSpecifications (const FileSpec &file, + lldb::offset_t file_offset, + lldb::offset_t file_size, + ModuleSpecList &specs) +{ + DataBufferSP data_sp (file.ReadFileContents(file_offset, 512)); + if (data_sp) + { + if (file_size == 0) + { + const lldb::offset_t actual_file_size = file.GetByteSize(); + if (actual_file_size > file_offset) + file_size = actual_file_size - file_offset; + } + return ObjectFile::GetModuleSpecifications (file, // file spec + data_sp, // data bytes + 0, // data offset + file_offset,// file offset + file_size, // file length + specs); + } + return 0; +} + +size_t +ObjectFile::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + ObjectFileGetModuleSpecifications callback; + uint32_t i; + // Try the ObjectFile plug-ins + for (i = 0; (callback = PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex(i)) != NULL; ++i) + { + if (callback (file, data_sp, data_offset, file_offset, file_size, specs) > 0) + return specs.GetSize() - initial_count; + } + + // Try the ObjectContainer plug-ins + for (i = 0; (callback = PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex(i)) != NULL; ++i) + { + if (callback (file, data_sp, data_offset, file_offset, file_size, specs) > 0) + return specs.GetSize() - initial_count; + } + return 0; +} + +ObjectFile::ObjectFile (const lldb::ModuleSP &module_sp, + const FileSpec *file_spec_ptr, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset +) : + ModuleChild (module_sp), + m_file (), // This file could be different from the original module's file + m_type (eTypeInvalid), + m_strata (eStrataInvalid), + m_file_offset (file_offset), + m_length (length), + m_data (), + m_unwind_table (*this), + m_process_wp(), + m_memory_addr (LLDB_INVALID_ADDRESS), + m_sections_ap(), + m_symtab_ap () +{ + if (file_spec_ptr) + m_file = *file_spec_ptr; + if (data_sp) + m_data.SetData (data_sp, data_offset, length); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + { + if (m_file) + { + log->Printf ("%p ObjectFile::ObjectFile() module = %p (%s), file = %s, file_offset = 0x%8.8" PRIx64 ", size = %" PRIu64, + this, + module_sp.get(), + module_sp->GetSpecificationDescription().c_str(), + m_file.GetPath().c_str(), + m_file_offset, + m_length); + } + else + { + log->Printf ("%p ObjectFile::ObjectFile() module = %p (%s), file = <NULL>, file_offset = 0x%8.8" PRIx64 ", size = %" PRIu64, + this, + module_sp.get(), + module_sp->GetSpecificationDescription().c_str(), + m_file_offset, + m_length); + } + } +} + + +ObjectFile::ObjectFile (const lldb::ModuleSP &module_sp, + const ProcessSP &process_sp, + lldb::addr_t header_addr, + DataBufferSP& header_data_sp) : + ModuleChild (module_sp), + m_file (), + m_type (eTypeInvalid), + m_strata (eStrataInvalid), + m_file_offset (0), + m_length (0), + m_data (), + m_unwind_table (*this), + m_process_wp (process_sp), + m_memory_addr (header_addr), + m_sections_ap(), + m_symtab_ap () +{ + if (header_data_sp) + m_data.SetData (header_data_sp, 0, header_data_sp->GetByteSize()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + { + log->Printf ("%p ObjectFile::ObjectFile() module = %p (%s), process = %p, header_addr = 0x%" PRIx64, + this, + module_sp.get(), + module_sp->GetSpecificationDescription().c_str(), + process_sp.get(), + m_memory_addr); + } +} + + +ObjectFile::~ObjectFile() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ObjectFile::~ObjectFile ()\n", this); +} + +bool +ObjectFile::SetModulesArchitecture (const ArchSpec &new_arch) +{ + ModuleSP module_sp (GetModule()); + if (module_sp) + return module_sp->SetArchitecture (new_arch); + return false; +} + +AddressClass +ObjectFile::GetAddressClass (addr_t file_addr) +{ + Symtab *symtab = GetSymtab(); + if (symtab) + { + Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr); + if (symbol) + { + if (symbol->ValueIsAddress()) + { + const SectionSP section_sp (symbol->GetAddress().GetSection()); + if (section_sp) + { + const SectionType section_type = section_sp->GetType(); + switch (section_type) + { + case eSectionTypeInvalid: return eAddressClassUnknown; + case eSectionTypeCode: return eAddressClassCode; + case eSectionTypeContainer: return eAddressClassUnknown; + case eSectionTypeData: + case eSectionTypeDataCString: + case eSectionTypeDataCStringPointers: + case eSectionTypeDataSymbolAddress: + case eSectionTypeData4: + case eSectionTypeData8: + case eSectionTypeData16: + case eSectionTypeDataPointers: + case eSectionTypeZeroFill: + case eSectionTypeDataObjCMessageRefs: + case eSectionTypeDataObjCCFStrings: + return eAddressClassData; + case eSectionTypeDebug: + case eSectionTypeDWARFDebugAbbrev: + case eSectionTypeDWARFDebugAranges: + case eSectionTypeDWARFDebugFrame: + case eSectionTypeDWARFDebugInfo: + case eSectionTypeDWARFDebugLine: + case eSectionTypeDWARFDebugLoc: + case eSectionTypeDWARFDebugMacInfo: + case eSectionTypeDWARFDebugPubNames: + case eSectionTypeDWARFDebugPubTypes: + case eSectionTypeDWARFDebugRanges: + case eSectionTypeDWARFDebugStr: + case eSectionTypeDWARFAppleNames: + case eSectionTypeDWARFAppleTypes: + case eSectionTypeDWARFAppleNamespaces: + case eSectionTypeDWARFAppleObjC: + return eAddressClassDebug; + case eSectionTypeEHFrame: return eAddressClassRuntime; + case eSectionTypeELFSymbolTable: + case eSectionTypeELFDynamicSymbols: + case eSectionTypeELFRelocationEntries: + case eSectionTypeELFDynamicLinkInfo: + case eSectionTypeOther: return eAddressClassUnknown; + } + } + } + + const SymbolType symbol_type = symbol->GetType(); + switch (symbol_type) + { + case eSymbolTypeAny: return eAddressClassUnknown; + case eSymbolTypeAbsolute: return eAddressClassUnknown; + case eSymbolTypeCode: return eAddressClassCode; + case eSymbolTypeTrampoline: return eAddressClassCode; + case eSymbolTypeResolver: return eAddressClassCode; + case eSymbolTypeData: return eAddressClassData; + case eSymbolTypeRuntime: return eAddressClassRuntime; + case eSymbolTypeException: return eAddressClassRuntime; + case eSymbolTypeSourceFile: return eAddressClassDebug; + case eSymbolTypeHeaderFile: return eAddressClassDebug; + case eSymbolTypeObjectFile: return eAddressClassDebug; + case eSymbolTypeCommonBlock: return eAddressClassDebug; + case eSymbolTypeBlock: return eAddressClassDebug; + case eSymbolTypeLocal: return eAddressClassData; + case eSymbolTypeParam: return eAddressClassData; + case eSymbolTypeVariable: return eAddressClassData; + case eSymbolTypeVariableType: return eAddressClassDebug; + case eSymbolTypeLineEntry: return eAddressClassDebug; + case eSymbolTypeLineHeader: return eAddressClassDebug; + case eSymbolTypeScopeBegin: return eAddressClassDebug; + case eSymbolTypeScopeEnd: return eAddressClassDebug; + case eSymbolTypeAdditional: return eAddressClassUnknown; + case eSymbolTypeCompiler: return eAddressClassDebug; + case eSymbolTypeInstrumentation:return eAddressClassDebug; + case eSymbolTypeUndefined: return eAddressClassUnknown; + case eSymbolTypeObjCClass: return eAddressClassRuntime; + case eSymbolTypeObjCMetaClass: return eAddressClassRuntime; + case eSymbolTypeObjCIVar: return eAddressClassRuntime; + } + } + } + return eAddressClassUnknown; +} + +DataBufferSP +ObjectFile::ReadMemory (const ProcessSP &process_sp, lldb::addr_t addr, size_t byte_size) +{ + DataBufferSP data_sp; + if (process_sp) + { + std::unique_ptr<DataBufferHeap> data_ap (new DataBufferHeap (byte_size, 0)); + Error error; + const size_t bytes_read = process_sp->ReadMemory (addr, + data_ap->GetBytes(), + data_ap->GetByteSize(), + error); + if (bytes_read == byte_size) + data_sp.reset (data_ap.release()); + } + return data_sp; +} + +size_t +ObjectFile::GetData (off_t offset, size_t length, DataExtractor &data) const +{ + // The entire file has already been mmap'ed into m_data, so just copy from there + // as the back mmap buffer will be shared with shared pointers. + return data.SetData (m_data, offset, length); +} + +size_t +ObjectFile::CopyData (off_t offset, size_t length, void *dst) const +{ + // The entire file has already been mmap'ed into m_data, so just copy from there + return m_data.CopyByteOrderedData (offset, length, dst, length, lldb::endian::InlHostByteOrder()); +} + + +size_t +ObjectFile::ReadSectionData (const Section *section, off_t section_offset, void *dst, size_t dst_len) const +{ + // If some other objectfile owns this data, pass this to them. + if (section->GetObjectFile() != this) + return section->GetObjectFile()->ReadSectionData (section, section_offset, dst, dst_len); + + if (IsInMemory()) + { + ProcessSP process_sp (m_process_wp.lock()); + if (process_sp) + { + Error error; + const addr_t base_load_addr = section->GetLoadBaseAddress (&process_sp->GetTarget()); + if (base_load_addr != LLDB_INVALID_ADDRESS) + return process_sp->ReadMemory (base_load_addr + section_offset, dst, dst_len, error); + } + } + else + { + const uint64_t section_file_size = section->GetFileSize(); + if (section_offset < section_file_size) + { + const uint64_t section_bytes_left = section_file_size - section_offset; + uint64_t section_dst_len = dst_len; + if (section_dst_len > section_bytes_left) + section_dst_len = section_bytes_left; + return CopyData (section->GetFileOffset() + section_offset, section_dst_len, dst); + } + else + { + if (section->GetType() == eSectionTypeZeroFill) + { + const uint64_t section_size = section->GetByteSize(); + const uint64_t section_bytes_left = section_size - section_offset; + uint64_t section_dst_len = dst_len; + if (section_dst_len > section_bytes_left) + section_dst_len = section_bytes_left; + bzero(dst, section_dst_len); + return section_dst_len; + } + } + } + return 0; +} + +//---------------------------------------------------------------------- +// Get the section data the file on disk +//---------------------------------------------------------------------- +size_t +ObjectFile::ReadSectionData (const Section *section, DataExtractor& section_data) const +{ + // If some other objectfile owns this data, pass this to them. + if (section->GetObjectFile() != this) + return section->GetObjectFile()->ReadSectionData (section, section_data); + + if (IsInMemory()) + { + ProcessSP process_sp (m_process_wp.lock()); + if (process_sp) + { + const addr_t base_load_addr = section->GetLoadBaseAddress (&process_sp->GetTarget()); + if (base_load_addr != LLDB_INVALID_ADDRESS) + { + DataBufferSP data_sp (ReadMemory (process_sp, base_load_addr, section->GetByteSize())); + if (data_sp) + { + section_data.SetData (data_sp, 0, data_sp->GetByteSize()); + section_data.SetByteOrder (process_sp->GetByteOrder()); + section_data.SetAddressByteSize (process_sp->GetAddressByteSize()); + return section_data.GetByteSize(); + } + } + } + } + else + { + // The object file now contains a full mmap'ed copy of the object file data, so just use this + return MemoryMapSectionData (section, section_data); + } + section_data.Clear(); + return 0; +} + +size_t +ObjectFile::MemoryMapSectionData (const Section *section, DataExtractor& section_data) const +{ + // If some other objectfile owns this data, pass this to them. + if (section->GetObjectFile() != this) + return section->GetObjectFile()->MemoryMapSectionData (section, section_data); + + if (IsInMemory()) + { + return ReadSectionData (section, section_data); + } + else + { + // The object file now contains a full mmap'ed copy of the object file data, so just use this + return GetData(section->GetFileOffset(), section->GetFileSize(), section_data); + } + section_data.Clear(); + return 0; +} + + +bool +ObjectFile::SplitArchivePathWithObject (const char *path_with_object, FileSpec &archive_file, ConstString &archive_object, bool must_exist) +{ + RegularExpression g_object_regex("(.*)\\(([^\\)]+)\\)$"); + RegularExpression::Match regex_match(2); + if (g_object_regex.Execute (path_with_object, ®ex_match)) + { + std::string path; + std::string obj; + if (regex_match.GetMatchAtIndex (path_with_object, 1, path) && + regex_match.GetMatchAtIndex (path_with_object, 2, obj)) + { + archive_file.SetFile (path.c_str(), false); + archive_object.SetCString(obj.c_str()); + if (must_exist && !archive_file.Exists()) + return false; + return true; + } + } + return false; +} + +void +ObjectFile::ClearSymtab () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + { + log->Printf ("%p ObjectFile::ClearSymtab () symtab = %p", + this, + m_symtab_ap.get()); + } + m_symtab_ap.reset(); + } +} + +SectionList * +ObjectFile::GetSectionList() +{ + if (m_sections_ap.get() == NULL) + { + ModuleSP module_sp(GetModule()); + if (module_sp) + CreateSections(*module_sp->GetUnifiedSectionList()); + } + return m_sections_ap.get(); +} diff --git a/source/Symbol/Symbol.cpp b/source/Symbol/Symbol.cpp new file mode 100644 index 000000000000..7f3543c3b232 --- /dev/null +++ b/source/Symbol/Symbol.cpp @@ -0,0 +1,462 @@ +//===-- Symbol.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/Symbol.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolVendor.h" + +using namespace lldb; +using namespace lldb_private; + + +Symbol::Symbol() : + SymbolContextScope (), + m_uid (UINT32_MAX), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (false), + m_is_debug (false), + m_is_external (false), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_size_is_valid (false), + m_demangled_is_synthesized (false), + m_type (eSymbolTypeInvalid), + m_mangled (), + m_addr_range (), + m_flags () +{ +} + +Symbol::Symbol +( + uint32_t symID, + const char *name, + bool name_is_mangled, + SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const lldb::SectionSP §ion_sp, + addr_t offset, + addr_t size, + bool size_is_valid, + uint32_t flags +) : + SymbolContextScope (), + m_uid (symID), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (is_artificial), + m_is_debug (is_debug), + m_is_external (external), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_size_is_valid (size_is_valid || size > 0), + m_demangled_is_synthesized (false), + m_type (type), + m_mangled (ConstString(name), name_is_mangled), + m_addr_range (section_sp, offset, size), + m_flags (flags) +{ +} + +Symbol::Symbol +( + uint32_t symID, + const char *name, + bool name_is_mangled, + SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const AddressRange &range, + bool size_is_valid, + uint32_t flags +) : + SymbolContextScope (), + m_uid (symID), + m_type_data (0), + m_type_data_resolved (false), + m_is_synthetic (is_artificial), + m_is_debug (is_debug), + m_is_external (external), + m_size_is_sibling (false), + m_size_is_synthesized (false), + m_size_is_valid (size_is_valid || range.GetByteSize() > 0), + m_demangled_is_synthesized (false), + m_type (type), + m_mangled (ConstString(name), name_is_mangled), + m_addr_range (range), + m_flags (flags) +{ +} + +Symbol::Symbol(const Symbol& rhs): + SymbolContextScope (rhs), + m_uid (rhs.m_uid), + m_type_data (rhs.m_type_data), + m_type_data_resolved (rhs.m_type_data_resolved), + m_is_synthetic (rhs.m_is_synthetic), + m_is_debug (rhs.m_is_debug), + m_is_external (rhs.m_is_external), + m_size_is_sibling (rhs.m_size_is_sibling), + m_size_is_synthesized (false), + m_size_is_valid (rhs.m_size_is_valid), + m_demangled_is_synthesized (rhs.m_demangled_is_synthesized), + m_type (rhs.m_type), + m_mangled (rhs.m_mangled), + m_addr_range (rhs.m_addr_range), + m_flags (rhs.m_flags) +{ +} + +const Symbol& +Symbol::operator= (const Symbol& rhs) +{ + if (this != &rhs) + { + SymbolContextScope::operator= (rhs); + m_uid = rhs.m_uid; + m_type_data = rhs.m_type_data; + m_type_data_resolved = rhs.m_type_data_resolved; + m_is_synthetic = rhs.m_is_synthetic; + m_is_debug = rhs.m_is_debug; + m_is_external = rhs.m_is_external; + m_size_is_sibling = rhs.m_size_is_sibling; + m_size_is_synthesized = rhs.m_size_is_sibling; + m_size_is_valid = rhs.m_size_is_valid; + m_demangled_is_synthesized = rhs.m_demangled_is_synthesized; + m_type = rhs.m_type; + m_mangled = rhs.m_mangled; + m_addr_range = rhs.m_addr_range; + m_flags = rhs.m_flags; + } + return *this; +} + +void +Symbol::Clear() +{ + m_uid = UINT32_MAX; + m_mangled.Clear(); + m_type_data = 0; + m_type_data_resolved = false; + m_is_synthetic = false; + m_is_debug = false; + m_is_external = false; + m_size_is_sibling = false; + m_size_is_synthesized = false; + m_size_is_valid = false; + m_demangled_is_synthesized = false; + m_type = eSymbolTypeInvalid; + m_flags = 0; + m_addr_range.Clear(); +} + +bool +Symbol::ValueIsAddress() const +{ + return m_addr_range.GetBaseAddress().GetSection().get() != NULL; +} + +uint32_t +Symbol::GetSiblingIndex() const +{ + return m_size_is_sibling ? m_addr_range.GetByteSize() : 0; +} + +bool +Symbol::IsTrampoline () const +{ + return m_type == eSymbolTypeTrampoline; +} + +bool +Symbol::IsIndirect () const +{ + return m_type == eSymbolTypeResolver; +} + +void +Symbol::GetDescription (Stream *s, lldb::DescriptionLevel level, Target *target) const +{ + s->Printf("id = {0x%8.8x}", m_uid); + + if (m_addr_range.GetBaseAddress().GetSection()) + { + if (ValueIsAddress()) + { + const lldb::addr_t byte_size = GetByteSize(); + if (byte_size > 0) + { + s->PutCString (", range = "); + m_addr_range.Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + } + else + { + s->PutCString (", address = "); + m_addr_range.GetBaseAddress().Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + } + } + else + s->Printf (", value = 0x%16.16" PRIx64, m_addr_range.GetBaseAddress().GetOffset()); + } + else + { + if (m_size_is_sibling) + s->Printf (", sibling = %5" PRIu64, m_addr_range.GetBaseAddress().GetOffset()); + else + s->Printf (", value = 0x%16.16" PRIx64, m_addr_range.GetBaseAddress().GetOffset()); + } + if (m_mangled.GetDemangledName()) + s->Printf(", name=\"%s\"", m_mangled.GetDemangledName().AsCString()); + if (m_mangled.GetMangledName()) + s->Printf(", mangled=\"%s\"", m_mangled.GetMangledName().AsCString()); + +} + +void +Symbol::Dump(Stream *s, Target *target, uint32_t index) const +{ +// s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); +// s->Indent(); +// s->Printf("Symbol[%5u] %6u %c%c %-12s ", + s->Printf("[%5u] %6u %c%c%c %-12s ", + index, + GetID(), + m_is_debug ? 'D' : ' ', + m_is_synthetic ? 'S' : ' ', + m_is_external ? 'X' : ' ', + GetTypeAsString()); + + // Make sure the size of the symbol is up to date before dumping + GetByteSize(); + + if (ValueIsAddress()) + { + if (!m_addr_range.GetBaseAddress().Dump(s, NULL, Address::DumpStyleFileAddress)) + s->Printf("%*s", 18, ""); + + s->PutChar(' '); + + if (!m_addr_range.GetBaseAddress().Dump(s, target, Address::DumpStyleLoadAddress)) + s->Printf("%*s", 18, ""); + + const char *format = m_size_is_sibling ? + " Sibling -> [%5llu] 0x%8.8x %s\n": + " 0x%16.16" PRIx64 " 0x%8.8x %s\n"; + s->Printf( format, + GetByteSize(), + m_flags, + m_mangled.GetName().AsCString("")); + } + else + { + const char *format = m_size_is_sibling ? + "0x%16.16" PRIx64 " Sibling -> [%5llu] 0x%8.8x %s\n": + "0x%16.16" PRIx64 " 0x%16.16" PRIx64 " 0x%8.8x %s\n"; + s->Printf( format, + m_addr_range.GetBaseAddress().GetOffset(), + GetByteSize(), + m_flags, + m_mangled.GetName().AsCString("")); + } +} + +uint32_t +Symbol::GetPrologueByteSize () +{ + if (m_type == eSymbolTypeCode || m_type == eSymbolTypeResolver) + { + if (!m_type_data_resolved) + { + m_type_data_resolved = true; + + const Address &base_address = m_addr_range.GetBaseAddress(); + Function *function = base_address.CalculateSymbolContextFunction(); + if (function) + { + // Functions have line entries which can also potentially have end of prologue information. + // So if this symbol points to a function, use the prologue information from there. + m_type_data = function->GetPrologueByteSize(); + } + else + { + ModuleSP module_sp (base_address.GetModule()); + SymbolContext sc; + if (module_sp) + { + uint32_t resolved_flags = module_sp->ResolveSymbolContextForAddress (base_address, + eSymbolContextLineEntry, + sc); + if (resolved_flags & eSymbolContextLineEntry) + { + // Default to the end of the first line entry. + m_type_data = sc.line_entry.range.GetByteSize(); + + // Set address for next line. + Address addr (base_address); + addr.Slide (m_type_data); + + // Check the first few instructions and look for one that has a line number that is + // different than the first entry. This is also done in Function::GetPrologueByteSize(). + uint16_t total_offset = m_type_data; + for (int idx = 0; idx < 6; ++idx) + { + SymbolContext sc_temp; + resolved_flags = module_sp->ResolveSymbolContextForAddress (addr, eSymbolContextLineEntry, sc_temp); + // Make sure we got line number information... + if (!(resolved_flags & eSymbolContextLineEntry)) + break; + + // If this line number is different than our first one, use it and we're done. + if (sc_temp.line_entry.line != sc.line_entry.line) + { + m_type_data = total_offset; + break; + } + + // Slide addr up to the next line address. + addr.Slide (sc_temp.line_entry.range.GetByteSize()); + total_offset += sc_temp.line_entry.range.GetByteSize(); + // If we've gone too far, bail out. + if (total_offset >= m_addr_range.GetByteSize()) + break; + } + + // Sanity check - this may be a function in the middle of code that has debug information, but + // not for this symbol. So the line entries surrounding us won't lie inside our function. + // In that case, the line entry will be bigger than we are, so we do that quick check and + // if that is true, we just return 0. + if (m_type_data >= m_addr_range.GetByteSize()) + m_type_data = 0; + } + else + { + // TODO: expose something in Process to figure out the + // size of a function prologue. + m_type_data = 0; + } + } + } + } + return m_type_data; + } + return 0; +} + +bool +Symbol::Compare(const ConstString& name, SymbolType type) const +{ + if (type == eSymbolTypeAny || m_type == type) + return m_mangled.GetMangledName() == name || m_mangled.GetDemangledName() == name; + return false; +} + +#define ENUM_TO_CSTRING(x) case eSymbolType##x: return #x; + +const char * +Symbol::GetTypeAsString() const +{ + switch (m_type) + { + ENUM_TO_CSTRING(Invalid); + ENUM_TO_CSTRING(Absolute); + ENUM_TO_CSTRING(Code); + ENUM_TO_CSTRING(Data); + ENUM_TO_CSTRING(Trampoline); + ENUM_TO_CSTRING(Runtime); + ENUM_TO_CSTRING(Exception); + ENUM_TO_CSTRING(SourceFile); + ENUM_TO_CSTRING(HeaderFile); + ENUM_TO_CSTRING(ObjectFile); + ENUM_TO_CSTRING(CommonBlock); + ENUM_TO_CSTRING(Block); + ENUM_TO_CSTRING(Local); + ENUM_TO_CSTRING(Param); + ENUM_TO_CSTRING(Variable); + ENUM_TO_CSTRING(VariableType); + ENUM_TO_CSTRING(LineEntry); + ENUM_TO_CSTRING(LineHeader); + ENUM_TO_CSTRING(ScopeBegin); + ENUM_TO_CSTRING(ScopeEnd); + ENUM_TO_CSTRING(Additional); + ENUM_TO_CSTRING(Compiler); + ENUM_TO_CSTRING(Instrumentation); + ENUM_TO_CSTRING(Undefined); + ENUM_TO_CSTRING(ObjCClass); + ENUM_TO_CSTRING(ObjCMetaClass); + ENUM_TO_CSTRING(ObjCIVar); + default: + break; + } + return "<unknown SymbolType>"; +} + +void +Symbol::CalculateSymbolContext (SymbolContext *sc) +{ + // Symbols can reconstruct the symbol and the module in the symbol context + sc->symbol = this; + if (ValueIsAddress()) + sc->module_sp = GetAddress().GetModule(); + else + sc->module_sp.reset(); +} + +ModuleSP +Symbol::CalculateSymbolContextModule () +{ + if (ValueIsAddress()) + return GetAddress().GetModule(); + return ModuleSP(); +} + +Symbol * +Symbol::CalculateSymbolContextSymbol () +{ + return this; +} + +void +Symbol::DumpSymbolContext (Stream *s) +{ + bool dumped_module = false; + if (ValueIsAddress()) + { + ModuleSP module_sp (GetAddress().GetModule()); + if (module_sp) + { + dumped_module = true; + module_sp->DumpSymbolContext(s); + } + } + if (dumped_module) + s->PutCString(", "); + + s->Printf("Symbol{0x%8.8x}", GetID()); +} + +lldb::addr_t +Symbol::GetByteSize () const +{ + return m_addr_range.GetByteSize(); +} + diff --git a/source/Symbol/SymbolContext.cpp b/source/Symbol/SymbolContext.cpp new file mode 100644 index 000000000000..ac91161f7d6f --- /dev/null +++ b/source/Symbol/SymbolContext.cpp @@ -0,0 +1,1205 @@ +//===-- SymbolContext.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/SymbolContext.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SymbolContext::SymbolContext() : + target_sp (), + module_sp (), + comp_unit (NULL), + function (NULL), + block (NULL), + line_entry (), + symbol (NULL) +{ +} + +SymbolContext::SymbolContext(const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : + target_sp (), + module_sp (m), + comp_unit (cu), + function (f), + block (b), + line_entry (), + symbol (s) +{ + if (le) + line_entry = *le; +} + +SymbolContext::SymbolContext(const TargetSP &t, const ModuleSP& m, CompileUnit *cu, Function *f, Block *b, LineEntry *le, Symbol *s) : + target_sp (t), + module_sp (m), + comp_unit (cu), + function (f), + block (b), + line_entry (), + symbol (s) +{ + if (le) + line_entry = *le; +} + +SymbolContext::SymbolContext(const SymbolContext& rhs) : + target_sp (rhs.target_sp), + module_sp (rhs.module_sp), + comp_unit (rhs.comp_unit), + function (rhs.function), + block (rhs.block), + line_entry (rhs.line_entry), + symbol (rhs.symbol) +{ +} + + +SymbolContext::SymbolContext (SymbolContextScope *sc_scope) : + target_sp (), + module_sp (), + comp_unit (NULL), + function (NULL), + block (NULL), + line_entry (), + symbol (NULL) +{ + sc_scope->CalculateSymbolContext (this); +} + +SymbolContext::~SymbolContext () +{ +} + +const SymbolContext& +SymbolContext::operator= (const SymbolContext& rhs) +{ + if (this != &rhs) + { + target_sp = rhs.target_sp; + module_sp = rhs.module_sp; + comp_unit = rhs.comp_unit; + function = rhs.function; + block = rhs.block; + line_entry = rhs.line_entry; + symbol = rhs.symbol; + } + return *this; +} + +void +SymbolContext::Clear(bool clear_target) +{ + if (clear_target) + target_sp.reset(); + module_sp.reset(); + comp_unit = NULL; + function = NULL; + block = NULL; + line_entry.Clear(); + symbol = NULL; +} + +bool +SymbolContext::DumpStopContext +( + Stream *s, + ExecutionContextScope *exe_scope, + const Address &addr, + bool show_fullpaths, + bool show_module, + bool show_inlined_frames +) const +{ + bool dumped_something = false; + if (show_module && module_sp) + { + if (show_fullpaths) + *s << module_sp->GetFileSpec(); + else + *s << module_sp->GetFileSpec().GetFilename(); + s->PutChar('`'); + dumped_something = true; + } + + if (function != NULL) + { + SymbolContext inline_parent_sc; + Address inline_parent_addr; + if (function->GetMangled().GetName()) + { + dumped_something = true; + function->GetMangled().GetName().Dump(s); + } + + if (addr.IsValid()) + { + const addr_t function_offset = addr.GetOffset() - function->GetAddressRange().GetBaseAddress().GetOffset(); + if (function_offset) + { + dumped_something = true; + s->Printf(" + %" PRIu64, function_offset); + } + } + + if (GetParentOfInlinedScope (addr, inline_parent_sc, inline_parent_addr)) + { + dumped_something = true; + Block *inlined_block = block->GetContainingInlinedBlock(); + const InlineFunctionInfo* inlined_block_info = inlined_block->GetInlinedFunctionInfo(); + s->Printf (" [inlined] %s", inlined_block_info->GetName().GetCString()); + + lldb_private::AddressRange block_range; + if (inlined_block->GetRangeContainingAddress(addr, block_range)) + { + const addr_t inlined_function_offset = addr.GetOffset() - block_range.GetBaseAddress().GetOffset(); + if (inlined_function_offset) + { + s->Printf(" + %" PRIu64, inlined_function_offset); + } + } + const Declaration &call_site = inlined_block_info->GetCallSite(); + if (call_site.IsValid()) + { + s->PutCString(" at "); + call_site.DumpStopContext (s, show_fullpaths); + } + if (show_inlined_frames) + { + s->EOL(); + s->Indent(); + return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames); + } + } + else + { + if (line_entry.IsValid()) + { + dumped_something = true; + s->PutCString(" at "); + if (line_entry.DumpStopContext(s, show_fullpaths)) + dumped_something = true; + } + } + } + else if (symbol != NULL) + { + if (symbol->GetMangled().GetName()) + { + dumped_something = true; + if (symbol->GetType() == eSymbolTypeTrampoline) + s->PutCString("symbol stub for: "); + symbol->GetMangled().GetName().Dump(s); + } + + if (addr.IsValid() && symbol->ValueIsAddress()) + { + const addr_t symbol_offset = addr.GetOffset() - symbol->GetAddress().GetOffset(); + if (symbol_offset) + { + dumped_something = true; + s->Printf(" + %" PRIu64, symbol_offset); + } + } + } + else if (addr.IsValid()) + { + addr.Dump(s, exe_scope, Address::DumpStyleModuleWithFileAddress); + dumped_something = true; + } + return dumped_something; +} + +void +SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target) const +{ + if (module_sp) + { + s->Indent(" Module: file = \""); + module_sp->GetFileSpec().Dump(s); + *s << '"'; + if (module_sp->GetArchitecture().IsValid()) + s->Printf (", arch = \"%s\"", module_sp->GetArchitecture().GetArchitectureName()); + s->EOL(); + } + + if (comp_unit != NULL) + { + s->Indent("CompileUnit: "); + comp_unit->GetDescription (s, level); + s->EOL(); + } + + if (function != NULL) + { + s->Indent(" Function: "); + function->GetDescription (s, level, target); + s->EOL(); + + Type *func_type = function->GetType(); + if (func_type) + { + s->Indent(" FuncType: "); + func_type->GetDescription (s, level, false); + s->EOL(); + } + } + + if (block != NULL) + { + std::vector<Block *> blocks; + blocks.push_back (block); + Block *parent_block = block->GetParent(); + + while (parent_block) + { + blocks.push_back (parent_block); + parent_block = parent_block->GetParent(); + } + std::vector<Block *>::reverse_iterator pos; + std::vector<Block *>::reverse_iterator begin = blocks.rbegin(); + std::vector<Block *>::reverse_iterator end = blocks.rend(); + for (pos = begin; pos != end; ++pos) + { + if (pos == begin) + s->Indent(" Blocks: "); + else + s->Indent(" "); + (*pos)->GetDescription(s, function, level, target); + s->EOL(); + } + } + + if (line_entry.IsValid()) + { + s->Indent(" LineEntry: "); + line_entry.GetDescription (s, level, comp_unit, target, false); + s->EOL(); + } + + if (symbol != NULL) + { + s->Indent(" Symbol: "); + symbol->GetDescription(s, level, target); + s->EOL(); + } +} + +uint32_t +SymbolContext::GetResolvedMask () const +{ + uint32_t resolved_mask = 0; + if (target_sp) resolved_mask |= eSymbolContextTarget; + if (module_sp) resolved_mask |= eSymbolContextModule; + if (comp_unit) resolved_mask |= eSymbolContextCompUnit; + if (function) resolved_mask |= eSymbolContextFunction; + if (block) resolved_mask |= eSymbolContextBlock; + if (line_entry.IsValid()) resolved_mask |= eSymbolContextLineEntry; + if (symbol) resolved_mask |= eSymbolContextSymbol; + return resolved_mask; +} + +void +SymbolContext::Dump(Stream *s, Target *target) const +{ + *s << (void *)this << ": "; + s->Indent(); + s->PutCString("SymbolContext"); + s->IndentMore(); + s->EOL(); + s->IndentMore(); + s->Indent(); + *s << "Module = " << (void *)module_sp.get() << ' '; + if (module_sp) + module_sp->GetFileSpec().Dump(s); + s->EOL(); + s->Indent(); + *s << "CompileUnit = " << (void *)comp_unit; + if (comp_unit != NULL) + *s << " {0x" << comp_unit->GetID() << "} " << *(static_cast<FileSpec*> (comp_unit)); + s->EOL(); + s->Indent(); + *s << "Function = " << (void *)function; + if (function != NULL) + { + *s << " {0x" << function->GetID() << "} " << function->GetType()->GetName() << ", address-range = "; + function->GetAddressRange().Dump(s, target, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + s->EOL(); + s->Indent(); + Type* func_type = function->GetType(); + if (func_type) + { + *s << " Type = "; + func_type->Dump (s, false); + } + } + s->EOL(); + s->Indent(); + *s << "Block = " << (void *)block; + if (block != NULL) + *s << " {0x" << block->GetID() << '}'; + // Dump the block and pass it a negative depth to we print all the parent blocks + //if (block != NULL) + // block->Dump(s, function->GetFileAddress(), INT_MIN); + s->EOL(); + s->Indent(); + *s << "LineEntry = "; + line_entry.Dump (s, target, true, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, true); + s->EOL(); + s->Indent(); + *s << "Symbol = " << (void *)symbol; + if (symbol != NULL && symbol->GetMangled()) + *s << ' ' << symbol->GetMangled().GetName().AsCString(); + s->EOL(); + s->IndentLess(); + s->IndentLess(); +} + +bool +lldb_private::operator== (const SymbolContext& lhs, const SymbolContext& rhs) +{ + return lhs.function == rhs.function + && lhs.symbol == rhs.symbol + && lhs.module_sp.get() == rhs.module_sp.get() + && lhs.comp_unit == rhs.comp_unit + && lhs.target_sp.get() == rhs.target_sp.get() + && LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0; +} + +bool +lldb_private::operator!= (const SymbolContext& lhs, const SymbolContext& rhs) +{ + return lhs.function != rhs.function + || lhs.symbol != rhs.symbol + || lhs.module_sp.get() != rhs.module_sp.get() + || lhs.comp_unit != rhs.comp_unit + || lhs.target_sp.get() != rhs.target_sp.get() + || LineEntry::Compare(lhs.line_entry, rhs.line_entry) != 0; +} + +bool +SymbolContext::GetAddressRange (uint32_t scope, + uint32_t range_idx, + bool use_inline_block_range, + AddressRange &range) const +{ + if ((scope & eSymbolContextLineEntry) && line_entry.IsValid()) + { + range = line_entry.range; + return true; + } + + if ((scope & eSymbolContextBlock) && (block != NULL)) + { + if (use_inline_block_range) + { + Block *inline_block = block->GetContainingInlinedBlock(); + if (inline_block) + return inline_block->GetRangeAtIndex (range_idx, range); + } + else + { + return block->GetRangeAtIndex (range_idx, range); + } + } + + if ((scope & eSymbolContextFunction) && (function != NULL)) + { + if (range_idx == 0) + { + range = function->GetAddressRange(); + return true; + } + } + + if ((scope & eSymbolContextSymbol) && (symbol != NULL)) + { + if (range_idx == 0) + { + if (symbol->ValueIsAddress()) + { + range.GetBaseAddress() = symbol->GetAddress(); + range.SetByteSize (symbol->GetByteSize()); + return true; + } + } + } + range.Clear(); + return false; +} + +bool +SymbolContext::GetParentOfInlinedScope (const Address &curr_frame_pc, + SymbolContext &next_frame_sc, + Address &next_frame_pc) const +{ + next_frame_sc.Clear(false); + next_frame_pc.Clear(); + + if (block) + { + //const addr_t curr_frame_file_addr = curr_frame_pc.GetFileAddress(); + + // In order to get the parent of an inlined function we first need to + // see if we are in an inlined block as "this->block" could be an + // inlined block, or a parent of "block" could be. So lets check if + // this block or one of this blocks parents is an inlined function. + Block *curr_inlined_block = block->GetContainingInlinedBlock(); + if (curr_inlined_block) + { + // "this->block" is contained in an inline function block, so to + // get the scope above the inlined block, we get the parent of the + // inlined block itself + Block *next_frame_block = curr_inlined_block->GetParent(); + // Now calculate the symbol context of the containing block + next_frame_block->CalculateSymbolContext (&next_frame_sc); + + // If we get here we weren't able to find the return line entry using the nesting of the blocks and + // the line table. So just use the call site info from our inlined block. + + AddressRange range; + if (curr_inlined_block->GetRangeContainingAddress (curr_frame_pc, range)) + { + // To see there this new frame block it, we need to look at the + // call site information from + const InlineFunctionInfo* curr_inlined_block_inlined_info = curr_inlined_block->GetInlinedFunctionInfo(); + next_frame_pc = range.GetBaseAddress(); + next_frame_sc.line_entry.range.GetBaseAddress() = next_frame_pc; + next_frame_sc.line_entry.file = curr_inlined_block_inlined_info->GetCallSite().GetFile(); + next_frame_sc.line_entry.line = curr_inlined_block_inlined_info->GetCallSite().GetLine(); + next_frame_sc.line_entry.column = curr_inlined_block_inlined_info->GetCallSite().GetColumn(); + return true; + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); + + if (log) + { + log->Printf ("warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64, + curr_inlined_block->GetID(), curr_frame_pc.GetFileAddress()); + } +#ifdef LLDB_CONFIGURATION_DEBUG + else + { + ObjectFile *objfile = NULL; + if (module_sp) + { + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); + if (symbol_file) + objfile = symbol_file->GetObjectFile(); + } + } + if (objfile) + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 " in %s\n", + curr_inlined_block->GetID(), + curr_frame_pc.GetFileAddress(), + objfile->GetFileSpec().GetPath().c_str()); + } + else + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: inlined block 0x%8.8" PRIx64 " doesn't have a range that contains file address 0x%" PRIx64 "\n", + curr_inlined_block->GetID(), + curr_frame_pc.GetFileAddress()); + } + } +#endif + } + } + } + + return false; +} + +Block * +SymbolContext::GetFunctionBlock () +{ + if (function) + { + if (block) + { + // If this symbol context has a block, check to see if this block + // is itself, or is contained within a block with inlined function + // information. If so, then the inlined block is the block that + // defines the function. + Block *inlined_block = block->GetContainingInlinedBlock(); + if (inlined_block) + return inlined_block; + + // The block in this symbol context is not inside an inlined + // block, so the block that defines the function is the function's + // top level block, which is returned below. + } + + // There is no block information in this symbol context, so we must + // assume that the block that is desired is the top level block of + // the function itself. + return &function->GetBlock(true); + } + return NULL; +} + +bool +SymbolContext::GetFunctionMethodInfo (lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name) + + +{ + Block *function_block = GetFunctionBlock (); + if (function_block) + { + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (decl_context) + { + return ClangASTContext::GetClassMethodInfoForDeclContext (decl_context, + language, + is_instance_method, + language_object_name); + } + } + language = eLanguageTypeUnknown; + is_instance_method = false; + language_object_name.Clear(); + return false; +} + +ConstString +SymbolContext::GetFunctionName (Mangled::NamePreference preference) const +{ + if (function) + { + if (block) + { + Block *inlined_block = block->GetContainingInlinedBlock(); + + if (inlined_block) + { + const InlineFunctionInfo *inline_info = inlined_block->GetInlinedFunctionInfo(); + if (inline_info) + return inline_info->GetName(); + } + } + return function->GetMangled().GetName(preference); + } + else if (symbol && symbol->ValueIsAddress()) + { + return symbol->GetMangled().GetName(preference); + } + else + { + // No function, return an empty string. + return ConstString(); + } +} + +LineEntry +SymbolContext::GetFunctionStartLineEntry () const +{ + LineEntry line_entry; + Address start_addr; + if (block) + { + Block *inlined_block = block->GetContainingInlinedBlock(); + if (inlined_block) + { + if (inlined_block->GetStartAddress (start_addr)) + { + if (start_addr.CalculateSymbolContextLineEntry (line_entry)) + return line_entry; + } + return LineEntry(); + } + } + + if (function) + { + if (function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry(line_entry)) + return line_entry; + } + return LineEntry(); +} + +//---------------------------------------------------------------------- +// +// SymbolContextSpecifier +// +//---------------------------------------------------------------------- + +SymbolContextSpecifier::SymbolContextSpecifier (const TargetSP &target_sp) : + m_target_sp (target_sp), + m_module_spec (), + m_module_sp (), + m_file_spec_ap (), + m_start_line (0), + m_end_line (0), + m_function_spec (), + m_class_name (), + m_address_range_ap (), + m_type (eNothingSpecified) +{ +} + +SymbolContextSpecifier::~SymbolContextSpecifier() +{ +} + +bool +SymbolContextSpecifier::AddLineSpecification (uint32_t line_no, SpecificationType type) +{ + bool return_value = true; + switch (type) + { + case eNothingSpecified: + Clear(); + break; + case eLineStartSpecified: + m_start_line = line_no; + m_type |= eLineStartSpecified; + break; + case eLineEndSpecified: + m_end_line = line_no; + m_type |= eLineEndSpecified; + break; + default: + return_value = false; + break; + } + return return_value; +} + +bool +SymbolContextSpecifier::AddSpecification (const char *spec_string, SpecificationType type) +{ + bool return_value = true; + switch (type) + { + case eNothingSpecified: + Clear(); + break; + case eModuleSpecified: + { + // See if we can find the Module, if so stick it in the SymbolContext. + FileSpec module_file_spec(spec_string, false); + ModuleSpec module_spec (module_file_spec); + lldb::ModuleSP module_sp (m_target_sp->GetImages().FindFirstModule (module_spec)); + m_type |= eModuleSpecified; + if (module_sp) + m_module_sp = module_sp; + else + m_module_spec.assign (spec_string); + } + break; + case eFileSpecified: + // CompUnits can't necessarily be resolved here, since an inlined function might show up in + // a number of CompUnits. Instead we just convert to a FileSpec and store it away. + m_file_spec_ap.reset (new FileSpec (spec_string, false)); + m_type |= eFileSpecified; + break; + case eLineStartSpecified: + m_start_line = Args::StringToSInt32(spec_string, 0, 0, &return_value); + if (return_value) + m_type |= eLineStartSpecified; + break; + case eLineEndSpecified: + m_end_line = Args::StringToSInt32(spec_string, 0, 0, &return_value); + if (return_value) + m_type |= eLineEndSpecified; + break; + case eFunctionSpecified: + m_function_spec.assign(spec_string); + m_type |= eFunctionSpecified; + break; + case eClassOrNamespaceSpecified: + Clear(); + m_class_name.assign (spec_string); + m_type = eClassOrNamespaceSpecified; + break; + case eAddressRangeSpecified: + // Not specified yet... + break; + } + + return return_value; +} + +void +SymbolContextSpecifier::Clear() +{ + m_module_spec.clear(); + m_file_spec_ap.reset(); + m_function_spec.clear(); + m_class_name.clear(); + m_start_line = 0; + m_end_line = 0; + m_address_range_ap.reset(); + + m_type = eNothingSpecified; +} + +bool +SymbolContextSpecifier::SymbolContextMatches(SymbolContext &sc) +{ + if (m_type == eNothingSpecified) + return true; + + if (m_target_sp.get() != sc.target_sp.get()) + return false; + + if (m_type & eModuleSpecified) + { + if (sc.module_sp) + { + if (m_module_sp.get() != NULL) + { + if (m_module_sp.get() != sc.module_sp.get()) + return false; + } + else + { + FileSpec module_file_spec (m_module_spec.c_str(), false); + if (!FileSpec::Equal (module_file_spec, sc.module_sp->GetFileSpec(), false)) + return false; + } + } + } + if (m_type & eFileSpecified) + { + if (m_file_spec_ap.get()) + { + // If we don't have a block or a comp_unit, then we aren't going to match a source file. + if (sc.block == NULL && sc.comp_unit == NULL) + return false; + + // Check if the block is present, and if so is it inlined: + bool was_inlined = false; + if (sc.block != NULL) + { + const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo(); + if (inline_info != NULL) + { + was_inlined = true; + if (!FileSpec::Equal (inline_info->GetDeclaration().GetFile(), *(m_file_spec_ap.get()), false)) + return false; + } + } + + // Next check the comp unit, but only if the SymbolContext was not inlined. + if (!was_inlined && sc.comp_unit != NULL) + { + if (!FileSpec::Equal (*(sc.comp_unit), *(m_file_spec_ap.get()), false)) + return false; + } + } + } + if (m_type & eLineStartSpecified + || m_type & eLineEndSpecified) + { + if (sc.line_entry.line < m_start_line || sc.line_entry.line > m_end_line) + return false; + } + + if (m_type & eFunctionSpecified) + { + // First check the current block, and if it is inlined, get the inlined function name: + bool was_inlined = false; + ConstString func_name(m_function_spec.c_str()); + + if (sc.block != NULL) + { + const InlineFunctionInfo *inline_info = sc.block->GetInlinedFunctionInfo(); + if (inline_info != NULL) + { + was_inlined = true; + const Mangled &name = inline_info->GetMangled(); + if (!name.NameMatches (func_name)) + return false; + } + } + // If it wasn't inlined, check the name in the function or symbol: + if (!was_inlined) + { + if (sc.function != NULL) + { + if (!sc.function->GetMangled().NameMatches(func_name)) + return false; + } + else if (sc.symbol != NULL) + { + if (!sc.symbol->GetMangled().NameMatches(func_name)) + return false; + } + } + + + } + + return true; +} + +bool +SymbolContextSpecifier::AddressMatches(lldb::addr_t addr) +{ + if (m_type & eAddressRangeSpecified) + { + + } + else + { + Address match_address (addr, NULL); + SymbolContext sc; + m_target_sp->GetImages().ResolveSymbolContextForAddress(match_address, eSymbolContextEverything, sc); + return SymbolContextMatches(sc); + } + return true; +} + +void +SymbolContextSpecifier::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + char path_str[PATH_MAX + 1]; + + if (m_type == eNothingSpecified) + { + s->Printf ("Nothing specified.\n"); + } + + if (m_type == eModuleSpecified) + { + s->Indent(); + if (m_module_sp) + { + m_module_sp->GetFileSpec().GetPath (path_str, PATH_MAX); + s->Printf ("Module: %s\n", path_str); + } + else + s->Printf ("Module: %s\n", m_module_spec.c_str()); + } + + if (m_type == eFileSpecified && m_file_spec_ap.get() != NULL) + { + m_file_spec_ap->GetPath (path_str, PATH_MAX); + s->Indent(); + s->Printf ("File: %s", path_str); + if (m_type == eLineStartSpecified) + { + s->Printf (" from line %lu", m_start_line); + if (m_type == eLineEndSpecified) + s->Printf ("to line %lu", m_end_line); + else + s->Printf ("to end"); + } + else if (m_type == eLineEndSpecified) + { + s->Printf (" from start to line %ld", m_end_line); + } + s->Printf (".\n"); + } + + if (m_type == eLineStartSpecified) + { + s->Indent(); + s->Printf ("From line %lu", m_start_line); + if (m_type == eLineEndSpecified) + s->Printf ("to line %lu", m_end_line); + else + s->Printf ("to end"); + s->Printf (".\n"); + } + else if (m_type == eLineEndSpecified) + { + s->Printf ("From start to line %ld.\n", m_end_line); + } + + if (m_type == eFunctionSpecified) + { + s->Indent(); + s->Printf ("Function: %s.\n", m_function_spec.c_str()); + } + + if (m_type == eClassOrNamespaceSpecified) + { + s->Indent(); + s->Printf ("Class name: %s.\n", m_class_name.c_str()); + } + + if (m_type == eAddressRangeSpecified && m_address_range_ap.get() != NULL) + { + s->Indent(); + s->PutCString ("Address range: "); + m_address_range_ap->Dump (s, m_target_sp.get(), Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + s->PutCString ("\n"); + } +} + +//---------------------------------------------------------------------- +// +// SymbolContextList +// +//---------------------------------------------------------------------- + + +SymbolContextList::SymbolContextList() : + m_symbol_contexts() +{ +} + +SymbolContextList::~SymbolContextList() +{ +} + +void +SymbolContextList::Append(const SymbolContext& sc) +{ + m_symbol_contexts.push_back(sc); +} + +void +SymbolContextList::Append (const SymbolContextList& sc_list) +{ + collection::const_iterator pos, end = sc_list.m_symbol_contexts.end(); + for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos) + m_symbol_contexts.push_back (*pos); +} + + +uint32_t +SymbolContextList::AppendIfUnique (const SymbolContextList& sc_list, bool merge_symbol_into_function) +{ + uint32_t unique_sc_add_count = 0; + collection::const_iterator pos, end = sc_list.m_symbol_contexts.end(); + for (pos = sc_list.m_symbol_contexts.begin(); pos != end; ++pos) + { + if (AppendIfUnique (*pos, merge_symbol_into_function)) + ++unique_sc_add_count; + } + return unique_sc_add_count; +} + +bool +SymbolContextList::AppendIfUnique (const SymbolContext& sc, bool merge_symbol_into_function) +{ + collection::iterator pos, end = m_symbol_contexts.end(); + for (pos = m_symbol_contexts.begin(); pos != end; ++pos) + { + if (*pos == sc) + return false; + } + if (merge_symbol_into_function + && sc.symbol != NULL + && sc.comp_unit == NULL + && sc.function == NULL + && sc.block == NULL + && sc.line_entry.IsValid() == false) + { + if (sc.symbol->ValueIsAddress()) + { + for (pos = m_symbol_contexts.begin(); pos != end; ++pos) + { + // Don't merge symbols into inlined function symbol contexts + if (pos->block && pos->block->GetContainingInlinedBlock()) + continue; + + if (pos->function) + { + if (pos->function->GetAddressRange().GetBaseAddress() == sc.symbol->GetAddress()) + { + // Do we already have a function with this symbol? + if (pos->symbol == sc.symbol) + return false; + if (pos->symbol == NULL) + { + pos->symbol = sc.symbol; + return false; + } + } + } + } + } + } + m_symbol_contexts.push_back(sc); + return true; +} + +bool +SymbolContextList::MergeSymbolContextIntoFunctionContext (const SymbolContext& symbol_sc, + uint32_t start_idx, + uint32_t stop_idx) +{ + if (symbol_sc.symbol != NULL + && symbol_sc.comp_unit == NULL + && symbol_sc.function == NULL + && symbol_sc.block == NULL + && symbol_sc.line_entry.IsValid() == false) + { + if (symbol_sc.symbol->ValueIsAddress()) + { + const size_t end = std::min<size_t>(m_symbol_contexts.size(), stop_idx); + for (size_t i=start_idx; i<end; ++i) + { + const SymbolContext &function_sc = m_symbol_contexts[i]; + // Don't merge symbols into inlined function symbol contexts + if (function_sc.block && function_sc.block->GetContainingInlinedBlock()) + continue; + + if (function_sc.function) + { + if (function_sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddress()) + { + // Do we already have a function with this symbol? + if (function_sc.symbol == symbol_sc.symbol) + return true; // Already have a symbol context with this symbol, return true + + if (function_sc.symbol == NULL) + { + // We successfully merged this symbol into an existing symbol context + m_symbol_contexts[i].symbol = symbol_sc.symbol; + return true; + } + } + } + } + } + } + return false; +} + +void +SymbolContextList::Clear() +{ + m_symbol_contexts.clear(); +} + +void +SymbolContextList::Dump(Stream *s, Target *target) const +{ + + *s << (void *)this << ": "; + s->Indent(); + s->PutCString("SymbolContextList"); + s->EOL(); + s->IndentMore(); + + collection::const_iterator pos, end = m_symbol_contexts.end(); + for (pos = m_symbol_contexts.begin(); pos != end; ++pos) + { + //pos->Dump(s, target); + pos->GetDescription(s, eDescriptionLevelVerbose, target); + } + s->IndentLess(); +} + +bool +SymbolContextList::GetContextAtIndex(size_t idx, SymbolContext& sc) const +{ + if (idx < m_symbol_contexts.size()) + { + sc = m_symbol_contexts[idx]; + return true; + } + return false; +} + +bool +SymbolContextList::GetLastContext(SymbolContext& sc) const +{ + if (!m_symbol_contexts.empty()) + { + sc = m_symbol_contexts.back(); + return true; + } + return false; +} + +bool +SymbolContextList::RemoveContextAtIndex (size_t idx) +{ + if (idx < m_symbol_contexts.size()) + { + m_symbol_contexts.erase(m_symbol_contexts.begin() + idx); + return true; + } + return false; +} + +uint32_t +SymbolContextList::GetSize() const +{ + return m_symbol_contexts.size(); +} + +uint32_t +SymbolContextList::NumLineEntriesWithLine (uint32_t line) const +{ + uint32_t match_count = 0; + const size_t size = m_symbol_contexts.size(); + for (size_t idx = 0; idx<size; ++idx) + { + if (m_symbol_contexts[idx].line_entry.line == line) + ++match_count; + } + return match_count; +} + +void +SymbolContextList::GetDescription(Stream *s, + lldb::DescriptionLevel level, + Target *target) const +{ + const size_t size = m_symbol_contexts.size(); + for (size_t idx = 0; idx<size; ++idx) + m_symbol_contexts[idx].GetDescription (s, level, target); +} + +bool +lldb_private::operator== (const SymbolContextList& lhs, const SymbolContextList& rhs) +{ + const uint32_t size = lhs.GetSize(); + if (size != rhs.GetSize()) + return false; + + SymbolContext lhs_sc; + SymbolContext rhs_sc; + for (uint32_t i=0; i<size; ++i) + { + lhs.GetContextAtIndex(i, lhs_sc); + rhs.GetContextAtIndex(i, rhs_sc); + if (lhs_sc != rhs_sc) + return false; + } + return true; +} + +bool +lldb_private::operator!= (const SymbolContextList& lhs, const SymbolContextList& rhs) +{ + return !(lhs == rhs); +} + diff --git a/source/Symbol/SymbolFile.cpp b/source/Symbol/SymbolFile.cpp new file mode 100644 index 000000000000..412c4600c9f7 --- /dev/null +++ b/source/Symbol/SymbolFile.cpp @@ -0,0 +1,89 @@ +//===-- SymbolFile.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/SymbolFile.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb_private; + +SymbolFile* +SymbolFile::FindPlugin (ObjectFile* obj_file) +{ + std::unique_ptr<SymbolFile> best_symfile_ap; + if (obj_file != NULL) + { + + // We need to test the abilities of this section list. So create what it would + // be with this new obj_file. + lldb::ModuleSP module_sp(obj_file->GetModule()); + if (module_sp) + { + // Default to the main module section list. + ObjectFile *module_obj_file = module_sp->GetObjectFile(); + if (module_obj_file != obj_file) + { + // Make sure the main object file's sections are created + module_obj_file->GetSectionList(); + obj_file->CreateSections (*module_sp->GetUnifiedSectionList()); + } + } + + // TODO: Load any plug-ins in the appropriate plug-in search paths and + // iterate over all of them to find the best one for the job. + + uint32_t best_symfile_abilities = 0; + + SymbolFileCreateInstance create_callback; + for (uint32_t idx = 0; (create_callback = PluginManager::GetSymbolFileCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr<SymbolFile> curr_symfile_ap(create_callback(obj_file)); + + if (curr_symfile_ap.get()) + { + const uint32_t sym_file_abilities = curr_symfile_ap->GetAbilities(); + if (sym_file_abilities > best_symfile_abilities) + { + best_symfile_abilities = sym_file_abilities; + best_symfile_ap.reset (curr_symfile_ap.release()); + // If any symbol file parser has all of the abilities, then + // we should just stop looking. + if ((kAllAbilities & sym_file_abilities) == kAllAbilities) + break; + } + } + } + if (best_symfile_ap.get()) + { + // Let the winning symbol file parser initialize itself more + // completely now that it has been chosen + best_symfile_ap->InitializeObject(); + } + } + return best_symfile_ap.release(); +} + +TypeList * +SymbolFile::GetTypeList () +{ + if (m_obj_file) + return m_obj_file->GetModule()->GetTypeList(); + return NULL; +} + +lldb_private::ClangASTContext & +SymbolFile::GetClangASTContext () +{ + return m_obj_file->GetModule()->GetClangASTContext(); +} diff --git a/source/Symbol/SymbolVendor.cpp b/source/Symbol/SymbolVendor.cpp new file mode 100644 index 000000000000..b51ac5a550fb --- /dev/null +++ b/source/Symbol/SymbolVendor.cpp @@ -0,0 +1,486 @@ +//===-- SymbolVendor.mm -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/SymbolVendor.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// FindPlugin +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendor::FindPlugin (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm) +{ + std::unique_ptr<SymbolVendor> instance_ap; + SymbolVendorCreateInstance create_callback; + + for (size_t idx = 0; (create_callback = PluginManager::GetSymbolVendorCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + instance_ap.reset(create_callback(module_sp, feedback_strm)); + + if (instance_ap.get()) + { + return instance_ap.release(); + } + } + // The default implementation just tries to create debug information using the + // file representation for the module. + instance_ap.reset(new SymbolVendor(module_sp)); + if (instance_ap.get()) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + instance_ap->AddSymbolFileRepresentation(objfile->shared_from_this()); + } + return instance_ap.release(); +} + +//---------------------------------------------------------------------- +// SymbolVendor constructor +//---------------------------------------------------------------------- +SymbolVendor::SymbolVendor(const lldb::ModuleSP &module_sp) : + ModuleChild (module_sp), + m_type_list(), + m_compile_units(), + m_sym_file_ap() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendor::~SymbolVendor() +{ +} + +//---------------------------------------------------------------------- +// Add a represention given an object file. +//---------------------------------------------------------------------- +void +SymbolVendor::AddSymbolFileRepresentation(const ObjectFileSP &objfile_sp) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (objfile_sp) + { + m_objfile_sp = objfile_sp; + m_sym_file_ap.reset(SymbolFile::FindPlugin(objfile_sp.get())); + } + } +} + +bool +SymbolVendor::SetCompileUnitAtIndex (size_t idx, const CompUnitSP &cu_sp) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + const size_t num_compile_units = GetNumCompileUnits(); + if (idx < num_compile_units) + { + // Fire off an assertion if this compile unit already exists for now. + // The partial parsing should take care of only setting the compile + // unit once, so if this assertion fails, we need to make sure that + // we don't have a race condition, or have a second parse of the same + // compile unit. + assert(m_compile_units[idx].get() == NULL); + m_compile_units[idx] = cu_sp; + return true; + } + else + { + // This should NOT happen, and if it does, we want to crash and know + // about it + assert (idx < num_compile_units); + } + } + return false; +} + +size_t +SymbolVendor::GetNumCompileUnits() +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_compile_units.empty()) + { + if (m_sym_file_ap.get()) + { + // Resize our array of compile unit shared pointers -- which will + // each remain NULL until someone asks for the actual compile unit + // information. When this happens, the symbol file will be asked + // to parse this compile unit information. + m_compile_units.resize(m_sym_file_ap->GetNumCompileUnits()); + } + } + } + return m_compile_units.size(); +} + +lldb::LanguageType +SymbolVendor::ParseCompileUnitLanguage (const SymbolContext& sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitLanguage(sc); + } + return eLanguageTypeUnknown; +} + + +size_t +SymbolVendor::ParseCompileUnitFunctions (const SymbolContext &sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitFunctions(sc); + } + return 0; +} + +bool +SymbolVendor::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitLineTable(sc); + } + return false; +} + +bool +SymbolVendor::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseCompileUnitSupportFiles(sc, support_files); + } + return false; +} + +size_t +SymbolVendor::ParseFunctionBlocks (const SymbolContext &sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseFunctionBlocks(sc); + } + return 0; +} + +size_t +SymbolVendor::ParseTypes (const SymbolContext &sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseTypes(sc); + } + return 0; +} + +size_t +SymbolVendor::ParseVariablesForContext (const SymbolContext& sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ParseVariablesForContext(sc); + } + return 0; +} + +Type* +SymbolVendor::ResolveTypeUID(lldb::user_id_t type_uid) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveTypeUID(type_uid); + } + return NULL; +} + + +uint32_t +SymbolVendor::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveSymbolContext(so_addr, resolve_scope, sc); + } + return 0; +} + +uint32_t +SymbolVendor::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); + } + return 0; +} + +size_t +SymbolVendor::FindGlobalVariables (const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, size_t max_matches, VariableList& variables) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindGlobalVariables(name, namespace_decl, append, max_matches, variables); + } + return 0; +} + +size_t +SymbolVendor::FindGlobalVariables (const RegularExpression& regex, bool append, size_t max_matches, VariableList& variables) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindGlobalVariables(regex, append, max_matches, variables); + } + return 0; +} + +size_t +SymbolVendor::FindFunctions(const ConstString &name, const ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindFunctions(name, namespace_decl, name_type_mask, include_inlines, append, sc_list); + } + return 0; +} + +size_t +SymbolVendor::FindFunctions(const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindFunctions(regex, include_inlines, append, sc_list); + } + return 0; +} + + +size_t +SymbolVendor::FindTypes (const SymbolContext& sc, const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, size_t max_matches, TypeList& types) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->FindTypes(sc, name, namespace_decl, append, max_matches, types); + } + if (!append) + types.Clear(); + return 0; +} + +size_t +SymbolVendor::GetTypes (SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + return m_sym_file_ap->GetTypes (sc_scope, type_mask, type_list); + } + return 0; +} + +ClangNamespaceDecl +SymbolVendor::FindNamespace(const SymbolContext& sc, const ConstString &name, const ClangNamespaceDecl *parent_namespace_decl) +{ + ClangNamespaceDecl namespace_decl; + ModuleSP module_sp(GetModule()); + if (module_sp) + { + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + if (m_sym_file_ap.get()) + namespace_decl = m_sym_file_ap->FindNamespace (sc, name, parent_namespace_decl); + } + return namespace_decl; +} + +void +SymbolVendor::Dump(Stream *s) +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + bool show_context = false; + + s->Printf("%p: ", this); + s->Indent(); + s->PutCString("SymbolVendor"); + if (m_sym_file_ap.get()) + { + ObjectFile *objfile = m_sym_file_ap->GetObjectFile(); + if (objfile) + { + const FileSpec &objfile_file_spec = objfile->GetFileSpec(); + if (objfile_file_spec) + { + s->PutCString(" ("); + objfile_file_spec.Dump(s); + s->PutChar(')'); + } + } + } + s->EOL(); + s->IndentMore(); + m_type_list.Dump(s, show_context); + + CompileUnitConstIter cu_pos, cu_end; + cu_end = m_compile_units.end(); + for (cu_pos = m_compile_units.begin(); cu_pos != cu_end; ++cu_pos) + { + // We currently only dump the compile units that have been parsed + if (cu_pos->get()) + (*cu_pos)->Dump(s, show_context); + } + + s->IndentLess(); + } +} + +CompUnitSP +SymbolVendor::GetCompileUnitAtIndex(size_t idx) +{ + CompUnitSP cu_sp; + ModuleSP module_sp(GetModule()); + if (module_sp) + { + const size_t num_compile_units = GetNumCompileUnits(); + if (idx < num_compile_units) + { + cu_sp = m_compile_units[idx]; + if (cu_sp.get() == NULL) + { + m_compile_units[idx] = m_sym_file_ap->ParseCompileUnitAtIndex(idx); + cu_sp = m_compile_units[idx]; + } + } + } + return cu_sp; +} + +Symtab * +SymbolVendor::GetSymtab () +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + { + // Get symbol table from unified section list. + return objfile->GetSymtab (); + } + } + return NULL; +} + +void +SymbolVendor::ClearSymtab() +{ + ModuleSP module_sp(GetModule()); + if (module_sp) + { + ObjectFile *objfile = module_sp->GetObjectFile(); + if (objfile) + { + // Clear symbol table from unified section list. + objfile->ClearSymtab (); + } + } +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +SymbolVendor::GetPluginName() +{ + static ConstString g_name("vendor-default"); + return g_name; +} + +uint32_t +SymbolVendor::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Symbol/Symtab.cpp b/source/Symbol/Symtab.cpp new file mode 100644 index 000000000000..27af6988aa9d --- /dev/null +++ b/source/Symbol/Symtab.cpp @@ -0,0 +1,1211 @@ +//===-- Symtab.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <map> + +#include "lldb/Core/Module.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; + + + +Symtab::Symtab(ObjectFile *objfile) : + m_objfile (objfile), + m_symbols (), + m_file_addr_to_index (), + m_name_to_index (), + m_mutex (Mutex::eMutexTypeRecursive), + m_file_addr_to_index_computed (false), + m_name_indexes_computed (false) +{ +} + +Symtab::~Symtab() +{ +} + +void +Symtab::Reserve(size_t count) +{ + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + m_symbols.reserve (count); +} + +Symbol * +Symtab::Resize(size_t count) +{ + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + m_symbols.resize (count); + return &m_symbols[0]; +} + +uint32_t +Symtab::AddSymbol(const Symbol& symbol) +{ + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + uint32_t symbol_idx = m_symbols.size(); + m_name_to_index.Clear(); + m_file_addr_to_index.Clear(); + m_symbols.push_back(symbol); + m_file_addr_to_index_computed = false; + m_name_indexes_computed = false; + return symbol_idx; +} + +size_t +Symtab::GetNumSymbols() const +{ + Mutex::Locker locker (m_mutex); + return m_symbols.size(); +} + +void +Symtab::Dump (Stream *s, Target *target, SortOrder sort_order) +{ + Mutex::Locker locker (m_mutex); + +// s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + const FileSpec &file_spec = m_objfile->GetFileSpec(); + const char * object_name = NULL; + if (m_objfile->GetModule()) + object_name = m_objfile->GetModule()->GetObjectName().GetCString(); + + if (file_spec) + s->Printf("Symtab, file = %s%s%s%s, num_symbols = %lu", + file_spec.GetPath().c_str(), + object_name ? "(" : "", + object_name ? object_name : "", + object_name ? ")" : "", + m_symbols.size()); + else + s->Printf("Symtab, num_symbols = %lu", m_symbols.size()); + + if (!m_symbols.empty()) + { + switch (sort_order) + { + case eSortOrderNone: + { + s->PutCString (":\n"); + DumpSymbolHeader (s); + const_iterator begin = m_symbols.begin(); + const_iterator end = m_symbols.end(); + for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) + { + s->Indent(); + pos->Dump(s, target, std::distance(begin, pos)); + } + } + break; + + case eSortOrderByName: + { + // Although we maintain a lookup by exact name map, the table + // isn't sorted by name. So we must make the ordered symbol list + // up ourselves. + s->PutCString (" (sorted by name):\n"); + DumpSymbolHeader (s); + typedef std::multimap<const char*, const Symbol *, CStringCompareFunctionObject> CStringToSymbol; + CStringToSymbol name_map; + for (const_iterator pos = m_symbols.begin(), end = m_symbols.end(); pos != end; ++pos) + { + const char *name = pos->GetMangled().GetName(Mangled::ePreferDemangled).AsCString(); + if (name && name[0]) + name_map.insert (std::make_pair(name, &(*pos))); + } + + for (CStringToSymbol::const_iterator pos = name_map.begin(), end = name_map.end(); pos != end; ++pos) + { + s->Indent(); + pos->second->Dump (s, target, pos->second - &m_symbols[0]); + } + } + break; + + case eSortOrderByAddress: + s->PutCString (" (sorted by address):\n"); + DumpSymbolHeader (s); + if (!m_file_addr_to_index_computed) + InitAddressIndexes(); + const size_t num_entries = m_file_addr_to_index.GetSize(); + for (size_t i=0; i<num_entries; ++i) + { + s->Indent(); + const uint32_t symbol_idx = m_file_addr_to_index.GetEntryRef(i).data; + m_symbols[symbol_idx].Dump(s, target, symbol_idx); + } + break; + } + } +} + +void +Symtab::Dump(Stream *s, Target *target, std::vector<uint32_t>& indexes) const +{ + Mutex::Locker locker (m_mutex); + + const size_t num_symbols = GetNumSymbols(); + //s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + s->Printf("Symtab %lu symbol indexes (%lu symbols total):\n", indexes.size(), m_symbols.size()); + s->IndentMore(); + + if (!indexes.empty()) + { + std::vector<uint32_t>::const_iterator pos; + std::vector<uint32_t>::const_iterator end = indexes.end(); + DumpSymbolHeader (s); + for (pos = indexes.begin(); pos != end; ++pos) + { + size_t idx = *pos; + if (idx < num_symbols) + { + s->Indent(); + m_symbols[idx].Dump(s, target, idx); + } + } + } + s->IndentLess (); +} + +void +Symtab::DumpSymbolHeader (Stream *s) +{ + s->Indent(" Debug symbol\n"); + s->Indent(" |Synthetic symbol\n"); + s->Indent(" ||Externally Visible\n"); + s->Indent(" |||\n"); + s->Indent("Index UserID DSX Type File Address/Value Load Address Size Flags Name\n"); + s->Indent("------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ----------------------------------\n"); +} + + +static int +CompareSymbolID (const void *key, const void *p) +{ + const user_id_t match_uid = *(user_id_t*) key; + const user_id_t symbol_uid = ((Symbol *)p)->GetID(); + if (match_uid < symbol_uid) + return -1; + if (match_uid > symbol_uid) + return 1; + return 0; +} + +Symbol * +Symtab::FindSymbolByID (lldb::user_id_t symbol_uid) const +{ + Mutex::Locker locker (m_mutex); + + Symbol *symbol = (Symbol*)::bsearch (&symbol_uid, + &m_symbols[0], + m_symbols.size(), + (uint8_t *)&m_symbols[1] - (uint8_t *)&m_symbols[0], + CompareSymbolID); + return symbol; +} + + +Symbol * +Symtab::SymbolAtIndex(size_t idx) +{ + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + if (idx < m_symbols.size()) + return &m_symbols[idx]; + return NULL; +} + + +const Symbol * +Symtab::SymbolAtIndex(size_t idx) const +{ + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + if (idx < m_symbols.size()) + return &m_symbols[idx]; + return NULL; +} + +//---------------------------------------------------------------------- +// InitNameIndexes +//---------------------------------------------------------------------- +void +Symtab::InitNameIndexes() +{ + // Protected function, no need to lock mutex... + if (!m_name_indexes_computed) + { + m_name_indexes_computed = true; + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + // Create the name index vector to be able to quickly search by name + const size_t num_symbols = m_symbols.size(); +#if 1 + m_name_to_index.Reserve (num_symbols); +#else + // TODO: benchmark this to see if we save any memory. Otherwise we + // will always keep the memory reserved in the vector unless we pull + // some STL swap magic and then recopy... + uint32_t actual_count = 0; + for (const_iterator pos = m_symbols.begin(), end = m_symbols.end(); + pos != end; + ++pos) + { + const Mangled &mangled = pos->GetMangled(); + if (mangled.GetMangledName()) + ++actual_count; + + if (mangled.GetDemangledName()) + ++actual_count; + } + + m_name_to_index.Reserve (actual_count); +#endif + + NameToIndexMap::Entry entry; + + // The "const char *" in "class_contexts" must come from a ConstString::GetCString() + std::set<const char *> class_contexts; + UniqueCStringMap<uint32_t> mangled_name_to_index; + std::vector<const char *> symbol_contexts(num_symbols, NULL); + + for (entry.value = 0; entry.value<num_symbols; ++entry.value) + { + const Symbol *symbol = &m_symbols[entry.value]; + + // Don't let trampolines get into the lookup by name map + // If we ever need the trampoline symbols to be searchable by name + // we can remove this and then possibly add a new bool to any of the + // Symtab functions that lookup symbols by name to indicate if they + // want trampolines. + if (symbol->IsTrampoline()) + continue; + + const Mangled &mangled = symbol->GetMangled(); + entry.cstring = mangled.GetMangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + { + m_name_to_index.Append (entry); + + const SymbolType symbol_type = symbol->GetType(); + if (symbol_type == eSymbolTypeCode || symbol_type == eSymbolTypeResolver) + { + if (entry.cstring[0] == '_' && entry.cstring[1] == 'Z' && + (entry.cstring[2] != 'T' && // avoid virtual table, VTT structure, typeinfo structure, and typeinfo name + entry.cstring[2] != 'G' && // avoid guard variables + entry.cstring[2] != 'Z')) // named local entities (if we eventually handle eSymbolTypeData, we will want this back) + { + CPPLanguageRuntime::MethodName cxx_method (mangled.GetDemangledName()); + entry.cstring = ConstString(cxx_method.GetBasename()).GetCString(); + if (entry.cstring && entry.cstring[0]) + { + // ConstString objects permanently store the string in the pool so calling + // GetCString() on the value gets us a const char * that will never go away + const char *const_context = ConstString(cxx_method.GetContext()).GetCString(); + + if (entry.cstring[0] == '~' || !cxx_method.GetQualifiers().empty()) + { + // The first character of the demangled basename is '~' which + // means we have a class destructor. We can use this information + // to help us know what is a class and what isn't. + if (class_contexts.find(const_context) == class_contexts.end()) + class_contexts.insert(const_context); + m_method_to_index.Append (entry); + } + else + { + if (const_context && const_context[0]) + { + if (class_contexts.find(const_context) != class_contexts.end()) + { + // The current decl context is in our "class_contexts" which means + // this is a method on a class + m_method_to_index.Append (entry); + } + else + { + // We don't know if this is a function basename or a method, + // so put it into a temporary collection so once we are done + // we can look in class_contexts to see if each entry is a class + // or just a function and will put any remaining items into + // m_method_to_index or m_basename_to_index as needed + mangled_name_to_index.Append (entry); + symbol_contexts[entry.value] = const_context; + } + } + else + { + // No context for this function so this has to be a basename + m_basename_to_index.Append(entry); + } + } + } + } + } + } + + entry.cstring = mangled.GetDemangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + m_name_to_index.Append (entry); + + // If the demangled name turns out to be an ObjC name, and + // is a category name, add the version without categories to the index too. + ObjCLanguageRuntime::MethodName objc_method (entry.cstring, true); + if (objc_method.IsValid(true)) + { + entry.cstring = objc_method.GetSelector().GetCString(); + m_selector_to_index.Append (entry); + + ConstString objc_method_no_category (objc_method.GetFullNameWithoutCategory(true)); + if (objc_method_no_category) + { + entry.cstring = objc_method_no_category.GetCString(); + m_name_to_index.Append (entry); + } + } + + } + + size_t count; + if (!mangled_name_to_index.IsEmpty()) + { + count = mangled_name_to_index.GetSize(); + for (size_t i=0; i<count; ++i) + { + if (mangled_name_to_index.GetValueAtIndex(i, entry.value)) + { + entry.cstring = mangled_name_to_index.GetCStringAtIndex(i); + if (symbol_contexts[entry.value] && class_contexts.find(symbol_contexts[entry.value]) != class_contexts.end()) + { + m_method_to_index.Append (entry); + } + else + { + // If we got here, we have something that had a context (was inside a namespace or class) + // yet we don't know if the entry + m_method_to_index.Append (entry); + m_basename_to_index.Append (entry); + } + } + } + } + m_name_to_index.Sort(); + m_name_to_index.SizeToFit(); + m_selector_to_index.Sort(); + m_selector_to_index.SizeToFit(); + m_basename_to_index.Sort(); + m_basename_to_index.SizeToFit(); + m_method_to_index.Sort(); + m_method_to_index.SizeToFit(); + +// static StreamFile a ("/tmp/a.txt"); +// +// count = m_basename_to_index.GetSize(); +// if (count) +// { +// for (size_t i=0; i<count; ++i) +// { +// if (m_basename_to_index.GetValueAtIndex(i, entry.value)) +// a.Printf ("%s BASENAME\n", m_symbols[entry.value].GetMangled().GetName().GetCString()); +// } +// } +// count = m_method_to_index.GetSize(); +// if (count) +// { +// for (size_t i=0; i<count; ++i) +// { +// if (m_method_to_index.GetValueAtIndex(i, entry.value)) +// a.Printf ("%s METHOD\n", m_symbols[entry.value].GetMangled().GetName().GetCString()); +// } +// } + } +} + +void +Symtab::AppendSymbolNamesToMap (const IndexCollection &indexes, + bool add_demangled, + bool add_mangled, + NameToIndexMap &name_to_index_map) const +{ + if (add_demangled || add_mangled) + { + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + Mutex::Locker locker (m_mutex); + + // Create the name index vector to be able to quickly search by name + NameToIndexMap::Entry entry; + const size_t num_indexes = indexes.size(); + for (size_t i=0; i<num_indexes; ++i) + { + entry.value = indexes[i]; + assert (i < m_symbols.size()); + const Symbol *symbol = &m_symbols[entry.value]; + + const Mangled &mangled = symbol->GetMangled(); + if (add_demangled) + { + entry.cstring = mangled.GetDemangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + name_to_index_map.Append (entry); + } + + if (add_mangled) + { + entry.cstring = mangled.GetMangledName().GetCString(); + if (entry.cstring && entry.cstring[0]) + name_to_index_map.Append (entry); + } + } + } +} + +uint32_t +Symtab::AppendSymbolIndexesWithType (SymbolType symbol_type, std::vector<uint32_t>& indexes, uint32_t start_idx, uint32_t end_index) const +{ + Mutex::Locker locker (m_mutex); + + uint32_t prev_size = indexes.size(); + + const uint32_t count = std::min<uint32_t> (m_symbols.size(), end_index); + + for (uint32_t i = start_idx; i < count; ++i) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + indexes.push_back(i); + } + + return indexes.size() - prev_size; +} + +uint32_t +Symtab::AppendSymbolIndexesWithTypeAndFlagsValue (SymbolType symbol_type, uint32_t flags_value, std::vector<uint32_t>& indexes, uint32_t start_idx, uint32_t end_index) const +{ + Mutex::Locker locker (m_mutex); + + uint32_t prev_size = indexes.size(); + + const uint32_t count = std::min<uint32_t> (m_symbols.size(), end_index); + + for (uint32_t i = start_idx; i < count; ++i) + { + if ((symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) && m_symbols[i].GetFlags() == flags_value) + indexes.push_back(i); + } + + return indexes.size() - prev_size; +} + +uint32_t +Symtab::AppendSymbolIndexesWithType (SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& indexes, uint32_t start_idx, uint32_t end_index) const +{ + Mutex::Locker locker (m_mutex); + + uint32_t prev_size = indexes.size(); + + const uint32_t count = std::min<uint32_t> (m_symbols.size(), end_index); + + for (uint32_t i = start_idx; i < count; ++i) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + { + if (CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility)) + indexes.push_back(i); + } + } + + return indexes.size() - prev_size; +} + + +uint32_t +Symtab::GetIndexForSymbol (const Symbol *symbol) const +{ + const Symbol *first_symbol = &m_symbols[0]; + if (symbol >= first_symbol && symbol < first_symbol + m_symbols.size()) + return symbol - first_symbol; + return UINT32_MAX; +} + +struct SymbolSortInfo +{ + const bool sort_by_load_addr; + const Symbol *symbols; +}; + +namespace { + struct SymbolIndexComparator { + const std::vector<Symbol>& symbols; + std::vector<lldb::addr_t> &addr_cache; + + // Getting from the symbol to the Address to the File Address involves some work. + // Since there are potentially many symbols here, and we're using this for sorting so + // we're going to be computing the address many times, cache that in addr_cache. + // The array passed in has to be the same size as the symbols array passed into the + // member variable symbols, and should be initialized with LLDB_INVALID_ADDRESS. + // NOTE: You have to make addr_cache externally and pass it in because std::stable_sort + // makes copies of the comparator it is initially passed in, and you end up spending + // huge amounts of time copying this array... + + SymbolIndexComparator(const std::vector<Symbol>& s, std::vector<lldb::addr_t> &a) : symbols(s), addr_cache(a) { + assert (symbols.size() == addr_cache.size()); + } + bool operator()(uint32_t index_a, uint32_t index_b) { + addr_t value_a = addr_cache[index_a]; + if (value_a == LLDB_INVALID_ADDRESS) + { + value_a = symbols[index_a].GetAddress().GetFileAddress(); + addr_cache[index_a] = value_a; + } + + addr_t value_b = addr_cache[index_b]; + if (value_b == LLDB_INVALID_ADDRESS) + { + value_b = symbols[index_b].GetAddress().GetFileAddress(); + addr_cache[index_b] = value_b; + } + + + if (value_a == value_b) { + // The if the values are equal, use the original symbol user ID + lldb::user_id_t uid_a = symbols[index_a].GetID(); + lldb::user_id_t uid_b = symbols[index_b].GetID(); + if (uid_a < uid_b) + return true; + if (uid_a > uid_b) + return false; + return false; + } else if (value_a < value_b) + return true; + + return false; + } + }; +} + +void +Symtab::SortSymbolIndexesByValue (std::vector<uint32_t>& indexes, bool remove_duplicates) const +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__,__PRETTY_FUNCTION__); + // No need to sort if we have zero or one items... + if (indexes.size() <= 1) + return; + + // Sort the indexes in place using std::stable_sort. + // NOTE: The use of std::stable_sort instead of std::sort here is strictly for performance, + // not correctness. The indexes vector tends to be "close" to sorted, which the + // stable sort handles better. + + std::vector<lldb::addr_t> addr_cache(m_symbols.size(), LLDB_INVALID_ADDRESS); + + SymbolIndexComparator comparator(m_symbols, addr_cache); + std::stable_sort(indexes.begin(), indexes.end(), comparator); + + // Remove any duplicates if requested + if (remove_duplicates) + std::unique(indexes.begin(), indexes.end()); +} + +uint32_t +Symtab::AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + if (symbol_name) + { + const char *symbol_cstr = symbol_name.GetCString(); + if (!m_name_indexes_computed) + InitNameIndexes(); + + return m_name_to_index.GetValues (symbol_cstr, indexes); + } + return 0; +} + +uint32_t +Symtab::AppendSymbolIndexesWithName (const ConstString& symbol_name, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + if (symbol_name) + { + const size_t old_size = indexes.size(); + if (!m_name_indexes_computed) + InitNameIndexes(); + + const char *symbol_cstr = symbol_name.GetCString(); + + std::vector<uint32_t> all_name_indexes; + const size_t name_match_count = m_name_to_index.GetValues (symbol_cstr, all_name_indexes); + for (size_t i=0; i<name_match_count; ++i) + { + if (CheckSymbolAtIndex(all_name_indexes[i], symbol_debug_type, symbol_visibility)) + indexes.push_back (all_name_indexes[i]); + } + return indexes.size() - old_size; + } + return 0; +} + +uint32_t +Symtab::AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, SymbolType symbol_type, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0) + { + std::vector<uint32_t>::iterator pos = indexes.begin(); + while (pos != indexes.end()) + { + if (symbol_type == eSymbolTypeAny || m_symbols[*pos].GetType() == symbol_type) + ++pos; + else + indexes.erase(pos); + } + } + return indexes.size(); +} + +uint32_t +Symtab::AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + if (AppendSymbolIndexesWithName(symbol_name, symbol_debug_type, symbol_visibility, indexes) > 0) + { + std::vector<uint32_t>::iterator pos = indexes.begin(); + while (pos != indexes.end()) + { + if (symbol_type == eSymbolTypeAny || m_symbols[*pos].GetType() == symbol_type) + ++pos; + else + indexes.erase(pos); + } + } + return indexes.size(); +} + + +uint32_t +Symtab::AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®exp, SymbolType symbol_type, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + uint32_t prev_size = indexes.size(); + uint32_t sym_end = m_symbols.size(); + + for (uint32_t i = 0; i < sym_end; i++) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + { + const char *name = m_symbols[i].GetMangled().GetName().AsCString(); + if (name) + { + if (regexp.Execute (name)) + indexes.push_back(i); + } + } + } + return indexes.size() - prev_size; + +} + +uint32_t +Symtab::AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®exp, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& indexes) +{ + Mutex::Locker locker (m_mutex); + + uint32_t prev_size = indexes.size(); + uint32_t sym_end = m_symbols.size(); + + for (uint32_t i = 0; i < sym_end; i++) + { + if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) + { + if (CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility) == false) + continue; + + const char *name = m_symbols[i].GetMangled().GetName().AsCString(); + if (name) + { + if (regexp.Execute (name)) + indexes.push_back(i); + } + } + } + return indexes.size() - prev_size; + +} + +Symbol * +Symtab::FindSymbolWithType (SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t& start_idx) +{ + Mutex::Locker locker (m_mutex); + + const size_t count = m_symbols.size(); + for (size_t idx = start_idx; idx < count; ++idx) + { + if (symbol_type == eSymbolTypeAny || m_symbols[idx].GetType() == symbol_type) + { + if (CheckSymbolAtIndex(idx, symbol_debug_type, symbol_visibility)) + { + start_idx = idx; + return &m_symbols[idx]; + } + } + } + return NULL; +} + +size_t +Symtab::FindAllSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, std::vector<uint32_t>& symbol_indexes) +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + // Initialize all of the lookup by name indexes before converting NAME + // to a uniqued string NAME_STR below. + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (name) + { + // The string table did have a string that matched, but we need + // to check the symbols and match the symbol_type if any was given. + AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_indexes); + } + return symbol_indexes.size(); +} + +size_t +Symtab::FindAllSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& symbol_indexes) +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + // Initialize all of the lookup by name indexes before converting NAME + // to a uniqued string NAME_STR below. + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (name) + { + // The string table did have a string that matched, but we need + // to check the symbols and match the symbol_type if any was given. + AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_debug_type, symbol_visibility, symbol_indexes); + } + return symbol_indexes.size(); +} + +size_t +Symtab::FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector<uint32_t>& symbol_indexes) +{ + Mutex::Locker locker (m_mutex); + + AppendSymbolIndexesMatchingRegExAndType(regex, symbol_type, symbol_debug_type, symbol_visibility, symbol_indexes); + return symbol_indexes.size(); +} + +Symbol * +Symtab::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility) +{ + Mutex::Locker locker (m_mutex); + + Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (name) + { + std::vector<uint32_t> matching_indexes; + // The string table did have a string that matched, but we need + // to check the symbols and match the symbol_type if any was given. + if (AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_debug_type, symbol_visibility, matching_indexes)) + { + std::vector<uint32_t>::const_iterator pos, end = matching_indexes.end(); + for (pos = matching_indexes.begin(); pos != end; ++pos) + { + Symbol *symbol = SymbolAtIndex(*pos); + + if (symbol->Compare(name, symbol_type)) + return symbol; + } + } + } + return NULL; +} + +typedef struct +{ + const Symtab *symtab; + const addr_t file_addr; + Symbol *match_symbol; + const uint32_t *match_index_ptr; + addr_t match_offset; +} SymbolSearchInfo; + +static int +SymbolWithFileAddress (SymbolSearchInfo *info, const uint32_t *index_ptr) +{ + const Symbol *curr_symbol = info->symtab->SymbolAtIndex (index_ptr[0]); + if (curr_symbol == NULL) + return -1; + + const addr_t info_file_addr = info->file_addr; + + // lldb::Symbol::GetAddressRangePtr() will only return a non NULL address + // range if the symbol has a section! + if (curr_symbol->ValueIsAddress()) + { + const addr_t curr_file_addr = curr_symbol->GetAddress().GetFileAddress(); + if (info_file_addr < curr_file_addr) + return -1; + if (info_file_addr > curr_file_addr) + return +1; + info->match_symbol = const_cast<Symbol *>(curr_symbol); + info->match_index_ptr = index_ptr; + return 0; + } + + return -1; +} + +static int +SymbolWithClosestFileAddress (SymbolSearchInfo *info, const uint32_t *index_ptr) +{ + const Symbol *symbol = info->symtab->SymbolAtIndex (index_ptr[0]); + if (symbol == NULL) + return -1; + + const addr_t info_file_addr = info->file_addr; + if (symbol->ValueIsAddress()) + { + const addr_t curr_file_addr = symbol->GetAddress().GetFileAddress(); + if (info_file_addr < curr_file_addr) + return -1; + + // Since we are finding the closest symbol that is greater than or equal + // to 'info->file_addr' we set the symbol here. This will get set + // multiple times, but after the search is done it will contain the best + // symbol match + info->match_symbol = const_cast<Symbol *>(symbol); + info->match_index_ptr = index_ptr; + info->match_offset = info_file_addr - curr_file_addr; + + if (info_file_addr > curr_file_addr) + return +1; + return 0; + } + return -1; +} + +static SymbolSearchInfo +FindIndexPtrForSymbolContainingAddress(Symtab* symtab, addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes) +{ + SymbolSearchInfo info = { symtab, file_addr, NULL, NULL, 0 }; + ::bsearch (&info, + indexes, + num_indexes, + sizeof(uint32_t), + (ComparisonFunction)SymbolWithClosestFileAddress); + return info; +} + + +void +Symtab::InitAddressIndexes() +{ + // Protected function, no need to lock mutex... + if (!m_file_addr_to_index_computed && !m_symbols.empty()) + { + m_file_addr_to_index_computed = true; + + FileRangeToIndexMap::Entry entry; + const_iterator begin = m_symbols.begin(); + const_iterator end = m_symbols.end(); + for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) + { + if (pos->ValueIsAddress()) + { + entry.SetRangeBase(pos->GetAddress().GetFileAddress()); + entry.SetByteSize(pos->GetByteSize()); + entry.data = std::distance(begin, pos); + m_file_addr_to_index.Append(entry); + } + } + const size_t num_entries = m_file_addr_to_index.GetSize(); + if (num_entries > 0) + { + m_file_addr_to_index.Sort(); + m_file_addr_to_index.CalculateSizesOfZeroByteSizeRanges(); + + // Now our last symbols might not have had sizes because there + // was no subsequent symbol to calculate the size from. If this is + // the case, then calculate the size by capping it at the end of the + // section in which the symbol resides + for (int i = num_entries - 1; i >= 0; --i) + { + const FileRangeToIndexMap::Entry &entry = m_file_addr_to_index.GetEntryRef(i); + // As we iterate backwards, as soon as we find a symbol with a valid + // byte size, we are done + if (entry.GetByteSize() > 0) + break; + + // Cap the size to the end of the section in which the symbol resides + SectionSP section_sp (m_objfile->GetSectionList()->FindSectionContainingFileAddress (entry.GetRangeBase())); + if (section_sp) + { + const lldb::addr_t end_section_file_addr = section_sp->GetFileAddress() + section_sp->GetByteSize(); + const lldb::addr_t symbol_file_addr = entry.GetRangeBase(); + if (end_section_file_addr > symbol_file_addr) + { + Symbol &symbol = m_symbols[entry.data]; + + symbol.SetByteSize(end_section_file_addr - symbol_file_addr); + symbol.SetSizeIsSynthesized(true); + } + } + } + // Sort again in case the range size changes the ordering + m_file_addr_to_index.Sort(); + } + } +} + +void +Symtab::CalculateSymbolSizes () +{ + Mutex::Locker locker (m_mutex); + + if (!m_symbols.empty()) + { + if (!m_file_addr_to_index_computed) + InitAddressIndexes(); + + const size_t num_entries = m_file_addr_to_index.GetSize(); + + for (size_t i = 0; i < num_entries; ++i) + { + // The entries in the m_file_addr_to_index have calculated the sizes already + // so we will use this size if we need to. + const FileRangeToIndexMap::Entry &entry = m_file_addr_to_index.GetEntryRef(i); + + Symbol &symbol = m_symbols[entry.data]; + + // If the symbol size is already valid, no need to do anything + if (symbol.GetByteSizeIsValid()) + continue; + + const addr_t range_size = entry.GetByteSize(); + if (range_size > 0) + { + symbol.SetByteSize(range_size); + symbol.SetSizeIsSynthesized(true); + } + } + } +} + +Symbol * +Symtab::FindSymbolContainingFileAddress (addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes) +{ + Mutex::Locker locker (m_mutex); + + + SymbolSearchInfo info = { this, file_addr, NULL, NULL, 0 }; + + ::bsearch (&info, + indexes, + num_indexes, + sizeof(uint32_t), + (ComparisonFunction)SymbolWithClosestFileAddress); + + if (info.match_symbol) + { + if (info.match_offset == 0) + { + // We found an exact match! + return info.match_symbol; + } + + const size_t symbol_byte_size = info.match_symbol->GetByteSize(); + + if (symbol_byte_size == 0) + { + // We weren't able to find the size of the symbol so lets just go + // with that match we found in our search... + return info.match_symbol; + } + + // We were able to figure out a symbol size so lets make sure our + // offset puts "file_addr" in the symbol's address range. + if (info.match_offset < symbol_byte_size) + return info.match_symbol; + } + return NULL; +} + +Symbol * +Symtab::FindSymbolContainingFileAddress (addr_t file_addr) +{ + Mutex::Locker locker (m_mutex); + + if (!m_file_addr_to_index_computed) + InitAddressIndexes(); + + const FileRangeToIndexMap::Entry *entry = m_file_addr_to_index.FindEntryThatContains(file_addr); + if (entry) + return SymbolAtIndex(entry->data); + return NULL; +} + +void +Symtab::SymbolIndicesToSymbolContextList (std::vector<uint32_t> &symbol_indexes, SymbolContextList &sc_list) +{ + // No need to protect this call using m_mutex all other method calls are + // already thread safe. + + const bool merge_symbol_into_function = true; + size_t num_indices = symbol_indexes.size(); + if (num_indices > 0) + { + SymbolContext sc; + sc.module_sp = m_objfile->GetModule(); + for (size_t i = 0; i < num_indices; i++) + { + sc.symbol = SymbolAtIndex (symbol_indexes[i]); + if (sc.symbol) + sc_list.AppendIfUnique(sc, merge_symbol_into_function); + } + } +} + + +size_t +Symtab::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + size_t count = 0; + std::vector<uint32_t> symbol_indexes; + + const char *name_cstr = name.GetCString(); + + // eFunctionNameTypeAuto should be pre-resolved by a call to Module::PrepareForFunctionNameLookup() + assert ((name_type_mask & eFunctionNameTypeAuto) == 0); + + if (name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeFull)) + { + std::vector<uint32_t> temp_symbol_indexes; + FindAllSymbolsWithNameAndType (name, eSymbolTypeAny, temp_symbol_indexes); + + unsigned temp_symbol_indexes_size = temp_symbol_indexes.size(); + if (temp_symbol_indexes_size > 0) + { + Mutex::Locker locker (m_mutex); + for (unsigned i = 0; i < temp_symbol_indexes_size; i++) + { + SymbolContext sym_ctx; + sym_ctx.symbol = SymbolAtIndex (temp_symbol_indexes[i]); + if (sym_ctx.symbol) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeResolver: + symbol_indexes.push_back(temp_symbol_indexes[i]); + break; + default: + break; + } + } + } + } + } + + if (name_type_mask & eFunctionNameTypeBase) + { + // From mangled names we can't tell what is a basename and what + // is a method name, so we just treat them the same + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (!m_basename_to_index.IsEmpty()) + { + const UniqueCStringMap<uint32_t>::Entry *match; + for (match = m_basename_to_index.FindFirstValueForName(name_cstr); + match != NULL; + match = m_basename_to_index.FindNextValueForName(match)) + { + symbol_indexes.push_back(match->value); + } + } + } + + if (name_type_mask & eFunctionNameTypeMethod) + { + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (!m_method_to_index.IsEmpty()) + { + const UniqueCStringMap<uint32_t>::Entry *match; + for (match = m_method_to_index.FindFirstValueForName(name_cstr); + match != NULL; + match = m_method_to_index.FindNextValueForName(match)) + { + symbol_indexes.push_back(match->value); + } + } + } + + if (name_type_mask & eFunctionNameTypeSelector) + { + if (!m_name_indexes_computed) + InitNameIndexes(); + + if (!m_selector_to_index.IsEmpty()) + { + const UniqueCStringMap<uint32_t>::Entry *match; + for (match = m_selector_to_index.FindFirstValueForName(name_cstr); + match != NULL; + match = m_selector_to_index.FindNextValueForName(match)) + { + symbol_indexes.push_back(match->value); + } + } + } + + if (!symbol_indexes.empty()) + { + std::sort(symbol_indexes.begin(), symbol_indexes.end()); + symbol_indexes.erase(std::unique(symbol_indexes.begin(), symbol_indexes.end()), symbol_indexes.end()); + count = symbol_indexes.size(); + SymbolIndicesToSymbolContextList (symbol_indexes, sc_list); + } + + return count; +} + diff --git a/source/Symbol/Type.cpp b/source/Symbol/Type.cpp new file mode 100644 index 000000000000..0af2359e1dad --- /dev/null +++ b/source/Symbol/Type.cpp @@ -0,0 +1,990 @@ +//===-- Type.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Other libraries and framework includes + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContextScope.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +class TypeAppendVisitor +{ +public: + TypeAppendVisitor(TypeListImpl &type_list) : + m_type_list(type_list) + { + } + + bool + operator() (const lldb::TypeSP& type) + { + m_type_list.Append(TypeImplSP(new TypeImpl(type))); + return true; + } + +private: + TypeListImpl &m_type_list; +}; + +void +TypeListImpl::Append (const lldb_private::TypeList &type_list) +{ + TypeAppendVisitor cb(*this); + type_list.ForEach(cb); +} + + +Type * +SymbolFileType::GetType () +{ + if (!m_type_sp) + { + Type *resolved_type = m_symbol_file.ResolveTypeUID (GetID()); + if (resolved_type) + m_type_sp = resolved_type->shared_from_this(); + } + return m_type_sp.get(); +} + + +Type::Type +( + lldb::user_id_t uid, + SymbolFile* symbol_file, + const ConstString &name, + uint64_t byte_size, + SymbolContextScope *context, + user_id_t encoding_uid, + EncodingDataType encoding_uid_type, + const Declaration& decl, + const ClangASTType &clang_type, + ResolveState clang_type_resolve_state +) : + std::enable_shared_from_this<Type> (), + UserID (uid), + m_name (name), + m_symbol_file (symbol_file), + m_context (context), + m_encoding_type (NULL), + m_encoding_uid (encoding_uid), + m_encoding_uid_type (encoding_uid_type), + m_byte_size (byte_size), + m_decl (decl), + m_clang_type (clang_type) +{ + m_flags.clang_type_resolve_state = (clang_type ? clang_type_resolve_state : eResolveStateUnresolved); + m_flags.is_complete_objc_class = false; +} + +Type::Type () : + std::enable_shared_from_this<Type> (), + UserID (0), + m_name ("<INVALID TYPE>"), + m_symbol_file (NULL), + m_context (NULL), + m_encoding_type (NULL), + m_encoding_uid (LLDB_INVALID_UID), + m_encoding_uid_type (eEncodingInvalid), + m_byte_size (0), + m_decl (), + m_clang_type () +{ + m_flags.clang_type_resolve_state = eResolveStateUnresolved; + m_flags.is_complete_objc_class = false; +} + + +Type::Type (const Type &rhs) : + std::enable_shared_from_this<Type> (rhs), + UserID (rhs), + m_name (rhs.m_name), + m_symbol_file (rhs.m_symbol_file), + m_context (rhs.m_context), + m_encoding_type (rhs.m_encoding_type), + m_encoding_uid (rhs.m_encoding_uid), + m_encoding_uid_type (rhs.m_encoding_uid_type), + m_byte_size (rhs.m_byte_size), + m_decl (rhs.m_decl), + m_clang_type (rhs.m_clang_type), + m_flags (rhs.m_flags) +{ +} + +const Type& +Type::operator= (const Type& rhs) +{ + if (this != &rhs) + { + } + return *this; +} + + +void +Type::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_name) +{ + *s << "id = " << (const UserID&)*this; + + // Call the name accessor to make sure we resolve the type name + if (show_name) + { + const ConstString &type_name = GetName(); + if (type_name) + { + *s << ", name = \"" << type_name << '"'; + ConstString qualified_type_name (GetQualifiedName()); + if (qualified_type_name != type_name) + { + *s << ", qualified = \"" << qualified_type_name << '"'; + } + } + } + + // Call the get byte size accesor so we resolve our byte size + if (GetByteSize()) + s->Printf(", byte-size = %" PRIu64, m_byte_size); + bool show_fullpaths = (level == lldb::eDescriptionLevelVerbose); + m_decl.Dump(s, show_fullpaths); + + if (m_clang_type.IsValid()) + { + *s << ", clang_type = \""; + GetClangForwardType().DumpTypeDescription(s); + *s << '"'; + } + else if (m_encoding_uid != LLDB_INVALID_UID) + { + s->Printf(", type_uid = 0x%8.8" PRIx64, m_encoding_uid); + switch (m_encoding_uid_type) + { + case eEncodingInvalid: break; + case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; + case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; + case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; + case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; + case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; + case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; + case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; + case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; + case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + } + } +} + + +void +Type::Dump (Stream *s, bool show_context) +{ + s->Printf("%p: ", this); + s->Indent(); + *s << "Type" << (const UserID&)*this << ' '; + if (m_name) + *s << ", name = \"" << m_name << "\""; + + if (m_byte_size != 0) + s->Printf(", size = %" PRIu64, m_byte_size); + + if (show_context && m_context != NULL) + { + s->PutCString(", context = ( "); + m_context->DumpSymbolContext(s); + s->PutCString(" )"); + } + + bool show_fullpaths = false; + m_decl.Dump (s,show_fullpaths); + + if (m_clang_type.IsValid()) + { + *s << ", clang_type = " << m_clang_type.GetOpaqueQualType() << ' '; + GetClangForwardType().DumpTypeDescription (s); + } + else if (m_encoding_uid != LLDB_INVALID_UID) + { + *s << ", type_data = " << (uint64_t)m_encoding_uid; + switch (m_encoding_uid_type) + { + case eEncodingInvalid: break; + case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; + case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; + case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; + case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; + case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; + case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; + case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; + case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; + case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; + } + } + +// +// if (m_access) +// s->Printf(", access = %u", m_access); + s->EOL(); +} + +const ConstString & +Type::GetName() +{ + if (!m_name) + m_name = GetClangForwardType().GetConstTypeName(); + return m_name; +} + +void +Type::DumpTypeName(Stream *s) +{ + GetName().Dump(s, "<invalid-type-name>"); +} + + +void +Type::DumpValue +( + ExecutionContext *exe_ctx, + Stream *s, + const DataExtractor &data, + uint32_t data_byte_offset, + bool show_types, + bool show_summary, + bool verbose, + lldb::Format format +) +{ + if (ResolveClangType(eResolveStateForward)) + { + if (show_types) + { + s->PutChar('('); + if (verbose) + s->Printf("Type{0x%8.8" PRIx64 "} ", GetID()); + DumpTypeName (s); + s->PutCString(") "); + } + + GetClangForwardType().DumpValue (exe_ctx, + s, + format == lldb::eFormatDefault ? GetFormat() : format, + data, + data_byte_offset, + GetByteSize(), + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, + show_summary, + verbose, + 0); + } +} + +Type * +Type::GetEncodingType () +{ + if (m_encoding_type == NULL && m_encoding_uid != LLDB_INVALID_UID) + m_encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); + return m_encoding_type; +} + + + +uint64_t +Type::GetByteSize() +{ + if (m_byte_size == 0) + { + switch (m_encoding_uid_type) + { + case eEncodingInvalid: + case eEncodingIsSyntheticUID: + break; + case eEncodingIsUID: + case eEncodingIsConstUID: + case eEncodingIsRestrictUID: + case eEncodingIsVolatileUID: + case eEncodingIsTypedefUID: + { + Type *encoding_type = GetEncodingType (); + if (encoding_type) + m_byte_size = encoding_type->GetByteSize(); + if (m_byte_size == 0) + m_byte_size = GetClangLayoutType().GetByteSize(); + } + break; + + // If we are a pointer or reference, then this is just a pointer size; + case eEncodingIsPointerUID: + case eEncodingIsLValueReferenceUID: + case eEncodingIsRValueReferenceUID: + m_byte_size = m_symbol_file->GetClangASTContext().GetPointerByteSize(); + break; + } + } + return m_byte_size; +} + + +uint32_t +Type::GetNumChildren (bool omit_empty_base_classes) +{ + return GetClangForwardType().GetNumChildren(omit_empty_base_classes); +} + +bool +Type::IsAggregateType () +{ + return GetClangForwardType().IsAggregateType(); +} + +lldb::TypeSP +Type::GetTypedefType() +{ + lldb::TypeSP type_sp; + if (IsTypedef()) + { + Type *typedef_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); + if (typedef_type) + type_sp = typedef_type->shared_from_this(); + } + return type_sp; +} + + + +lldb::Format +Type::GetFormat () +{ + return GetClangForwardType().GetFormat(); +} + + + +lldb::Encoding +Type::GetEncoding (uint64_t &count) +{ + // Make sure we resolve our type if it already hasn't been. + return GetClangForwardType().GetEncoding(count); +} + +bool +Type::DumpValueInMemory +( + ExecutionContext *exe_ctx, + Stream *s, + lldb::addr_t address, + AddressType address_type, + bool show_types, + bool show_summary, + bool verbose +) +{ + if (address != LLDB_INVALID_ADDRESS) + { + DataExtractor data; + Target *target = NULL; + if (exe_ctx) + target = exe_ctx->GetTargetPtr(); + if (target) + data.SetByteOrder (target->GetArchitecture().GetByteOrder()); + if (ReadFromMemory (exe_ctx, address, address_type, data)) + { + DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose); + return true; + } + } + return false; +} + + +bool +Type::ReadFromMemory (ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) +{ + if (address_type == eAddressTypeFile) + { + // Can't convert a file address to anything valid without more + // context (which Module it came from) + return false; + } + + const uint64_t byte_size = GetByteSize(); + if (data.GetByteSize() < byte_size) + { + lldb::DataBufferSP data_sp(new DataBufferHeap (byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = (uint8_t*)data.PeekData(0, byte_size); + if (dst != NULL) + { + if (address_type == eAddressTypeHost) + { + // The address is an address in this process, so just copy it + if (addr == 0) + return false; + memcpy (dst, (uint8_t*)NULL + addr, byte_size); + return true; + } + else + { + if (exe_ctx) + { + Process *process = exe_ctx->GetProcessPtr(); + if (process) + { + Error error; + return exe_ctx->GetProcessPtr()->ReadMemory(addr, dst, byte_size, error) == byte_size; + } + } + } + } + return false; +} + + +bool +Type::WriteToMemory (ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) +{ + return false; +} + + +TypeList* +Type::GetTypeList() +{ + return GetSymbolFile()->GetTypeList(); +} + +const Declaration & +Type::GetDeclaration () const +{ + return m_decl; +} + +bool +Type::ResolveClangType (ResolveState clang_type_resolve_state) +{ + Type *encoding_type = NULL; + if (!m_clang_type.IsValid()) + { + encoding_type = GetEncodingType(); + if (encoding_type) + { + switch (m_encoding_uid_type) + { + case eEncodingIsUID: + { + ClangASTType encoding_clang_type = encoding_type->GetClangForwardType(); + if (encoding_clang_type.IsValid()) + { + m_clang_type = encoding_clang_type; + m_flags.clang_type_resolve_state = encoding_type->m_flags.clang_type_resolve_state; + } + } + break; + + case eEncodingIsConstUID: + m_clang_type = encoding_type->GetClangForwardType().AddConstModifier(); + break; + + case eEncodingIsRestrictUID: + m_clang_type = encoding_type->GetClangForwardType().AddRestrictModifier(); + break; + + case eEncodingIsVolatileUID: + m_clang_type = encoding_type->GetClangForwardType().AddVolatileModifier(); + break; + + case eEncodingIsTypedefUID: + m_clang_type = encoding_type->GetClangForwardType().CreateTypedefType (GetName().AsCString(), + GetSymbolFile()->GetClangDeclContextContainingTypeUID(GetID())); + m_name.Clear(); + break; + + case eEncodingIsPointerUID: + m_clang_type = encoding_type->GetClangForwardType().GetPointerType(); + break; + + case eEncodingIsLValueReferenceUID: + m_clang_type = encoding_type->GetClangForwardType().GetLValueReferenceType(); + break; + + case eEncodingIsRValueReferenceUID: + m_clang_type = encoding_type->GetClangForwardType().GetRValueReferenceType(); + break; + + default: + assert(!"Unhandled encoding_data_type."); + break; + } + } + else + { + // We have no encoding type, return void? + ClangASTType void_clang_type (ClangASTContext::GetBasicType(GetClangASTContext().getASTContext(), eBasicTypeVoid)); + switch (m_encoding_uid_type) + { + case eEncodingIsUID: + m_clang_type = void_clang_type; + break; + + case eEncodingIsConstUID: + m_clang_type = void_clang_type.AddConstModifier (); + break; + + case eEncodingIsRestrictUID: + m_clang_type = void_clang_type.AddRestrictModifier (); + break; + + case eEncodingIsVolatileUID: + m_clang_type = void_clang_type.AddVolatileModifier (); + break; + + case eEncodingIsTypedefUID: + m_clang_type = void_clang_type.CreateTypedefType (GetName().AsCString(), + GetSymbolFile()->GetClangDeclContextContainingTypeUID(GetID())); + break; + + case eEncodingIsPointerUID: + m_clang_type = void_clang_type.GetPointerType (); + break; + + case eEncodingIsLValueReferenceUID: + m_clang_type = void_clang_type.GetLValueReferenceType (); + break; + + case eEncodingIsRValueReferenceUID: + m_clang_type = void_clang_type.GetRValueReferenceType (); + break; + + default: + assert(!"Unhandled encoding_data_type."); + break; + } + } + } + + // Check if we have a forward reference to a class/struct/union/enum? + if (m_clang_type.IsValid() && m_flags.clang_type_resolve_state < clang_type_resolve_state) + { + m_flags.clang_type_resolve_state = eResolveStateFull; + if (!m_clang_type.IsDefined ()) + { + // We have a forward declaration, we need to resolve it to a complete definition. + m_symbol_file->ResolveClangOpaqueTypeDefinition (m_clang_type); + } + } + + // If we have an encoding type, then we need to make sure it is + // resolved appropriately. + if (m_encoding_uid != LLDB_INVALID_UID) + { + if (encoding_type == NULL) + encoding_type = GetEncodingType(); + if (encoding_type) + { + ResolveState encoding_clang_type_resolve_state = clang_type_resolve_state; + + if (clang_type_resolve_state == eResolveStateLayout) + { + switch (m_encoding_uid_type) + { + case eEncodingIsPointerUID: + case eEncodingIsLValueReferenceUID: + case eEncodingIsRValueReferenceUID: + encoding_clang_type_resolve_state = eResolveStateForward; + break; + default: + break; + } + } + encoding_type->ResolveClangType (encoding_clang_type_resolve_state); + } + } + return m_clang_type.IsValid(); +} +uint32_t +Type::GetEncodingMask () +{ + uint32_t encoding_mask = 1u << m_encoding_uid_type; + Type *encoding_type = GetEncodingType(); + assert (encoding_type != this); + if (encoding_type) + encoding_mask |= encoding_type->GetEncodingMask (); + return encoding_mask; +} + +ClangASTType +Type::GetClangFullType () +{ + ResolveClangType(eResolveStateFull); + return m_clang_type; +} + +ClangASTType +Type::GetClangLayoutType () +{ + ResolveClangType(eResolveStateLayout); + return m_clang_type; +} + +ClangASTType +Type::GetClangForwardType () +{ + ResolveClangType (eResolveStateForward); + return m_clang_type; +} + +ClangASTContext & +Type::GetClangASTContext () +{ + return m_symbol_file->GetClangASTContext(); +} + +int +Type::Compare(const Type &a, const Type &b) +{ + // Just compare the UID values for now... + lldb::user_id_t a_uid = a.GetID(); + lldb::user_id_t b_uid = b.GetID(); + if (a_uid < b_uid) + return -1; + if (a_uid > b_uid) + return 1; + return 0; +// if (a.getQualType() == b.getQualType()) +// return 0; +} + + +#if 0 // START REMOVE +// Move this into ClangASTType +void * +Type::CreateClangPointerType (Type *type) +{ + assert(type); + return GetClangASTContext().CreatePointerType(type->GetClangForwardType()); +} + +void * +Type::CreateClangTypedefType (Type *typedef_type, Type *base_type) +{ + assert(typedef_type && base_type); + return GetClangASTContext().CreateTypedefType (typedef_type->GetName().AsCString(), + base_type->GetClangForwardType(), + typedef_type->GetSymbolFile()->GetClangDeclContextContainingTypeUID(typedef_type->GetID())); +} + +void * +Type::CreateClangLValueReferenceType (Type *type) +{ + assert(type); + return GetClangASTContext().CreateLValueReferenceType(type->GetClangForwardType()); +} + +void * +Type::CreateClangRValueReferenceType (Type *type) +{ + assert(type); + return GetClangASTContext().CreateRValueReferenceType (type->GetClangForwardType()); +} +#endif // END REMOVE + +bool +Type::IsRealObjCClass() +{ + // For now we are just skipping ObjC classes that get made by hand from the runtime, because + // those don't have any information. We could extend this to only return true for "full + // definitions" if we can figure that out. + + if (m_clang_type.IsObjCObjectOrInterfaceType() && GetByteSize() != 0) + return true; + else + return false; +} + +ConstString +Type::GetQualifiedName () +{ + return GetClangForwardType().GetConstTypeName(); +} + + +bool +Type::GetTypeScopeAndBasename (const char* &name_cstr, + std::string &scope, + std::string &basename, + TypeClass &type_class) +{ + // Protect against null c string. + + type_class = eTypeClassAny; + + if (name_cstr && name_cstr[0]) + { + llvm::StringRef name_strref(name_cstr); + if (name_strref.startswith("struct ")) + { + name_cstr += 7; + type_class = eTypeClassStruct; + } + else if (name_strref.startswith("class ")) + { + name_cstr += 6; + type_class = eTypeClassClass; + } + else if (name_strref.startswith("union ")) + { + name_cstr += 6; + type_class = eTypeClassUnion; + } + else if (name_strref.startswith("enum ")) + { + name_cstr += 5; + type_class = eTypeClassEnumeration; + } + else if (name_strref.startswith("typedef ")) + { + name_cstr += 8; + type_class = eTypeClassTypedef; + } + const char *basename_cstr = name_cstr; + const char* namespace_separator = ::strstr (basename_cstr, "::"); + if (namespace_separator) + { + const char* template_arg_char = ::strchr (basename_cstr, '<'); + while (namespace_separator != NULL) + { + if (template_arg_char && namespace_separator > template_arg_char) // but namespace'd template arguments are still good to go + break; + basename_cstr = namespace_separator + 2; + namespace_separator = strstr(basename_cstr, "::"); + } + if (basename_cstr > name_cstr) + { + scope.assign (name_cstr, basename_cstr - name_cstr); + basename.assign (basename_cstr); + return true; + } + } + } + return false; +} + + + + +TypeAndOrName::TypeAndOrName () : m_type_sp(), m_type_name() +{ + +} + +TypeAndOrName::TypeAndOrName (TypeSP &in_type_sp) : m_type_sp(in_type_sp) +{ + if (in_type_sp) + m_type_name = in_type_sp->GetName(); +} + +TypeAndOrName::TypeAndOrName (const char *in_type_str) : m_type_name(in_type_str) +{ +} + +TypeAndOrName::TypeAndOrName (const TypeAndOrName &rhs) : m_type_sp (rhs.m_type_sp), m_type_name (rhs.m_type_name) +{ + +} + +TypeAndOrName::TypeAndOrName (ConstString &in_type_const_string) : m_type_name (in_type_const_string) +{ +} + +TypeAndOrName & +TypeAndOrName::operator= (const TypeAndOrName &rhs) +{ + if (this != &rhs) + { + m_type_name = rhs.m_type_name; + m_type_sp = rhs.m_type_sp; + } + return *this; +} + +bool +TypeAndOrName::operator==(const TypeAndOrName &other) const +{ + if (m_type_sp != other.m_type_sp) + return false; + if (m_type_name != other.m_type_name) + return false; + return true; +} + +bool +TypeAndOrName::operator!=(const TypeAndOrName &other) const +{ + if (m_type_sp != other.m_type_sp) + return true; + if (m_type_name != other.m_type_name) + return true; + return false; +} + +ConstString +TypeAndOrName::GetName () const +{ + if (m_type_sp) + return m_type_sp->GetName(); + else + return m_type_name; +} + +void +TypeAndOrName::SetName (const ConstString &type_name) +{ + m_type_name = type_name; +} + +void +TypeAndOrName::SetName (const char *type_name_cstr) +{ + m_type_name.SetCString (type_name_cstr); +} + +void +TypeAndOrName::SetTypeSP (lldb::TypeSP type_sp) +{ + m_type_sp = type_sp; + if (type_sp) + m_type_name = type_sp->GetName(); +} + +bool +TypeAndOrName::IsEmpty() +{ + if (m_type_name || m_type_sp) + return false; + else + return true; +} + +void +TypeAndOrName::Clear () +{ + m_type_name.Clear(); + m_type_sp.reset(); +} + +bool +TypeAndOrName::HasName () +{ + return (bool)m_type_name; +} + +bool +TypeAndOrName::HasTypeSP () +{ + return m_type_sp.get() != NULL; +} + +TypeImpl::TypeImpl(const lldb_private::ClangASTType& clang_ast_type) : + m_clang_ast_type(clang_ast_type), + m_type_sp() +{ +} + +TypeImpl::TypeImpl(const lldb::TypeSP& type) : + m_clang_ast_type(type->GetClangForwardType()), + m_type_sp(type) +{ +} + +void +TypeImpl::SetType (const lldb::TypeSP &type_sp) +{ + if (type_sp) + { + m_clang_ast_type = type_sp->GetClangForwardType(); + m_type_sp = type_sp; + } + else + { + m_clang_ast_type.Clear(); + m_type_sp.reset(); + } +} + +TypeImpl& +TypeImpl::operator = (const TypeImpl& rhs) +{ + if (*this != rhs) + { + m_clang_ast_type = rhs.m_clang_ast_type; + m_type_sp = rhs.m_type_sp; + } + return *this; +} + +clang::ASTContext* +TypeImpl::GetASTContext() +{ + if (!IsValid()) + return NULL; + + return m_clang_ast_type.GetASTContext(); +} + +lldb::clang_type_t +TypeImpl::GetOpaqueQualType() +{ + if (!IsValid()) + return NULL; + + return m_clang_ast_type.GetOpaqueQualType(); +} + +bool +TypeImpl::GetDescription (lldb_private::Stream &strm, + lldb::DescriptionLevel description_level) +{ + if (m_clang_ast_type.IsValid()) + { + m_clang_ast_type.DumpTypeDescription (&strm); + } + else + { + strm.PutCString ("No value"); + } + return true; +} + +ConstString +TypeImpl::GetName () +{ + if (m_clang_ast_type.IsValid()) + return m_clang_ast_type.GetConstTypeName(); + return ConstString(); +} diff --git a/source/Symbol/TypeList.cpp b/source/Symbol/TypeList.cpp new file mode 100644 index 000000000000..a033edd22e1f --- /dev/null +++ b/source/Symbol/TypeList.cpp @@ -0,0 +1,356 @@ +//===-- TypeList.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" + +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" + +// Project includes +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeList.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +TypeList::TypeList() : + m_types () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TypeList::~TypeList() +{ +} + +void +TypeList::Insert (const TypeSP& type_sp) +{ + // Just push each type on the back for now. We will worry about uniquing later + if (type_sp) + m_types.insert(std::make_pair(type_sp->GetID(), type_sp)); +} + + +bool +TypeList::InsertUnique (const TypeSP& type_sp) +{ + if (type_sp) + { + user_id_t type_uid = type_sp->GetID(); + iterator pos, end = m_types.end(); + + for (pos = m_types.find(type_uid); pos != end && pos->second->GetID() == type_uid; ++pos) + { + if (pos->second.get() == type_sp.get()) + return false; + } + } + Insert (type_sp); + return true; +} + +//---------------------------------------------------------------------- +// Find a base type by its unique ID. +//---------------------------------------------------------------------- +//TypeSP +//TypeList::FindType(lldb::user_id_t uid) +//{ +// iterator pos = m_types.find(uid); +// if (pos != m_types.end()) +// return pos->second; +// return TypeSP(); +//} + +//---------------------------------------------------------------------- +// Find a type by name. +//---------------------------------------------------------------------- +//TypeList +//TypeList::FindTypes (const ConstString &name) +//{ +// // Do we ever need to make a lookup by name map? Here we are doing +// // a linear search which isn't going to be fast. +// TypeList types(m_ast.getTargetInfo()->getTriple().getTriple().c_str()); +// iterator pos, end; +// for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) +// if (pos->second->GetName() == name) +// types.Insert (pos->second); +// return types; +//} + +void +TypeList::Clear() +{ + m_types.clear(); +} + +uint32_t +TypeList::GetSize() const +{ + return m_types.size(); +} + +// GetTypeAtIndex isn't used a lot for large type lists, currently only for +// type lists that are returned for "image dump -t TYPENAME" commands and other +// simple symbol queries that grab the first result... + +TypeSP +TypeList::GetTypeAtIndex(uint32_t idx) +{ + iterator pos, end; + uint32_t i = idx; + for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + { + if (i == 0) + return pos->second; + --i; + } + return TypeSP(); +} + +void +TypeList::ForEach (std::function <bool(const lldb::TypeSP &type_sp)> const &callback) const +{ + for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + { + if (!callback(pos->second)) + break; + } +} + +void +TypeList::ForEach (std::function <bool(lldb::TypeSP &type_sp)> const &callback) +{ + for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + { + if (!callback(pos->second)) + break; + } +} + + +bool +TypeList::RemoveTypeWithUID (user_id_t uid) +{ + iterator pos = m_types.find(uid); + + if (pos != m_types.end()) + { + m_types.erase(pos); + return true; + } + return false; +} + + +void +TypeList::Dump(Stream *s, bool show_context) +{ + for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) + { + pos->second->Dump(s, show_context); + } +} + +// depending on implementation details, type lookup might fail because of +// embedded spurious namespace:: prefixes. this call strips them, paying +// attention to the fact that a type might have namespace'd type names as +// arguments to templates, and those must not be stripped off +static bool +GetTypeScopeAndBasename(const char* name_cstr, std::string &scope, std::string &basename, bool *exact_ptr) +{ + // Protect against null c string. + + if (name_cstr && name_cstr[0]) + { + const char *basename_cstr = name_cstr; + const char* namespace_separator = ::strstr (basename_cstr, "::"); + if (namespace_separator) + { + const char* template_arg_char = ::strchr (basename_cstr, '<'); + while (namespace_separator != NULL) + { + if (template_arg_char && namespace_separator > template_arg_char) // but namespace'd template arguments are still good to go + break; + basename_cstr = namespace_separator + 2; + namespace_separator = strstr(basename_cstr, "::"); + } + if (basename_cstr > name_cstr) + { + scope.assign (name_cstr, basename_cstr - name_cstr); + if (scope.size() >= 2 && scope[0] == ':' && scope[1] == ':') + { + // The typename passed in started with "::" so make sure we only do exact matches + if (exact_ptr) + *exact_ptr = true; + // Strip the leading "::" as this won't ever show in qualified typenames we get + // from clang. + scope.erase(0,2); + } + basename.assign (basename_cstr); + return true; + } + } + } + return false; +} + +void +TypeList::RemoveMismatchedTypes (const char *qualified_typename, + bool exact_match) +{ + std::string type_scope; + std::string type_basename; + TypeClass type_class = eTypeClassAny; + if (!Type::GetTypeScopeAndBasename (qualified_typename, type_scope, type_basename, type_class)) + { + type_basename = qualified_typename; + type_scope.clear(); + } + return RemoveMismatchedTypes (type_scope, type_basename, type_class, exact_match); +} + +void +TypeList::RemoveMismatchedTypes (const std::string &type_scope, + const std::string &type_basename, + TypeClass type_class, + bool exact_match) +{ + // Our "collection" type currently is a std::map which doesn't + // have any good way to iterate and remove items from the map + // so we currently just make a new list and add all of the matching + // types to it, and then swap it into m_types at the end + collection matching_types; + + iterator pos, end = m_types.end(); + + for (pos = m_types.begin(); pos != end; ++pos) + { + Type* the_type = pos->second.get(); + bool keep_match = false; + TypeClass match_type_class = eTypeClassAny; + + if (type_class != eTypeClassAny) + { + match_type_class = the_type->GetClangForwardType().GetTypeClass (); + if ((match_type_class & type_class) == 0) + continue; + } + + ConstString match_type_name_const_str (the_type->GetQualifiedName()); + if (match_type_name_const_str) + { + const char *match_type_name = match_type_name_const_str.GetCString(); + std::string match_type_scope; + std::string match_type_basename; + if (Type::GetTypeScopeAndBasename (match_type_name, + match_type_scope, + match_type_basename, + match_type_class)) + { + if (match_type_basename == type_basename) + { + const size_t type_scope_size = type_scope.size(); + const size_t match_type_scope_size = match_type_scope.size(); + if (exact_match || (type_scope_size == match_type_scope_size)) + { + keep_match = match_type_scope == type_scope; + } + else + { + if (match_type_scope_size > type_scope_size) + { + const size_t type_scope_pos = match_type_scope.rfind(type_scope); + if (type_scope_pos == match_type_scope_size - type_scope_size) + { + if (type_scope_pos >= 2) + { + // Our match scope ends with the type scope we were lookikng for, + // but we need to make sure what comes before the matching + // type scope is a namepace boundary in case we are trying to match: + // type_basename = "d" + // type_scope = "b::c::" + // We want to match: + // match_type_scope "a::b::c::" + // But not: + // match_type_scope "a::bb::c::" + // So below we make sure what comes before "b::c::" in match_type_scope + // is "::", or the namespace boundary + if (match_type_scope[type_scope_pos - 1] == ':' && + match_type_scope[type_scope_pos - 2] == ':') + { + keep_match = true; + } + } + } + } + } + } + } + else + { + // The type we are currently looking at doesn't exists + // in a namespace or class, so it only matches if there + // is no type scope... + keep_match = type_scope.empty() && type_basename.compare(match_type_name) == 0; + } + } + + if (keep_match) + { + matching_types.insert (*pos); + } + } + m_types.swap(matching_types); +} + +void +TypeList::RemoveMismatchedTypes (TypeClass type_class) +{ + if (type_class == eTypeClassAny) + return; + + // Our "collection" type currently is a std::map which doesn't + // have any good way to iterate and remove items from the map + // so we currently just make a new list and add all of the matching + // types to it, and then swap it into m_types at the end + collection matching_types; + + iterator pos, end = m_types.end(); + + for (pos = m_types.begin(); pos != end; ++pos) + { + Type* the_type = pos->second.get(); + TypeClass match_type_class = the_type->GetClangForwardType().GetTypeClass (); + if (match_type_class & type_class) + matching_types.insert (*pos); + } + m_types.swap(matching_types); +} diff --git a/source/Symbol/UnwindPlan.cpp b/source/Symbol/UnwindPlan.cpp new file mode 100644 index 000000000000..c089a7bb8db8 --- /dev/null +++ b/source/Symbol/UnwindPlan.cpp @@ -0,0 +1,441 @@ +//===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/UnwindPlan.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +bool +UnwindPlan::Row::RegisterLocation::operator == (const UnwindPlan::Row::RegisterLocation& rhs) const +{ + if (m_type == rhs.m_type) + { + switch (m_type) + { + case unspecified: + case undefined: + case same: + return true; + + case atCFAPlusOffset: + case isCFAPlusOffset: + return m_location.offset == rhs.m_location.offset; + + case inOtherRegister: + return m_location.reg_num == rhs.m_location.reg_num; + + case atDWARFExpression: + case isDWARFExpression: + if (m_location.expr.length == rhs.m_location.expr.length) + return !memcmp (m_location.expr.opcodes, rhs.m_location.expr.opcodes, m_location.expr.length); + break; + } + } + return false; +} + +// This function doesn't copy the dwarf expression bytes; they must remain in allocated +// memory for the lifespan of this UnwindPlan object. +void +UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression (const uint8_t *opcodes, uint32_t len) +{ + m_type = atDWARFExpression; + m_location.expr.opcodes = opcodes; + m_location.expr.length = len; +} + +// This function doesn't copy the dwarf expression bytes; they must remain in allocated +// memory for the lifespan of this UnwindPlan object. +void +UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression (const uint8_t *opcodes, uint32_t len) +{ + m_type = isDWARFExpression; + m_location.expr.opcodes = opcodes; + m_location.expr.length = len; +} + +void +UnwindPlan::Row::RegisterLocation::Dump (Stream &s, const UnwindPlan* unwind_plan, const UnwindPlan::Row* row, Thread* thread, bool verbose) const +{ + switch (m_type) + { + case unspecified: + if (verbose) + s.PutCString ("=<unspec>"); + else + s.PutCString ("=!"); + break; + case undefined: + if (verbose) + s.PutCString ("=<undef>"); + else + s.PutCString ("=?"); + break; + case same: + s.PutCString ("= <same>"); + break; + + case atCFAPlusOffset: + case isCFAPlusOffset: + { + s.PutChar('='); + if (m_type == atCFAPlusOffset) + s.PutChar('['); + if (verbose) + s.Printf ("CFA%+d", m_location.offset); + + if (unwind_plan && row) + { + const uint32_t cfa_reg = row->GetCFARegister(); + const RegisterInfo *cfa_reg_info = unwind_plan->GetRegisterInfo (thread, cfa_reg); + const int32_t offset = row->GetCFAOffset() + m_location.offset; + if (verbose) + { + if (cfa_reg_info) + s.Printf (" (%s%+d)", cfa_reg_info->name, offset); + else + s.Printf (" (reg(%u)%+d)", cfa_reg, offset); + } + else + { + if (cfa_reg_info) + s.Printf ("%s", cfa_reg_info->name); + else + s.Printf ("reg(%u)", cfa_reg); + if (offset != 0) + s.Printf ("%+d", offset); + } + } + if (m_type == atCFAPlusOffset) + s.PutChar(']'); + } + break; + + case inOtherRegister: + { + const RegisterInfo *other_reg_info = NULL; + if (unwind_plan) + other_reg_info = unwind_plan->GetRegisterInfo (thread, m_location.reg_num); + if (other_reg_info) + s.Printf ("=%s", other_reg_info->name); + else + s.Printf ("=reg(%u)", m_location.reg_num); + } + break; + + case atDWARFExpression: + case isDWARFExpression: + { + s.PutChar('='); + if (m_type == atDWARFExpression) + s.PutCString("[dwarf-expr]"); + else + s.PutCString("dwarf-expr"); + } + break; + + } +} + +void +UnwindPlan::Row::Clear () +{ + m_offset = 0; + m_cfa_reg_num = LLDB_INVALID_REGNUM; + m_cfa_offset = 0; + m_register_locations.clear(); +} + +void +UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, addr_t base_addr) const +{ + const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo (thread, GetCFARegister()); + + if (base_addr != LLDB_INVALID_ADDRESS) + s.Printf ("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset()); + else + s.Printf ("0x%8.8" PRIx64 ": CFA=", GetOffset()); + + if (reg_info) + s.Printf ("%s", reg_info->name); + else + s.Printf ("reg(%u)", GetCFARegister()); + s.Printf ("%+3d => ", GetCFAOffset ()); + for (collection::const_iterator idx = m_register_locations.begin (); idx != m_register_locations.end (); ++idx) + { + reg_info = unwind_plan->GetRegisterInfo (thread, idx->first); + if (reg_info) + s.Printf ("%s", reg_info->name); + else + s.Printf ("reg(%u)", idx->first); + const bool verbose = false; + idx->second.Dump(s, unwind_plan, this, thread, verbose); + s.PutChar (' '); + } + s.EOL(); +} + +UnwindPlan::Row::Row() : + m_offset(0), + m_cfa_reg_num(LLDB_INVALID_REGNUM), + m_cfa_offset(0), + m_register_locations() +{ +} + +bool +UnwindPlan::Row::GetRegisterInfo (uint32_t reg_num, UnwindPlan::Row::RegisterLocation& register_location) const +{ + collection::const_iterator pos = m_register_locations.find(reg_num); + if (pos != m_register_locations.end()) + { + register_location = pos->second; + return true; + } + return false; +} + +void +UnwindPlan::Row::SetRegisterInfo (uint32_t reg_num, const UnwindPlan::Row::RegisterLocation register_location) +{ + m_register_locations[reg_num] = register_location; +} + +bool +UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset (uint32_t reg_num, int32_t offset, bool can_replace) +{ + if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end()) + return false; + RegisterLocation reg_loc; + reg_loc.SetAtCFAPlusOffset(offset); + m_register_locations[reg_num] = reg_loc; + return true; +} + +bool +UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset (uint32_t reg_num, int32_t offset, bool can_replace) +{ + if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end()) + return false; + RegisterLocation reg_loc; + reg_loc.SetIsCFAPlusOffset(offset); + m_register_locations[reg_num] = reg_loc; + return true; +} + +bool +UnwindPlan::Row::SetRegisterLocationToUndefined (uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) +{ + collection::iterator pos = m_register_locations.find(reg_num); + collection::iterator end = m_register_locations.end(); + + if (pos != end) + { + if (!can_replace) + return false; + if (can_replace_only_if_unspecified && !pos->second.IsUnspecified()) + return false; + } + RegisterLocation reg_loc; + reg_loc.SetUndefined(); + m_register_locations[reg_num] = reg_loc; + return true; +} + +bool +UnwindPlan::Row::SetRegisterLocationToUnspecified (uint32_t reg_num, bool can_replace) +{ + if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end()) + return false; + RegisterLocation reg_loc; + reg_loc.SetUnspecified(); + m_register_locations[reg_num] = reg_loc; + return true; +} + +bool +UnwindPlan::Row::SetRegisterLocationToRegister (uint32_t reg_num, + uint32_t other_reg_num, + bool can_replace) +{ + if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end()) + return false; + RegisterLocation reg_loc; + reg_loc.SetInRegister(other_reg_num); + m_register_locations[reg_num] = reg_loc; + return true; +} + +bool +UnwindPlan::Row::SetRegisterLocationToSame (uint32_t reg_num, bool must_replace) +{ + if (must_replace && m_register_locations.find(reg_num) == m_register_locations.end()) + return false; + RegisterLocation reg_loc; + reg_loc.SetSame(); + m_register_locations[reg_num] = reg_loc; + return true; +} + +void +UnwindPlan::Row::SetCFARegister (uint32_t reg_num) +{ + m_cfa_reg_num = reg_num; +} + +bool +UnwindPlan::Row::operator == (const UnwindPlan::Row& rhs) const +{ + if (m_offset != rhs.m_offset || m_cfa_reg_num != rhs.m_cfa_reg_num || m_cfa_offset != rhs.m_cfa_offset) + return false; + return m_register_locations == rhs.m_register_locations; +} + +void +UnwindPlan::AppendRow (const UnwindPlan::RowSP &row_sp) +{ + if (m_row_list.empty() || m_row_list.back()->GetOffset() != row_sp->GetOffset()) + m_row_list.push_back(row_sp); + else + m_row_list.back() = row_sp; +} + +UnwindPlan::RowSP +UnwindPlan::GetRowForFunctionOffset (int offset) const +{ + RowSP row; + if (!m_row_list.empty()) + { + if (offset == -1) + row = m_row_list.back(); + else + { + collection::const_iterator pos, end = m_row_list.end(); + for (pos = m_row_list.begin(); pos != end; ++pos) + { + if ((*pos)->GetOffset() <= offset) + row = *pos; + else + break; + } + } + } + return row; +} + +bool +UnwindPlan::IsValidRowIndex (uint32_t idx) const +{ + return idx < m_row_list.size(); +} + +const UnwindPlan::RowSP +UnwindPlan::GetRowAtIndex (uint32_t idx) const +{ + // You must call IsValidRowIndex(idx) first before calling this!!! + assert (idx < m_row_list.size()); + return m_row_list[idx]; +} + +const UnwindPlan::RowSP +UnwindPlan::GetLastRow () const +{ + // You must call GetRowCount() first to make sure there is at least one row + assert (!m_row_list.empty()); + return m_row_list.back(); +} + +int +UnwindPlan::GetRowCount () const +{ + return m_row_list.size (); +} + +void +UnwindPlan::SetPlanValidAddressRange (const AddressRange& range) +{ + if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) + m_plan_valid_address_range = range; +} + +bool +UnwindPlan::PlanValidAtAddress (Address addr) +{ + if (!m_plan_valid_address_range.GetBaseAddress().IsValid() || m_plan_valid_address_range.GetByteSize() == 0) + return true; + + if (!addr.IsValid()) + return true; + + if (m_plan_valid_address_range.ContainsFileAddress (addr)) + return true; + + return false; +} + +void +UnwindPlan::Dump (Stream& s, Thread *thread, lldb::addr_t base_addr) const +{ + if (!m_source_name.IsEmpty()) + { + s.Printf ("This UnwindPlan originally sourced from %s\n", m_source_name.GetCString()); + } + if (m_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0) + { + s.PutCString ("Address range of this UnwindPlan: "); + TargetSP target_sp(thread->CalculateTarget()); + m_plan_valid_address_range.Dump (&s, target_sp.get(), Address::DumpStyleSectionNameOffset); + s.EOL(); + } + collection::const_iterator pos, begin = m_row_list.begin(), end = m_row_list.end(); + for (pos = begin; pos != end; ++pos) + { + s.Printf ("row[%u]: ", (uint32_t)std::distance (begin, pos)); + (*pos)->Dump(s, this, thread, base_addr); + } +} + +void +UnwindPlan::SetSourceName (const char *source) +{ + m_source_name = ConstString (source); +} + +ConstString +UnwindPlan::GetSourceName () const +{ + return m_source_name; +} + +const RegisterInfo * +UnwindPlan::GetRegisterInfo (Thread* thread, uint32_t unwind_reg) const +{ + if (thread) + { + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + if (reg_ctx) + { + uint32_t reg; + if (m_register_kind == eRegisterKindLLDB) + reg = unwind_reg; + else + reg = reg_ctx->ConvertRegisterKindToRegisterNumber (m_register_kind, unwind_reg); + if (reg != LLDB_INVALID_REGNUM) + return reg_ctx->GetRegisterInfoAtIndex (reg); + } + } + return NULL; +} + diff --git a/source/Symbol/UnwindTable.cpp b/source/Symbol/UnwindTable.cpp new file mode 100644 index 000000000000..c77628bf7dd8 --- /dev/null +++ b/source/Symbol/UnwindTable.cpp @@ -0,0 +1,153 @@ +//===-- UnwindTable.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/UnwindTable.h" + +#include <stdio.h> + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Target/UnwindAssembly.h" + +// There is one UnwindTable object per ObjectFile. +// It contains a list of Unwind objects -- one per function, populated lazily -- for the ObjectFile. +// Each Unwind object has multiple UnwindPlans for different scenarios. + +using namespace lldb; +using namespace lldb_private; + +UnwindTable::UnwindTable (ObjectFile& objfile) : + m_object_file (objfile), + m_unwinds (), + m_initialized (false), + m_assembly_profiler (NULL), + m_eh_frame (NULL) +{ +} + +// We can't do some of this initialization when the ObjectFile is running its ctor; delay doing it +// until needed for something. + +void +UnwindTable::Initialize () +{ + if (m_initialized) + return; + + SectionList* sl = m_object_file.GetSectionList (); + if (sl) + { + SectionSP sect = sl->FindSectionByType (eSectionTypeEHFrame, true); + if (sect.get()) + { + m_eh_frame = new DWARFCallFrameInfo(m_object_file, sect, eRegisterKindGCC, true); + } + } + + ArchSpec arch; + if (m_object_file.GetArchitecture (arch)) + { + m_assembly_profiler = UnwindAssembly::FindPlugin (arch); + m_initialized = true; + } +} + +UnwindTable::~UnwindTable () +{ + if (m_eh_frame) + delete m_eh_frame; +} + +FuncUnwindersSP +UnwindTable::GetFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc) +{ + FuncUnwindersSP no_unwind_found; + + Initialize(); + + // There is an UnwindTable per object file, so we can safely use file handles + addr_t file_addr = addr.GetFileAddress(); + iterator end = m_unwinds.end (); + iterator insert_pos = end; + if (!m_unwinds.empty()) + { + insert_pos = m_unwinds.lower_bound (file_addr); + iterator pos = insert_pos; + if ((pos == m_unwinds.end ()) || (pos != m_unwinds.begin() && pos->second->GetFunctionStartAddress() != addr)) + --pos; + + if (pos->second->ContainsAddress (addr)) + return pos->second; + } + + AddressRange range; + if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) || !range.GetBaseAddress().IsValid()) + { + // Does the eh_frame unwind info has a function bounds for this addr? + if (m_eh_frame == NULL || !m_eh_frame->GetAddressRange (addr, range)) + { + return no_unwind_found; + } + } + + FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, m_assembly_profiler, range)); + m_unwinds.insert (insert_pos, std::make_pair(range.GetBaseAddress().GetFileAddress(), func_unwinder_sp)); +// StreamFile s(stdout); +// Dump (s); + return func_unwinder_sp; +} + +// Ignore any existing FuncUnwinders for this function, create a new one and don't add it to the +// UnwindTable. This is intended for use by target modules show-unwind where we want to create +// new UnwindPlans, not re-use existing ones. + +FuncUnwindersSP +UnwindTable::GetUncachedFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc) +{ + FuncUnwindersSP no_unwind_found; + Initialize(); + + AddressRange range; + if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range) || !range.GetBaseAddress().IsValid()) + { + // Does the eh_frame unwind info has a function bounds for this addr? + if (m_eh_frame == NULL || !m_eh_frame->GetAddressRange (addr, range)) + { + return no_unwind_found; + } + } + + FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, m_assembly_profiler, range)); + return func_unwinder_sp; +} + + +void +UnwindTable::Dump (Stream &s) +{ + s.Printf("UnwindTable for '%s':\n", m_object_file.GetFileSpec().GetPath().c_str()); + const_iterator begin = m_unwinds.begin(); + const_iterator end = m_unwinds.end(); + for (const_iterator pos = begin; pos != end; ++pos) + { + s.Printf ("[%u] 0x%16.16" PRIx64 "\n", (unsigned)std::distance (begin, pos), pos->first); + } + s.EOL(); +} + +DWARFCallFrameInfo * +UnwindTable::GetEHFrameInfo () +{ + Initialize(); + return m_eh_frame; +} diff --git a/source/Symbol/Variable.cpp b/source/Symbol/Variable.cpp new file mode 100644 index 000000000000..36439b561626 --- /dev/null +++ b/source/Symbol/Variable.cpp @@ -0,0 +1,894 @@ +//===-- Variable.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/Variable.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Type.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/Thread.h" +#include "lldb/Target/Target.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 variable name for variables in namespaces + const lldb::SymbolFileTypeSP &symfile_type_sp, + ValueType scope, + SymbolContextScope *context, + Declaration* decl_ptr, + const DWARFExpression& location, + bool external, + bool artificial +) : + UserID(uid), + m_name(name), + m_mangled (ConstString(mangled), true), + m_symfile_type_sp(symfile_type_sp), + m_scope(scope), + m_owner_scope(context), + m_declaration(decl_ptr), + m_location(location), + m_external(external), + m_artificial(artificial) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Variable::~Variable() +{ +} + + +const ConstString& +Variable::GetName() const +{ + if (m_mangled) + return m_mangled.GetName(); + return m_name; +} + +bool +Variable::NameMatches (const RegularExpression& regex) const +{ + if (regex.Execute (m_name.AsCString())) + return true; + return m_mangled.NameMatches (regex); +} + +Type * +Variable::GetType() +{ + if (m_symfile_type_sp) + return m_symfile_type_sp->GetType(); + return NULL; +} + +void +Variable::Dump(Stream *s, bool show_context) const +{ + s->Printf("%p: ", 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; + default: *s << "??? (" << m_scope << ')'; + } + } + + if (show_context && m_owner_scope != NULL) + { + 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(); + } + ABI *abi = NULL; + if (m_owner_scope) + { + ModuleSP module_sp (m_owner_scope->CalculateSymbolContextModule()); + if (module_sp) + abi = ABI::FindPlugin (module_sp->GetArchitecture()).get(); + } + m_location.GetDescription(s, lldb::eDescriptionLevelBrief, loclist_base_addr, abi); + } + + 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 = NULL; + sc.line_entry.Clear(); + bool show_inlined_frames = false; + + dumped_declaration_info = sc.DumpStopContext (s, + NULL, + Address(), + show_fullpaths, + show_module, + show_inlined_frames); + + 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); +} + + +void +Variable::CalculateSymbolContext (SymbolContext *sc) +{ + if (m_owner_scope) + m_owner_scope->CalculateSymbolContext(sc); + 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 != NULL; + + case eValueTypeConstResult: + case eValueTypeVariableGlobal: + case eValueTypeVariableStatic: + 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 == NULL) + return true; + + if (variable_sc.block == deepest_frame_block) + return true; + return variable_sc.block->Contains (deepest_frame_block); + } + } + break; + + default: + break; + } + return false; +} + +Error +Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path, + ExecutionContextScope *scope, + GetVariableCallback callback, + void *baton, + VariableList &variable_list, + ValueObjectList &valobj_list) +{ + Error error; + if (variable_expr_path && callback) + { + switch (variable_expr_path[0]) + { + case '*': + { + error = Variable::GetValuesForVariableExpressionPath (variable_expr_path + 1, + scope, + callback, + baton, + variable_list, + valobj_list); + if (error.Success()) + { + for (uint32_t i=0; i<valobj_list.GetSize(); ) + { + Error 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; + } + } + } + else + { + error.SetErrorString ("unknown error"); + } + return error; + } + break; + + case '&': + { + error = Variable::GetValuesForVariableExpressionPath (variable_expr_path + 1, + scope, + callback, + baton, + variable_list, + valobj_list); + if (error.Success()) + { + for (uint32_t i=0; i<valobj_list.GetSize(); ) + { + Error 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 ("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"); + RegularExpression::Match regex_match(1); + if (g_regex.Execute(variable_expr_path, ®ex_match)) + { + std::string variable_name; + if (regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) + { + variable_list.Clear(); + if (callback (baton, variable_name.c_str(), variable_list)) + { + uint32_t i=0; + while (i < variable_list.GetSize()) + { + VariableSP var_sp (variable_list.GetVariableAtIndex (i)); + ValueObjectSP valobj_sp; + if (var_sp) + { + ValueObjectSP variable_valobj_sp(ValueObjectVariable::Create (scope, var_sp)); + if (variable_valobj_sp) + { + const char *variable_sub_expr_path = variable_expr_path + variable_name.size(); + if (*variable_sub_expr_path) + { + const char* first_unparsed = NULL; + ValueObject::ExpressionPathScanEndReason reason_to_stop; + ValueObject::ExpressionPathEndResultType final_value_type; + ValueObject::GetValueForExpressionPathOptions options; + ValueObject::ExpressionPathAftermath final_task_on_target; + + valobj_sp = variable_valobj_sp->GetValueForExpressionPath (variable_sub_expr_path, + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &final_task_on_target); + if (!valobj_sp) + { + error.SetErrorStringWithFormat ("invalid expression path '%s' for variable '%s'", + variable_sub_expr_path, + var_sp->GetName().GetCString()); + } + } + else + { + // Just the name of a variable with no extras + valobj_sp = variable_valobj_sp; + } + } + } + + if (!var_sp || !valobj_sp) + { + variable_list.RemoveVariableAtIndex (i); + } + else + { + valobj_list.Append(valobj_sp); + ++i; + } + } + + if (variable_list.GetSize() > 0) + { + error.Clear(); + return error; + } + } + } + } + error.SetErrorStringWithFormat ("unable to extract a variable name from '%s'", variable_expr_path); + } + 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()) + { + ABI *abi = NULL; + if (m_owner_scope) + { + ModuleSP module_sp (m_owner_scope->CalculateSymbolContextModule()); + if (module_sp) + abi = ABI::FindPlugin (module_sp->GetArchitecture()).get(); + } + + 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); + } + } + return m_location.DumpLocationForAddress (s, + eDescriptionLevelBrief, + LLDB_INVALID_ADDRESS, + file_addr, + abi); + } + } + return false; +} + + +static void +PrivateAutoComplete (StackFrame *frame, + const std::string &partial_path, + const std::string &prefix_path, // Anything that has been resolved already will be in here + const ClangASTType& clang_type, + StringList &matches, + bool &word_complete); + +static void +PrivateAutoCompleteMembers (StackFrame *frame, + const std::string &partial_member_name, + const std::string &partial_path, + const std::string &prefix_path, // Anything that has been resolved already will be in here + const ClangASTType& clang_type, + StringList &matches, + bool &word_complete); + +static void +PrivateAutoCompleteMembers (StackFrame *frame, + const std::string &partial_member_name, + const std::string &partial_path, + const std::string &prefix_path, // Anything that has been resolved already will be in here + const ClangASTType& clang_type, + StringList &matches, + bool &word_complete) +{ + + // We are in a type parsing child members + const uint32_t num_bases = clang_type.GetNumDirectBaseClasses(); + + if (num_bases > 0) + { + for (uint32_t i = 0; i < num_bases; ++i) + { + ClangASTType base_class_type (clang_type.GetDirectBaseClassAtIndex (i, NULL)); + + PrivateAutoCompleteMembers (frame, + partial_member_name, + partial_path, + prefix_path, + base_class_type.GetCanonicalType(), + matches, + word_complete); + } + } + + const uint32_t num_vbases = clang_type.GetNumVirtualBaseClasses(); + + if (num_vbases > 0) + { + for (uint32_t i = 0; i < num_vbases; ++i) + { + ClangASTType vbase_class_type (clang_type.GetVirtualBaseClassAtIndex(i,NULL)); + + 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 = clang_type.GetNumFields(); + + if (num_fields > 0) + { + for (uint32_t i = 0; i < num_fields; ++i) + { + std::string member_name; + + ClangASTType member_clang_type = clang_type.GetFieldAtIndex (i, member_name, NULL, NULL, NULL); + + 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_clang_type.GetCanonicalType(), + matches, + word_complete); + } + else + { + matches.AppendString (prefix_path + member_name); + } + } + } + } +} + +static void +PrivateAutoComplete (StackFrame *frame, + const std::string &partial_path, + const std::string &prefix_path, // Anything that has been resolved already will be in here + const ClangASTType& clang_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 = clang_type.GetTypeClass(); + if (partial_path.empty()) + { + if (clang_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); + word_complete = matches.GetSize() == 1; + } + break; + + case eTypeClassClass: + case eTypeClassStruct: + case eTypeClassUnion: + if (prefix_path.back() != '.') + matches.AppendString (prefix_path + '.'); + break; + + case eTypeClassObjCObject: + case eTypeClassObjCInterface: + break; + case eTypeClassObjCObjectPointer: + case eTypeClassPointer: + { + bool omit_empty_base_classes = true; + if (clang_type.GetNumChildren (omit_empty_base_classes) > 0) + matches.AppendString (prefix_path + "->"); + else + { + matches.AppendString (prefix_path); + word_complete = true; + } + } + break; + } + } + else + { + if (frame) + { + const bool get_file_globals = true; + + VariableList *variable_list = frame->GetVariableList(get_file_globals); + + 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.empty()) + { + PrivateAutoComplete (frame, + partial_path.substr(1), + std::string("*"), + clang_type, + matches, + word_complete); + } + break; + + case '&': + if (prefix_path.empty()) + { + PrivateAutoComplete (frame, + partial_path.substr(1), + std::string("&"), + clang_type, + matches, + word_complete); + } + break; + + case '-': + if (partial_path[1] == '>' && !prefix_path.empty()) + { + switch (type_class) + { + case lldb::eTypeClassPointer: + { + ClangASTType pointee_type(clang_type.GetPointeeType()); + if (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); + } + } + default: + break; + } + } + break; + + case '.': + if (clang_type.IsValid()) + { + switch (type_class) + { + case lldb::eTypeClassUnion: + case lldb::eTypeClassStruct: + case lldb::eTypeClassClass: + if (partial_path[1]) + { + // If there is more after the ".", then search deeper + PrivateAutoComplete (frame, + partial_path.substr(1), + prefix_path + ".", + clang_type, + matches, + word_complete); + + } + else + { + // Nothing after the ".", so list all members + PrivateAutoCompleteMembers (frame, + std::string(), + partial_path, + prefix_path + ".", + clang_type, + matches, + word_complete); + } + 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 (clang_type.IsValid()) + { + PrivateAutoCompleteMembers (frame, + token, + remaining_partial_path, + prefix_path, + clang_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); + + const size_t num_variables = variable_list->GetSize(); + for (size_t i=0; i<num_variables; ++i) + { + Variable *variable = variable_list->GetVariableAtIndex(i).get(); + 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) + { + ClangASTType variable_clang_type (variable_type->GetClangForwardType()); + PrivateAutoComplete (frame, + remaining_partial_path, + prefix_path + token, // Anything that has been resolved already will be in here + variable_clang_type.GetCanonicalType(), + matches, + word_complete); + } + else + { + matches.AppendString (prefix_path + variable_name); + } + } + else if (remaining_partial_path.empty()) + { + matches.AppendString (prefix_path + variable_name); + } + } + } + } + } + break; + } + } +} + + + +size_t +Variable::AutoComplete (const ExecutionContext &exe_ctx, + const char *partial_path_cstr, + StringList &matches, + bool &word_complete) +{ + word_complete = false; + std::string partial_path; + std::string prefix_path; + ClangASTType clang_type; + if (partial_path_cstr && partial_path_cstr[0]) + partial_path = partial_path_cstr; + + PrivateAutoComplete (exe_ctx.GetFramePtr(), + partial_path, + prefix_path, + clang_type, + matches, + word_complete); + + return matches.GetSize(); +} + diff --git a/source/Symbol/VariableList.cpp b/source/Symbol/VariableList.cpp new file mode 100644 index 000000000000..3451166e52d4 --- /dev/null +++ b/source/Symbol/VariableList.cpp @@ -0,0 +1,201 @@ +//===-- VariableList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/VariableList.h" + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/CompileUnit.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// VariableList constructor +//---------------------------------------------------------------------- +VariableList::VariableList() : + m_variables() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +VariableList::~VariableList() +{ +} + +void +VariableList::AddVariable(const VariableSP &var_sp) +{ + m_variables.push_back(var_sp); +} + +bool +VariableList::AddVariableIfUnique (const lldb::VariableSP &var_sp) +{ + if (FindVariableIndex (var_sp) == UINT32_MAX) + { + m_variables.push_back(var_sp); + return true; + } + return false; +} + +void +VariableList::AddVariables(VariableList *variable_list) +{ + if (variable_list) + { + std::copy(variable_list->m_variables.begin(), // source begin + variable_list->m_variables.end(), // source end + back_inserter(m_variables)); // destination + } +} + +void +VariableList::Clear() +{ + m_variables.clear(); +} + +VariableSP +VariableList::GetVariableAtIndex(size_t idx) const +{ + VariableSP var_sp; + if (idx < m_variables.size()) + var_sp = m_variables[idx]; + return var_sp; +} + +VariableSP +VariableList::RemoveVariableAtIndex(size_t idx) +{ + VariableSP var_sp; + if (idx < m_variables.size()) + { + var_sp = m_variables[idx]; + m_variables.erase (m_variables.begin() + idx); + } + return var_sp; +} + +uint32_t +VariableList::FindVariableIndex (const VariableSP &var_sp) +{ + iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if (pos->get() == var_sp.get()) + return std::distance (m_variables.begin(), pos); + } + return UINT32_MAX; +} + +VariableSP +VariableList::FindVariable(const ConstString& name) +{ + VariableSP var_sp; + iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if ((*pos)->NameMatches(name)) + { + var_sp = (*pos); + break; + } + } + return var_sp; +} + +size_t +VariableList::AppendVariablesIfUnique (const RegularExpression& regex, VariableList &var_list, size_t& total_matches) +{ + const size_t initial_size = var_list.GetSize(); + iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if ((*pos)->NameMatches (regex)) + { + // Note the total matches found + total_matches++; + // Only add this variable if it isn't already in the "var_list" + var_list.AddVariableIfUnique (*pos); + } + } + // Return the number of new unique variables added to "var_list" + return var_list.GetSize() - initial_size; +} + +size_t +VariableList::AppendVariablesWithScope (lldb::ValueType type, + VariableList &var_list, + bool if_unique) +{ + const size_t initial_size = var_list.GetSize(); + iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if ((*pos)->GetScope() == type) + { + if (if_unique) + var_list.AddVariableIfUnique (*pos); + else + var_list.AddVariable(*pos); + } + } + // Return the number of new unique variables added to "var_list" + return var_list.GetSize() - initial_size; +} + +uint32_t +VariableList::FindIndexForVariable (Variable* variable) +{ + VariableSP var_sp; + iterator pos; + const iterator begin = m_variables.begin(); + const iterator end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + if ((*pos).get() == variable) + return std::distance (begin, pos); + } + return UINT32_MAX; +} + +size_t +VariableList::MemorySize() const +{ + size_t mem_size = sizeof(VariableList); + const_iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + mem_size += (*pos)->MemorySize(); + return mem_size; +} + +size_t +VariableList::GetSize() const +{ + return m_variables.size(); +} + +void +VariableList::Dump(Stream *s, bool show_context) const +{ +// s.Printf("%.*p: ", (int)sizeof(void*) * 2, this); +// s.Indent(); +// s << "VariableList\n"; + + const_iterator pos, end = m_variables.end(); + for (pos = m_variables.begin(); pos != end; ++pos) + { + (*pos)->Dump(s, show_context); + } +} diff --git a/source/Symbol/VerifyDecl.cpp b/source/Symbol/VerifyDecl.cpp new file mode 100644 index 000000000000..765568040ece --- /dev/null +++ b/source/Symbol/VerifyDecl.cpp @@ -0,0 +1,16 @@ +//===-- VerifyDecl.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/VerifyDecl.h" +#include "clang/AST/DeclBase.h" + +void lldb_private::VerifyDecl (clang::Decl *decl) +{ + decl->getAccess(); +} |