The way this code was updated in
dd95877958 meant that if the first module
did not have the symbol, the iteration stopped as returning true means
stop. So only if every module had the symbol would we find it, in the
last module.
Invert the condition to break when we find the first instance, which is
what the previous code did.
3903 lines
140 KiB
C++
3903 lines
140 KiB
C++
//===-- DWARFASTParserClang.cpp -------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <cstdlib>
|
|
|
|
#include "DWARFASTParser.h"
|
|
#include "DWARFASTParserClang.h"
|
|
#include "DWARFDebugInfo.h"
|
|
#include "DWARFDeclContext.h"
|
|
#include "DWARFDefines.h"
|
|
#include "SymbolFileDWARF.h"
|
|
#include "SymbolFileDWARFDebugMap.h"
|
|
#include "SymbolFileDWARFDwo.h"
|
|
#include "UniqueDWARFASTType.h"
|
|
|
|
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h"
|
|
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
|
|
#include "Plugins/Language/ObjC/ObjCLanguage.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Host/Host.h"
|
|
#include "lldb/Symbol/CompileUnit.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/SymbolFile.h"
|
|
#include "lldb/Symbol/TypeList.h"
|
|
#include "lldb/Symbol/TypeMap.h"
|
|
#include "lldb/Symbol/VariableList.h"
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Utility/LLDBAssert.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "llvm/Demangle/Demangle.h"
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
|
|
|
|
#ifdef ENABLE_DEBUG_PRINTF
|
|
#include <cstdio>
|
|
#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
|
|
#else
|
|
#define DEBUG_PRINTF(fmt, ...)
|
|
#endif
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::dwarf;
|
|
using namespace lldb_private::plugin::dwarf;
|
|
|
|
DWARFASTParserClang::DWARFASTParserClang(TypeSystemClang &ast)
|
|
: DWARFASTParser(Kind::DWARFASTParserClang), m_ast(ast),
|
|
m_die_to_decl_ctx(), m_decl_ctx_to_die() {}
|
|
|
|
DWARFASTParserClang::~DWARFASTParserClang() = default;
|
|
|
|
static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) {
|
|
switch (decl_kind) {
|
|
case clang::Decl::CXXRecord:
|
|
case clang::Decl::ClassTemplateSpecialization:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() {
|
|
if (!m_clang_ast_importer_up) {
|
|
m_clang_ast_importer_up = std::make_unique<ClangASTImporter>();
|
|
}
|
|
return *m_clang_ast_importer_up;
|
|
}
|
|
|
|
/// Detect a forward declaration that is nested in a DW_TAG_module.
|
|
static bool IsClangModuleFwdDecl(const DWARFDIE &Die) {
|
|
if (!Die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))
|
|
return false;
|
|
auto Parent = Die.GetParent();
|
|
while (Parent.IsValid()) {
|
|
if (Parent.Tag() == DW_TAG_module)
|
|
return true;
|
|
Parent = Parent.GetParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static DWARFDIE GetContainingClangModuleDIE(const DWARFDIE &die) {
|
|
if (die.IsValid()) {
|
|
DWARFDIE top_module_die;
|
|
// Now make sure this DIE is scoped in a DW_TAG_module tag and return true
|
|
// if so
|
|
for (DWARFDIE parent = die.GetParent(); parent.IsValid();
|
|
parent = parent.GetParent()) {
|
|
const dw_tag_t tag = parent.Tag();
|
|
if (tag == DW_TAG_module)
|
|
top_module_die = parent;
|
|
else if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit)
|
|
break;
|
|
}
|
|
|
|
return top_module_die;
|
|
}
|
|
return DWARFDIE();
|
|
}
|
|
|
|
static lldb::ModuleSP GetContainingClangModule(const DWARFDIE &die) {
|
|
if (die.IsValid()) {
|
|
DWARFDIE clang_module_die = GetContainingClangModuleDIE(die);
|
|
|
|
if (clang_module_die) {
|
|
const char *module_name = clang_module_die.GetName();
|
|
if (module_name)
|
|
return die.GetDWARF()->GetExternalModule(
|
|
lldb_private::ConstString(module_name));
|
|
}
|
|
}
|
|
return lldb::ModuleSP();
|
|
}
|
|
|
|
// Returns true if the given artificial field name should be ignored when
|
|
// parsing the DWARF.
|
|
static bool ShouldIgnoreArtificialField(llvm::StringRef FieldName) {
|
|
return FieldName.starts_with("_vptr$")
|
|
// gdb emit vtable pointer as "_vptr.classname"
|
|
|| FieldName.starts_with("_vptr.");
|
|
}
|
|
|
|
TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
Log *log) {
|
|
ModuleSP clang_module_sp = GetContainingClangModule(die);
|
|
if (!clang_module_sp)
|
|
return TypeSP();
|
|
|
|
// If this type comes from a Clang module, recursively look in the
|
|
// DWARF section of the .pcm file in the module cache. Clang
|
|
// generates DWO skeleton units as breadcrumbs to find them.
|
|
std::vector<lldb_private::CompilerContext> die_context = die.GetDeclContext();
|
|
TypeQuery query(die_context, TypeQueryOptions::e_module_search |
|
|
TypeQueryOptions::e_find_one);
|
|
TypeResults results;
|
|
|
|
// The type in the Clang module must have the same language as the current CU.
|
|
query.AddLanguage(SymbolFileDWARF::GetLanguageFamily(*die.GetCU()));
|
|
clang_module_sp->FindTypes(query, results);
|
|
TypeSP pcm_type_sp = results.GetTypeMap().FirstType();
|
|
if (!pcm_type_sp) {
|
|
// Since this type is defined in one of the Clang modules imported
|
|
// by this symbol file, search all of them. Instead of calling
|
|
// sym_file->FindTypes(), which would return this again, go straight
|
|
// to the imported modules.
|
|
auto &sym_file = die.GetCU()->GetSymbolFileDWARF();
|
|
|
|
// Well-formed clang modules never form cycles; guard against corrupted
|
|
// ones by inserting the current file.
|
|
results.AlreadySearched(&sym_file);
|
|
sym_file.ForEachExternalModule(
|
|
*sc.comp_unit, results.GetSearchedSymbolFiles(), [&](Module &module) {
|
|
module.FindTypes(query, results);
|
|
pcm_type_sp = results.GetTypeMap().FirstType();
|
|
return (bool)pcm_type_sp;
|
|
});
|
|
}
|
|
|
|
if (!pcm_type_sp)
|
|
return TypeSP();
|
|
|
|
// We found a real definition for this type in the Clang module, so lets use
|
|
// it and cache the fact that we found a complete type for this die.
|
|
lldb_private::CompilerType pcm_type = pcm_type_sp->GetForwardCompilerType();
|
|
lldb_private::CompilerType type =
|
|
GetClangASTImporter().CopyType(m_ast, pcm_type);
|
|
|
|
if (!type)
|
|
return TypeSP();
|
|
|
|
// Under normal operation pcm_type is a shallow forward declaration
|
|
// that gets completed later. This is necessary to support cyclic
|
|
// data structures. If, however, pcm_type is already complete (for
|
|
// example, because it was loaded for a different target before),
|
|
// the definition needs to be imported right away, too.
|
|
// Type::ResolveClangType() effectively ignores the ResolveState
|
|
// inside type_sp and only looks at IsDefined(), so it never calls
|
|
// ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo(),
|
|
// which does extra work for Objective-C classes. This would result
|
|
// in only the forward declaration to be visible.
|
|
if (pcm_type.IsDefined())
|
|
GetClangASTImporter().RequireCompleteType(ClangUtil::GetQualType(type));
|
|
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
auto type_sp = dwarf->MakeType(
|
|
die.GetID(), pcm_type_sp->GetName(), pcm_type_sp->GetByteSize(nullptr),
|
|
nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid,
|
|
&pcm_type_sp->GetDeclaration(), type, Type::ResolveState::Forward,
|
|
TypePayloadClang(GetOwningClangModule(die)));
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
clang::TagDecl *tag_decl = TypeSystemClang::GetAsTagDecl(type);
|
|
if (tag_decl) {
|
|
LinkDeclContextToDIE(tag_decl, die);
|
|
} else {
|
|
clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die);
|
|
if (defn_decl_ctx)
|
|
LinkDeclContextToDIE(defn_decl_ctx, die);
|
|
}
|
|
|
|
return type_sp;
|
|
}
|
|
|
|
static void ForcefullyCompleteType(CompilerType type) {
|
|
bool started = TypeSystemClang::StartTagDeclarationDefinition(type);
|
|
lldbassert(started && "Unable to start a class type definition.");
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(type);
|
|
const clang::TagDecl *td = ClangUtil::GetAsTagDecl(type);
|
|
auto ts_sp = type.GetTypeSystem();
|
|
auto ts = ts_sp.dyn_cast_or_null<TypeSystemClang>();
|
|
if (ts)
|
|
ts->SetDeclIsForcefullyCompleted(td);
|
|
}
|
|
|
|
/// This function serves a similar purpose as RequireCompleteType above, but it
|
|
/// avoids completing the type if it is not immediately necessary. It only
|
|
/// ensures we _can_ complete the type later.
|
|
static void PrepareContextToReceiveMembers(TypeSystemClang &ast,
|
|
ClangASTImporter &ast_importer,
|
|
clang::DeclContext *decl_ctx,
|
|
DWARFDIE die,
|
|
const char *type_name_cstr) {
|
|
auto *tag_decl_ctx = clang::dyn_cast<clang::TagDecl>(decl_ctx);
|
|
if (!tag_decl_ctx)
|
|
return; // Non-tag context are always ready.
|
|
|
|
// We have already completed the type, or we have found its definition and are
|
|
// ready to complete it later (cf. ParseStructureLikeDIE).
|
|
if (tag_decl_ctx->isCompleteDefinition() || tag_decl_ctx->isBeingDefined())
|
|
return;
|
|
|
|
// We reach this point of the tag was present in the debug info as a
|
|
// declaration only. If it was imported from another AST context (in the
|
|
// gmodules case), we can complete the type by doing a full import.
|
|
|
|
// If this type was not imported from an external AST, there's nothing to do.
|
|
CompilerType type = ast.GetTypeForDecl(tag_decl_ctx);
|
|
if (type && ast_importer.CanImport(type)) {
|
|
auto qual_type = ClangUtil::GetQualType(type);
|
|
if (ast_importer.RequireCompleteType(qual_type))
|
|
return;
|
|
die.GetDWARF()->GetObjectFile()->GetModule()->ReportError(
|
|
"Unable to complete the Decl context for DIE {0} at offset "
|
|
"{1:x16}.\nPlease file a bug report.",
|
|
type_name_cstr ? type_name_cstr : "", die.GetOffset());
|
|
}
|
|
|
|
// We don't have a type definition and/or the import failed. We must
|
|
// forcefully complete the type to avoid crashes.
|
|
ForcefullyCompleteType(type);
|
|
}
|
|
|
|
ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) {
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
for (size_t i = 0; i < attributes.Size(); ++i) {
|
|
dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (!attributes.ExtractFormValueAtIndex(i, form_value))
|
|
continue;
|
|
switch (attr) {
|
|
default:
|
|
break;
|
|
case DW_AT_abstract_origin:
|
|
abstract_origin = form_value;
|
|
break;
|
|
|
|
case DW_AT_accessibility:
|
|
accessibility =
|
|
DWARFASTParser::GetAccessTypeFromDWARF(form_value.Unsigned());
|
|
break;
|
|
|
|
case DW_AT_artificial:
|
|
is_artificial = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_bit_stride:
|
|
bit_stride = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_byte_size:
|
|
byte_size = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_alignment:
|
|
alignment = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_byte_stride:
|
|
byte_stride = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_calling_convention:
|
|
calling_convention = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_containing_type:
|
|
containing_type = form_value;
|
|
break;
|
|
|
|
case DW_AT_decl_file:
|
|
// die.GetCU() can differ if DW_AT_specification uses DW_FORM_ref_addr.
|
|
decl.SetFile(
|
|
attributes.CompileUnitAtIndex(i)->GetFile(form_value.Unsigned()));
|
|
break;
|
|
case DW_AT_decl_line:
|
|
decl.SetLine(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_decl_column:
|
|
decl.SetColumn(form_value.Unsigned());
|
|
break;
|
|
|
|
case DW_AT_declaration:
|
|
is_forward_declaration = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_encoding:
|
|
encoding = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_enum_class:
|
|
is_scoped_enum = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_explicit:
|
|
is_explicit = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_external:
|
|
if (form_value.Unsigned())
|
|
storage = clang::SC_Extern;
|
|
break;
|
|
|
|
case DW_AT_inline:
|
|
is_inline = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_linkage_name:
|
|
case DW_AT_MIPS_linkage_name:
|
|
mangled_name = form_value.AsCString();
|
|
break;
|
|
|
|
case DW_AT_name:
|
|
name.SetCString(form_value.AsCString());
|
|
break;
|
|
|
|
case DW_AT_object_pointer:
|
|
object_pointer = form_value.Reference();
|
|
break;
|
|
|
|
case DW_AT_signature:
|
|
signature = form_value;
|
|
break;
|
|
|
|
case DW_AT_specification:
|
|
specification = form_value;
|
|
break;
|
|
|
|
case DW_AT_type:
|
|
type = form_value;
|
|
break;
|
|
|
|
case DW_AT_virtuality:
|
|
is_virtual = form_value.Boolean();
|
|
break;
|
|
|
|
case DW_AT_APPLE_objc_complete_type:
|
|
is_complete_objc_class = form_value.Signed();
|
|
break;
|
|
|
|
case DW_AT_APPLE_objc_direct:
|
|
is_objc_direct_call = true;
|
|
break;
|
|
|
|
case DW_AT_APPLE_runtime_class:
|
|
class_language = (LanguageType)form_value.Signed();
|
|
break;
|
|
|
|
case DW_AT_GNU_vector:
|
|
is_vector = form_value.Boolean();
|
|
break;
|
|
case DW_AT_export_symbols:
|
|
exports_symbols = form_value.Boolean();
|
|
break;
|
|
case DW_AT_rvalue_reference:
|
|
ref_qual = clang::RQ_RValue;
|
|
break;
|
|
case DW_AT_reference:
|
|
ref_qual = clang::RQ_LValue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::string GetUnitName(const DWARFDIE &die) {
|
|
if (DWARFUnit *unit = die.GetCU())
|
|
return unit->GetAbsolutePath().GetPath();
|
|
return "<missing DWARF unit path>";
|
|
}
|
|
|
|
TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
bool *type_is_new_ptr) {
|
|
if (type_is_new_ptr)
|
|
*type_is_new_ptr = false;
|
|
|
|
if (!die)
|
|
return nullptr;
|
|
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
if (log) {
|
|
DWARFDIE context_die;
|
|
clang::DeclContext *context =
|
|
GetClangDeclContextContainingDIE(die, &context_die);
|
|
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"DWARFASTParserClang::ParseTypeFromDWARF "
|
|
"(die = {0:x16}, decl_ctx = {1:p} (die "
|
|
"{2:x16})) {3} name = '{4}')",
|
|
die.GetOffset(), static_cast<void *>(context), context_die.GetOffset(),
|
|
die.GetTagAsCString(), die.GetName());
|
|
}
|
|
|
|
Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE());
|
|
if (type_ptr == DIE_IS_BEING_PARSED)
|
|
return nullptr;
|
|
if (type_ptr)
|
|
return type_ptr->shared_from_this();
|
|
// Set a bit that lets us know that we are currently parsing this
|
|
dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED;
|
|
|
|
ParsedDWARFTypeAttributes attrs(die);
|
|
|
|
if (DWARFDIE signature_die = attrs.signature.Reference()) {
|
|
if (TypeSP type_sp =
|
|
ParseTypeFromDWARF(sc, signature_die, type_is_new_ptr)) {
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
if (clang::DeclContext *decl_ctx =
|
|
GetCachedClangDeclContextForDIE(signature_die))
|
|
LinkDeclContextToDIE(decl_ctx, die);
|
|
return type_sp;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
if (type_is_new_ptr)
|
|
*type_is_new_ptr = true;
|
|
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
TypeSP type_sp;
|
|
|
|
switch (tag) {
|
|
case DW_TAG_typedef:
|
|
case DW_TAG_base_type:
|
|
case DW_TAG_pointer_type:
|
|
case DW_TAG_reference_type:
|
|
case DW_TAG_rvalue_reference_type:
|
|
case DW_TAG_const_type:
|
|
case DW_TAG_restrict_type:
|
|
case DW_TAG_volatile_type:
|
|
case DW_TAG_atomic_type:
|
|
case DW_TAG_unspecified_type: {
|
|
type_sp = ParseTypeModifier(sc, die, attrs);
|
|
break;
|
|
}
|
|
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_class_type: {
|
|
type_sp = ParseStructureLikeDIE(sc, die, attrs);
|
|
break;
|
|
}
|
|
|
|
case DW_TAG_enumeration_type: {
|
|
type_sp = ParseEnum(sc, die, attrs);
|
|
break;
|
|
}
|
|
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_subroutine_type: {
|
|
type_sp = ParseSubroutine(die, attrs);
|
|
break;
|
|
}
|
|
case DW_TAG_array_type: {
|
|
type_sp = ParseArrayType(die, attrs);
|
|
break;
|
|
}
|
|
case DW_TAG_ptr_to_member_type: {
|
|
type_sp = ParsePointerToMemberType(die, attrs);
|
|
break;
|
|
}
|
|
default:
|
|
dwarf->GetObjectFile()->GetModule()->ReportError(
|
|
"[{0:x16}]: unhandled type tag {1:x4} ({2}), "
|
|
"please file a bug and "
|
|
"attach the file at the start of this error message",
|
|
die.GetOffset(), tag, DW_TAG_value_to_name(tag));
|
|
break;
|
|
}
|
|
|
|
// TODO: We should consider making the switch above exhaustive to simplify
|
|
// control flow in ParseTypeFromDWARF. Then, we could simply replace this
|
|
// return statement with a call to llvm_unreachable.
|
|
return UpdateSymbolContextScopeForType(sc, die, type_sp);
|
|
}
|
|
|
|
static std::optional<uint32_t>
|
|
ExtractDataMemberLocation(DWARFDIE const &die, DWARFFormValue const &form_value,
|
|
ModuleSP module_sp) {
|
|
// With DWARF 3 and later, if the value is an integer constant,
|
|
// this form value is the offset in bytes from the beginning of
|
|
// the containing entity.
|
|
if (!form_value.BlockData())
|
|
return form_value.Unsigned();
|
|
|
|
Value initialValue(0);
|
|
Value memberOffset(0);
|
|
const DWARFDataExtractor &debug_info_data = die.GetData();
|
|
uint32_t block_length = form_value.Unsigned();
|
|
uint32_t block_offset =
|
|
form_value.BlockData() - debug_info_data.GetDataStart();
|
|
if (!DWARFExpression::Evaluate(
|
|
nullptr, // ExecutionContext *
|
|
nullptr, // RegisterContext *
|
|
module_sp, DataExtractor(debug_info_data, block_offset, block_length),
|
|
die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr, memberOffset,
|
|
nullptr)) {
|
|
return {};
|
|
}
|
|
|
|
return memberOffset.ResolveValue(nullptr).UInt();
|
|
}
|
|
|
|
lldb::TypeSP
|
|
DWARFASTParserClang::ParseTypeModifier(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
ParsedDWARFTypeAttributes &attrs) {
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
const dw_tag_t tag = die.Tag();
|
|
LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU());
|
|
Type::ResolveState resolve_state = Type::ResolveState::Unresolved;
|
|
Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID;
|
|
TypeSP type_sp;
|
|
CompilerType clang_type;
|
|
|
|
if (tag == DW_TAG_typedef) {
|
|
// DeclContext will be populated when the clang type is materialized in
|
|
// Type::ResolveCompilerType.
|
|
PrepareContextToReceiveMembers(
|
|
m_ast, GetClangASTImporter(),
|
|
GetClangDeclContextContainingDIE(die, nullptr), die,
|
|
attrs.name.GetCString());
|
|
|
|
if (attrs.type.IsValid()) {
|
|
// Try to parse a typedef from the (DWARF embedded in the) Clang
|
|
// module file first as modules can contain typedef'ed
|
|
// structures that have no names like:
|
|
//
|
|
// typedef struct { int a; } Foo;
|
|
//
|
|
// In this case we will have a structure with no name and a
|
|
// typedef named "Foo" that points to this unnamed
|
|
// structure. The name in the typedef is the only identifier for
|
|
// the struct, so always try to get typedefs from Clang modules
|
|
// if possible.
|
|
//
|
|
// The type_sp returned will be empty if the typedef doesn't
|
|
// exist in a module file, so it is cheap to call this function
|
|
// just to check.
|
|
//
|
|
// If we don't do this we end up creating a TypeSP that says
|
|
// this is a typedef to type 0x123 (the DW_AT_type value would
|
|
// be 0x123 in the DW_TAG_typedef), and this is the unnamed
|
|
// structure type. We will have a hard time tracking down an
|
|
// unnammed structure type in the module debug info, so we make
|
|
// sure we don't get into this situation by always resolving
|
|
// typedefs from the module.
|
|
const DWARFDIE encoding_die = attrs.type.Reference();
|
|
|
|
// First make sure that the die that this is typedef'ed to _is_
|
|
// just a declaration (DW_AT_declaration == 1), not a full
|
|
// definition since template types can't be represented in
|
|
// modules since only concrete instances of templates are ever
|
|
// emitted and modules won't contain those
|
|
if (encoding_die &&
|
|
encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) {
|
|
type_sp = ParseTypeFromClangModule(sc, die, log);
|
|
if (type_sp)
|
|
return type_sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n", die.GetID(),
|
|
DW_TAG_value_to_name(tag), type_name_cstr,
|
|
encoding_uid.Reference());
|
|
|
|
switch (tag) {
|
|
default:
|
|
break;
|
|
|
|
case DW_TAG_unspecified_type:
|
|
if (attrs.name == "nullptr_t" || attrs.name == "decltype(nullptr)") {
|
|
resolve_state = Type::ResolveState::Full;
|
|
clang_type = m_ast.GetBasicType(eBasicTypeNullPtr);
|
|
break;
|
|
}
|
|
// Fall through to base type below in case we can handle the type
|
|
// there...
|
|
[[fallthrough]];
|
|
|
|
case DW_TAG_base_type:
|
|
resolve_state = Type::ResolveState::Full;
|
|
clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
|
|
attrs.name.GetStringRef(), attrs.encoding,
|
|
attrs.byte_size.value_or(0) * 8);
|
|
break;
|
|
|
|
case DW_TAG_pointer_type:
|
|
encoding_data_type = Type::eEncodingIsPointerUID;
|
|
break;
|
|
case DW_TAG_reference_type:
|
|
encoding_data_type = Type::eEncodingIsLValueReferenceUID;
|
|
break;
|
|
case DW_TAG_rvalue_reference_type:
|
|
encoding_data_type = Type::eEncodingIsRValueReferenceUID;
|
|
break;
|
|
case DW_TAG_typedef:
|
|
encoding_data_type = Type::eEncodingIsTypedefUID;
|
|
break;
|
|
case DW_TAG_const_type:
|
|
encoding_data_type = Type::eEncodingIsConstUID;
|
|
break;
|
|
case DW_TAG_restrict_type:
|
|
encoding_data_type = Type::eEncodingIsRestrictUID;
|
|
break;
|
|
case DW_TAG_volatile_type:
|
|
encoding_data_type = Type::eEncodingIsVolatileUID;
|
|
break;
|
|
case DW_TAG_atomic_type:
|
|
encoding_data_type = Type::eEncodingIsAtomicUID;
|
|
break;
|
|
}
|
|
|
|
if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID ||
|
|
encoding_data_type == Type::eEncodingIsTypedefUID)) {
|
|
if (tag == DW_TAG_pointer_type) {
|
|
DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type);
|
|
|
|
if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) {
|
|
// Blocks have a __FuncPtr inside them which is a pointer to a
|
|
// function of the proper type.
|
|
|
|
for (DWARFDIE child_die : target_die.children()) {
|
|
if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""),
|
|
"__FuncPtr")) {
|
|
DWARFDIE function_pointer_type =
|
|
child_die.GetReferencedDIE(DW_AT_type);
|
|
|
|
if (function_pointer_type) {
|
|
DWARFDIE function_type =
|
|
function_pointer_type.GetReferencedDIE(DW_AT_type);
|
|
|
|
bool function_type_is_new_pointer;
|
|
TypeSP lldb_function_type_sp = ParseTypeFromDWARF(
|
|
sc, function_type, &function_type_is_new_pointer);
|
|
|
|
if (lldb_function_type_sp) {
|
|
clang_type = m_ast.CreateBlockPointerType(
|
|
lldb_function_type_sp->GetForwardCompilerType());
|
|
encoding_data_type = Type::eEncodingIsUID;
|
|
attrs.type.Clear();
|
|
resolve_state = Type::ResolveState::Full;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cu_language == eLanguageTypeObjC ||
|
|
cu_language == eLanguageTypeObjC_plus_plus) {
|
|
if (attrs.name) {
|
|
if (attrs.name == "id") {
|
|
if (log)
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::ParseType (die = {0:x16}) {1} '{2}' "
|
|
"is Objective-C 'id' built-in type.",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
|
|
encoding_data_type = Type::eEncodingIsUID;
|
|
attrs.type.Clear();
|
|
resolve_state = Type::ResolveState::Full;
|
|
} else if (attrs.name == "Class") {
|
|
if (log)
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::ParseType (die = {0:x16}) {1} '{2}' "
|
|
"is Objective-C 'Class' built-in type.",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
clang_type = m_ast.GetBasicType(eBasicTypeObjCClass);
|
|
encoding_data_type = Type::eEncodingIsUID;
|
|
attrs.type.Clear();
|
|
resolve_state = Type::ResolveState::Full;
|
|
} else if (attrs.name == "SEL") {
|
|
if (log)
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::ParseType (die = {0:x16}) {1} '{2}' "
|
|
"is Objective-C 'selector' built-in type.",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
clang_type = m_ast.GetBasicType(eBasicTypeObjCSel);
|
|
encoding_data_type = Type::eEncodingIsUID;
|
|
attrs.type.Clear();
|
|
resolve_state = Type::ResolveState::Full;
|
|
}
|
|
} else if (encoding_data_type == Type::eEncodingIsPointerUID &&
|
|
attrs.type.IsValid()) {
|
|
// Clang sometimes erroneously emits id as objc_object*. In that
|
|
// case we fix up the type to "id".
|
|
|
|
const DWARFDIE encoding_die = attrs.type.Reference();
|
|
|
|
if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) {
|
|
llvm::StringRef struct_name = encoding_die.GetName();
|
|
if (struct_name == "objc_object") {
|
|
if (log)
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::ParseType (die = {0:x16}) {1} "
|
|
"'{2}' is 'objc_object*', which we overrode to "
|
|
"'id'.",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
clang_type = m_ast.GetBasicType(eBasicTypeObjCID);
|
|
encoding_data_type = Type::eEncodingIsUID;
|
|
attrs.type.Clear();
|
|
resolve_state = Type::ResolveState::Full;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type_sp = dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr,
|
|
attrs.type.Reference().GetID(), encoding_data_type,
|
|
&attrs.decl, clang_type, resolve_state,
|
|
TypePayloadClang(GetOwningClangModule(die)));
|
|
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
return type_sp;
|
|
}
|
|
|
|
ConstString
|
|
DWARFASTParserClang::GetDIEClassTemplateParams(const DWARFDIE &die) {
|
|
if (llvm::StringRef(die.GetName()).contains("<"))
|
|
return ConstString();
|
|
|
|
TypeSystemClang::TemplateParameterInfos template_param_infos;
|
|
if (ParseTemplateParameterInfos(die, template_param_infos)) {
|
|
return ConstString(m_ast.PrintTemplateParams(template_param_infos));
|
|
}
|
|
return ConstString();
|
|
}
|
|
|
|
TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
ParsedDWARFTypeAttributes &attrs) {
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
const dw_tag_t tag = die.Tag();
|
|
TypeSP type_sp;
|
|
|
|
if (attrs.is_forward_declaration) {
|
|
type_sp = ParseTypeFromClangModule(sc, die, log);
|
|
if (type_sp)
|
|
return type_sp;
|
|
|
|
type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die);
|
|
|
|
if (!type_sp) {
|
|
SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile();
|
|
if (debug_map_symfile) {
|
|
// We weren't able to find a full declaration in this DWARF,
|
|
// see if we have a declaration anywhere else...
|
|
type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(die);
|
|
}
|
|
}
|
|
|
|
if (type_sp) {
|
|
if (log) {
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF({0:p}) - {1:x16}}: {2} type \"{3}\" is a "
|
|
"forward declaration, complete type is {4:x8}",
|
|
static_cast<void *>(this), die.GetOffset(),
|
|
DW_TAG_value_to_name(tag), attrs.name.GetCString(),
|
|
type_sp->GetID());
|
|
}
|
|
|
|
// We found a real definition for this type elsewhere so lets use
|
|
// it and cache the fact that we found a complete type for this
|
|
// die
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
clang::DeclContext *defn_decl_ctx =
|
|
GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
|
|
if (defn_decl_ctx)
|
|
LinkDeclContextToDIE(defn_decl_ctx, die);
|
|
return type_sp;
|
|
}
|
|
}
|
|
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
|
|
DW_TAG_value_to_name(tag), type_name_cstr);
|
|
|
|
CompilerType enumerator_clang_type;
|
|
CompilerType clang_type;
|
|
clang_type = CompilerType(
|
|
m_ast.weak_from_this(),
|
|
dwarf->GetForwardDeclDIEToCompilerType().lookup(die.GetDIE()));
|
|
if (!clang_type) {
|
|
if (attrs.type.IsValid()) {
|
|
Type *enumerator_type =
|
|
dwarf->ResolveTypeUID(attrs.type.Reference(), true);
|
|
if (enumerator_type)
|
|
enumerator_clang_type = enumerator_type->GetFullCompilerType();
|
|
}
|
|
|
|
if (!enumerator_clang_type) {
|
|
if (attrs.byte_size) {
|
|
enumerator_clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize(
|
|
"", DW_ATE_signed, *attrs.byte_size * 8);
|
|
} else {
|
|
enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt);
|
|
}
|
|
}
|
|
|
|
clang_type = m_ast.CreateEnumerationType(
|
|
attrs.name.GetStringRef(),
|
|
GetClangDeclContextContainingDIE(die, nullptr),
|
|
GetOwningClangModule(die), attrs.decl, enumerator_clang_type,
|
|
attrs.is_scoped_enum);
|
|
} else {
|
|
enumerator_clang_type = m_ast.GetEnumerationIntegerType(clang_type);
|
|
}
|
|
|
|
LinkDeclContextToDIE(TypeSystemClang::GetDeclContextForType(clang_type), die);
|
|
|
|
type_sp =
|
|
dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr,
|
|
attrs.type.Reference().GetID(), Type::eEncodingIsUID,
|
|
&attrs.decl, clang_type, Type::ResolveState::Forward,
|
|
TypePayloadClang(GetOwningClangModule(die)));
|
|
|
|
if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) {
|
|
if (die.HasChildren()) {
|
|
bool is_signed = false;
|
|
enumerator_clang_type.IsIntegerType(is_signed);
|
|
ParseChildEnumerators(clang_type, is_signed,
|
|
type_sp->GetByteSize(nullptr).value_or(0), die);
|
|
}
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(clang_type);
|
|
} else {
|
|
dwarf->GetObjectFile()->GetModule()->ReportError(
|
|
"DWARF DIE at {0:x16} named \"{1}\" was not able to start its "
|
|
"definition.\nPlease file a bug and attach the file at the "
|
|
"start of this error message",
|
|
die.GetOffset(), attrs.name.GetCString());
|
|
}
|
|
return type_sp;
|
|
}
|
|
|
|
static clang::CallingConv
|
|
ConvertDWARFCallingConventionToClang(const ParsedDWARFTypeAttributes &attrs) {
|
|
switch (attrs.calling_convention) {
|
|
case llvm::dwarf::DW_CC_normal:
|
|
return clang::CC_C;
|
|
case llvm::dwarf::DW_CC_BORLAND_stdcall:
|
|
return clang::CC_X86StdCall;
|
|
case llvm::dwarf::DW_CC_BORLAND_msfastcall:
|
|
return clang::CC_X86FastCall;
|
|
case llvm::dwarf::DW_CC_LLVM_vectorcall:
|
|
return clang::CC_X86VectorCall;
|
|
case llvm::dwarf::DW_CC_BORLAND_pascal:
|
|
return clang::CC_X86Pascal;
|
|
case llvm::dwarf::DW_CC_LLVM_Win64:
|
|
return clang::CC_Win64;
|
|
case llvm::dwarf::DW_CC_LLVM_X86_64SysV:
|
|
return clang::CC_X86_64SysV;
|
|
case llvm::dwarf::DW_CC_LLVM_X86RegCall:
|
|
return clang::CC_X86RegCall;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
LLDB_LOG(log, "Unsupported DW_AT_calling_convention value: {0}",
|
|
attrs.calling_convention);
|
|
// Use the default calling convention as a fallback.
|
|
return clang::CC_C;
|
|
}
|
|
|
|
TypeSP
|
|
DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
|
|
const ParsedDWARFTypeAttributes &attrs) {
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
bool is_variadic = false;
|
|
bool is_static = false;
|
|
bool has_template_params = false;
|
|
|
|
unsigned type_quals = 0;
|
|
|
|
std::string object_pointer_name;
|
|
if (attrs.object_pointer) {
|
|
const char *object_pointer_name_cstr = attrs.object_pointer.GetName();
|
|
if (object_pointer_name_cstr)
|
|
object_pointer_name = object_pointer_name_cstr;
|
|
}
|
|
|
|
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
|
|
DW_TAG_value_to_name(tag), type_name_cstr);
|
|
|
|
CompilerType return_clang_type;
|
|
Type *func_type = nullptr;
|
|
|
|
if (attrs.type.IsValid())
|
|
func_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
|
|
|
|
if (func_type)
|
|
return_clang_type = func_type->GetForwardCompilerType();
|
|
else
|
|
return_clang_type = m_ast.GetBasicType(eBasicTypeVoid);
|
|
|
|
std::vector<CompilerType> function_param_types;
|
|
std::vector<clang::ParmVarDecl *> function_param_decls;
|
|
|
|
// Parse the function children for the parameters
|
|
|
|
DWARFDIE decl_ctx_die;
|
|
clang::DeclContext *containing_decl_ctx =
|
|
GetClangDeclContextContainingDIE(die, &decl_ctx_die);
|
|
const clang::Decl::Kind containing_decl_kind =
|
|
containing_decl_ctx->getDeclKind();
|
|
|
|
bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind);
|
|
// Start off static. This will be set to false in
|
|
// ParseChildParameters(...) if we find a "this" parameters as the
|
|
// first parameter
|
|
if (is_cxx_method) {
|
|
is_static = true;
|
|
}
|
|
|
|
if (die.HasChildren()) {
|
|
bool skip_artificial = true;
|
|
ParseChildParameters(containing_decl_ctx, die, skip_artificial, is_static,
|
|
is_variadic, has_template_params,
|
|
function_param_types, function_param_decls,
|
|
type_quals);
|
|
}
|
|
|
|
bool ignore_containing_context = false;
|
|
// Check for templatized class member functions. If we had any
|
|
// DW_TAG_template_type_parameter or DW_TAG_template_value_parameter
|
|
// the DW_TAG_subprogram DIE, then we can't let this become a method in
|
|
// a class. Why? Because templatized functions are only emitted if one
|
|
// of the templatized methods is used in the current compile unit and
|
|
// we will end up with classes that may or may not include these member
|
|
// functions and this means one class won't match another class
|
|
// definition and it affects our ability to use a class in the clang
|
|
// expression parser. So for the greater good, we currently must not
|
|
// allow any template member functions in a class definition.
|
|
if (is_cxx_method && has_template_params) {
|
|
ignore_containing_context = true;
|
|
is_cxx_method = false;
|
|
}
|
|
|
|
clang::CallingConv calling_convention =
|
|
ConvertDWARFCallingConventionToClang(attrs);
|
|
|
|
// clang_type will get the function prototype clang type after this
|
|
// call
|
|
CompilerType clang_type =
|
|
m_ast.CreateFunctionType(return_clang_type, function_param_types.data(),
|
|
function_param_types.size(), is_variadic,
|
|
type_quals, calling_convention, attrs.ref_qual);
|
|
|
|
if (attrs.name) {
|
|
bool type_handled = false;
|
|
if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) {
|
|
std::optional<const ObjCLanguage::MethodName> objc_method =
|
|
ObjCLanguage::MethodName::Create(attrs.name.GetStringRef(), true);
|
|
if (objc_method) {
|
|
CompilerType class_opaque_type;
|
|
ConstString class_name(objc_method->GetClassName());
|
|
if (class_name) {
|
|
TypeSP complete_objc_class_type_sp(
|
|
dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(),
|
|
class_name, false));
|
|
|
|
if (complete_objc_class_type_sp) {
|
|
CompilerType type_clang_forward_type =
|
|
complete_objc_class_type_sp->GetForwardCompilerType();
|
|
if (TypeSystemClang::IsObjCObjectOrInterfaceType(
|
|
type_clang_forward_type))
|
|
class_opaque_type = type_clang_forward_type;
|
|
}
|
|
}
|
|
|
|
if (class_opaque_type) {
|
|
clang::ObjCMethodDecl *objc_method_decl =
|
|
m_ast.AddMethodToObjCObjectType(
|
|
class_opaque_type, attrs.name.GetCString(), clang_type,
|
|
attrs.is_artificial, is_variadic, attrs.is_objc_direct_call);
|
|
type_handled = objc_method_decl != nullptr;
|
|
if (type_handled) {
|
|
LinkDeclContextToDIE(objc_method_decl, die);
|
|
m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID());
|
|
} else {
|
|
dwarf->GetObjectFile()->GetModule()->ReportError(
|
|
"[{0:x16}]: invalid Objective-C method {1:x4} ({2}), "
|
|
"please file a bug and attach the file at the start of "
|
|
"this error message",
|
|
die.GetOffset(), tag, DW_TAG_value_to_name(tag));
|
|
}
|
|
}
|
|
} else if (is_cxx_method) {
|
|
// Look at the parent of this DIE and see if it is a class or
|
|
// struct and see if this is actually a C++ method
|
|
Type *class_type = dwarf->ResolveType(decl_ctx_die);
|
|
if (class_type) {
|
|
if (class_type->GetID() != decl_ctx_die.GetID() ||
|
|
IsClangModuleFwdDecl(decl_ctx_die)) {
|
|
|
|
// We uniqued the parent class of this function to another
|
|
// class so we now need to associate all dies under
|
|
// "decl_ctx_die" to DIEs in the DIE for "class_type"...
|
|
DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID());
|
|
|
|
if (class_type_die) {
|
|
std::vector<DWARFDIE> failures;
|
|
|
|
CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die,
|
|
class_type, failures);
|
|
|
|
// FIXME do something with these failures that's
|
|
// smarter than just dropping them on the ground.
|
|
// Unfortunately classes don't like having stuff added
|
|
// to them after their definitions are complete...
|
|
|
|
Type *type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
|
|
if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
|
|
return type_ptr->shared_from_this();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (attrs.specification.IsValid()) {
|
|
// We have a specification which we are going to base our
|
|
// function prototype off of, so we need this type to be
|
|
// completed so that the m_die_to_decl_ctx for the method in
|
|
// the specification has a valid clang decl context.
|
|
class_type->GetForwardCompilerType();
|
|
// If we have a specification, then the function type should
|
|
// have been made with the specification and not with this
|
|
// die.
|
|
DWARFDIE spec_die = attrs.specification.Reference();
|
|
clang::DeclContext *spec_clang_decl_ctx =
|
|
GetClangDeclContextForDIE(spec_die);
|
|
if (spec_clang_decl_ctx) {
|
|
LinkDeclContextToDIE(spec_clang_decl_ctx, die);
|
|
} else {
|
|
dwarf->GetObjectFile()->GetModule()->ReportWarning(
|
|
"{0:x8}: DW_AT_specification({1:x16}"
|
|
") has no decl\n",
|
|
die.GetID(), spec_die.GetOffset());
|
|
}
|
|
type_handled = true;
|
|
} else if (attrs.abstract_origin.IsValid()) {
|
|
// We have a specification which we are going to base our
|
|
// function prototype off of, so we need this type to be
|
|
// completed so that the m_die_to_decl_ctx for the method in
|
|
// the abstract origin has a valid clang decl context.
|
|
class_type->GetForwardCompilerType();
|
|
|
|
DWARFDIE abs_die = attrs.abstract_origin.Reference();
|
|
clang::DeclContext *abs_clang_decl_ctx =
|
|
GetClangDeclContextForDIE(abs_die);
|
|
if (abs_clang_decl_ctx) {
|
|
LinkDeclContextToDIE(abs_clang_decl_ctx, die);
|
|
} else {
|
|
dwarf->GetObjectFile()->GetModule()->ReportWarning(
|
|
"{0:x8}: DW_AT_abstract_origin({1:x16}"
|
|
") has no decl\n",
|
|
die.GetID(), abs_die.GetOffset());
|
|
}
|
|
type_handled = true;
|
|
} else {
|
|
CompilerType class_opaque_type =
|
|
class_type->GetForwardCompilerType();
|
|
if (TypeSystemClang::IsCXXClassType(class_opaque_type)) {
|
|
if (class_opaque_type.IsBeingDefined()) {
|
|
if (!is_static && !die.HasChildren()) {
|
|
// We have a C++ member function with no children (this
|
|
// pointer!) and clang will get mad if we try and make
|
|
// a function that isn't well formed in the DWARF, so
|
|
// we will just skip it...
|
|
type_handled = true;
|
|
} else {
|
|
llvm::PrettyStackTraceFormat stack_trace(
|
|
"SymbolFileDWARF::ParseType() is adding a method "
|
|
"%s to class %s in DIE 0x%8.8" PRIx64 " from %s",
|
|
attrs.name.GetCString(),
|
|
class_type->GetName().GetCString(), die.GetID(),
|
|
dwarf->GetObjectFile()->GetFileSpec().GetPath().c_str());
|
|
|
|
const bool is_attr_used = false;
|
|
// Neither GCC 4.2 nor clang++ currently set a valid
|
|
// accessibility in the DWARF for C++ methods...
|
|
// Default to public for now...
|
|
const auto accessibility = attrs.accessibility == eAccessNone
|
|
? eAccessPublic
|
|
: attrs.accessibility;
|
|
|
|
clang::CXXMethodDecl *cxx_method_decl =
|
|
m_ast.AddMethodToCXXRecordType(
|
|
class_opaque_type.GetOpaqueQualType(),
|
|
attrs.name.GetCString(), attrs.mangled_name,
|
|
clang_type, accessibility, attrs.is_virtual,
|
|
is_static, attrs.is_inline, attrs.is_explicit,
|
|
is_attr_used, attrs.is_artificial);
|
|
|
|
type_handled = cxx_method_decl != nullptr;
|
|
// Artificial methods are always handled even when we
|
|
// don't create a new declaration for them.
|
|
type_handled |= attrs.is_artificial;
|
|
|
|
if (cxx_method_decl) {
|
|
LinkDeclContextToDIE(cxx_method_decl, die);
|
|
|
|
ClangASTMetadata metadata;
|
|
metadata.SetUserID(die.GetID());
|
|
|
|
if (!object_pointer_name.empty()) {
|
|
metadata.SetObjectPtrName(object_pointer_name.c_str());
|
|
LLDB_LOGF(log,
|
|
"Setting object pointer name: %s on method "
|
|
"object %p.\n",
|
|
object_pointer_name.c_str(),
|
|
static_cast<void *>(cxx_method_decl));
|
|
}
|
|
m_ast.SetMetadata(cxx_method_decl, metadata);
|
|
} else {
|
|
ignore_containing_context = true;
|
|
}
|
|
}
|
|
} else {
|
|
// We were asked to parse the type for a method in a
|
|
// class, yet the class hasn't been asked to complete
|
|
// itself through the clang::ExternalASTSource protocol,
|
|
// so we need to just have the class complete itself and
|
|
// do things the right way, then our
|
|
// DIE should then have an entry in the
|
|
// dwarf->GetDIEToType() map. First
|
|
// we need to modify the dwarf->GetDIEToType() so it
|
|
// doesn't think we are trying to parse this DIE
|
|
// anymore...
|
|
dwarf->GetDIEToType()[die.GetDIE()] = NULL;
|
|
|
|
// Now we get the full type to force our class type to
|
|
// complete itself using the clang::ExternalASTSource
|
|
// protocol which will parse all base classes and all
|
|
// methods (including the method for this DIE).
|
|
class_type->GetFullCompilerType();
|
|
|
|
// The type for this DIE should have been filled in the
|
|
// function call above.
|
|
Type *type_ptr = dwarf->GetDIEToType()[die.GetDIE()];
|
|
if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) {
|
|
return type_ptr->shared_from_this();
|
|
}
|
|
|
|
// The previous comment isn't actually true if the class wasn't
|
|
// resolved using the current method's parent DIE as source
|
|
// data. We need to ensure that we look up the method correctly
|
|
// in the class and then link the method's DIE to the unique
|
|
// CXXMethodDecl appropriately.
|
|
type_handled = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!type_handled) {
|
|
clang::FunctionDecl *function_decl = nullptr;
|
|
clang::FunctionDecl *template_function_decl = nullptr;
|
|
|
|
if (attrs.abstract_origin.IsValid()) {
|
|
DWARFDIE abs_die = attrs.abstract_origin.Reference();
|
|
|
|
if (dwarf->ResolveType(abs_die)) {
|
|
function_decl = llvm::dyn_cast_or_null<clang::FunctionDecl>(
|
|
GetCachedClangDeclContextForDIE(abs_die));
|
|
|
|
if (function_decl) {
|
|
LinkDeclContextToDIE(function_decl, die);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function_decl) {
|
|
char *name_buf = nullptr;
|
|
llvm::StringRef name = attrs.name.GetStringRef();
|
|
|
|
// We currently generate function templates with template parameters in
|
|
// their name. In order to get closer to the AST that clang generates
|
|
// we want to strip these from the name when creating the AST.
|
|
if (attrs.mangled_name) {
|
|
llvm::ItaniumPartialDemangler D;
|
|
if (!D.partialDemangle(attrs.mangled_name)) {
|
|
name_buf = D.getFunctionBaseName(nullptr, nullptr);
|
|
name = name_buf;
|
|
}
|
|
}
|
|
|
|
// We just have a function that isn't part of a class
|
|
function_decl = m_ast.CreateFunctionDeclaration(
|
|
ignore_containing_context ? m_ast.GetTranslationUnitDecl()
|
|
: containing_decl_ctx,
|
|
GetOwningClangModule(die), name, clang_type, attrs.storage,
|
|
attrs.is_inline);
|
|
std::free(name_buf);
|
|
|
|
if (has_template_params) {
|
|
TypeSystemClang::TemplateParameterInfos template_param_infos;
|
|
ParseTemplateParameterInfos(die, template_param_infos);
|
|
template_function_decl = m_ast.CreateFunctionDeclaration(
|
|
ignore_containing_context ? m_ast.GetTranslationUnitDecl()
|
|
: containing_decl_ctx,
|
|
GetOwningClangModule(die), attrs.name.GetStringRef(), clang_type,
|
|
attrs.storage, attrs.is_inline);
|
|
clang::FunctionTemplateDecl *func_template_decl =
|
|
m_ast.CreateFunctionTemplateDecl(
|
|
containing_decl_ctx, GetOwningClangModule(die),
|
|
template_function_decl, template_param_infos);
|
|
m_ast.CreateFunctionTemplateSpecializationInfo(
|
|
template_function_decl, func_template_decl, template_param_infos);
|
|
}
|
|
|
|
lldbassert(function_decl);
|
|
|
|
if (function_decl) {
|
|
// Attach an asm(<mangled_name>) label to the FunctionDecl.
|
|
// This ensures that clang::CodeGen emits function calls
|
|
// using symbols that are mangled according to the DW_AT_linkage_name.
|
|
// If we didn't do this, the external symbols wouldn't exactly
|
|
// match the mangled name LLDB knows about and the IRExecutionUnit
|
|
// would have to fall back to searching object files for
|
|
// approximately matching function names. The motivating
|
|
// example is generating calls to ABI-tagged template functions.
|
|
// This is done separately for member functions in
|
|
// AddMethodToCXXRecordType.
|
|
if (attrs.mangled_name)
|
|
function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
|
|
m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false));
|
|
|
|
LinkDeclContextToDIE(function_decl, die);
|
|
|
|
if (!function_param_decls.empty()) {
|
|
m_ast.SetFunctionParameters(function_decl, function_param_decls);
|
|
if (template_function_decl)
|
|
m_ast.SetFunctionParameters(template_function_decl,
|
|
function_param_decls);
|
|
}
|
|
|
|
ClangASTMetadata metadata;
|
|
metadata.SetUserID(die.GetID());
|
|
|
|
if (!object_pointer_name.empty()) {
|
|
metadata.SetObjectPtrName(object_pointer_name.c_str());
|
|
LLDB_LOGF(log,
|
|
"Setting object pointer name: %s on function "
|
|
"object %p.",
|
|
object_pointer_name.c_str(),
|
|
static_cast<void *>(function_decl));
|
|
}
|
|
m_ast.SetMetadata(function_decl, metadata);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return dwarf->MakeType(
|
|
die.GetID(), attrs.name, std::nullopt, nullptr, LLDB_INVALID_UID,
|
|
Type::eEncodingIsUID, &attrs.decl, clang_type, Type::ResolveState::Full);
|
|
}
|
|
|
|
TypeSP
|
|
DWARFASTParserClang::ParseArrayType(const DWARFDIE &die,
|
|
const ParsedDWARFTypeAttributes &attrs) {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
|
|
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
|
|
DW_TAG_value_to_name(tag), type_name_cstr);
|
|
|
|
DWARFDIE type_die = attrs.type.Reference();
|
|
Type *element_type = dwarf->ResolveTypeUID(type_die, true);
|
|
|
|
if (!element_type)
|
|
return nullptr;
|
|
|
|
std::optional<SymbolFile::ArrayInfo> array_info = ParseChildArrayInfo(die);
|
|
uint32_t byte_stride = attrs.byte_stride;
|
|
uint32_t bit_stride = attrs.bit_stride;
|
|
if (array_info) {
|
|
byte_stride = array_info->byte_stride;
|
|
bit_stride = array_info->bit_stride;
|
|
}
|
|
if (byte_stride == 0 && bit_stride == 0)
|
|
byte_stride = element_type->GetByteSize(nullptr).value_or(0);
|
|
CompilerType array_element_type = element_type->GetForwardCompilerType();
|
|
TypeSystemClang::RequireCompleteType(array_element_type);
|
|
|
|
uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride;
|
|
CompilerType clang_type;
|
|
if (array_info && array_info->element_orders.size() > 0) {
|
|
uint64_t num_elements = 0;
|
|
auto end = array_info->element_orders.rend();
|
|
for (auto pos = array_info->element_orders.rbegin(); pos != end; ++pos) {
|
|
num_elements = *pos;
|
|
clang_type = m_ast.CreateArrayType(array_element_type, num_elements,
|
|
attrs.is_vector);
|
|
array_element_type = clang_type;
|
|
array_element_bit_stride = num_elements
|
|
? array_element_bit_stride * num_elements
|
|
: array_element_bit_stride;
|
|
}
|
|
} else {
|
|
clang_type =
|
|
m_ast.CreateArrayType(array_element_type, 0, attrs.is_vector);
|
|
}
|
|
ConstString empty_name;
|
|
TypeSP type_sp =
|
|
dwarf->MakeType(die.GetID(), empty_name, array_element_bit_stride / 8,
|
|
nullptr, type_die.GetID(), Type::eEncodingIsUID,
|
|
&attrs.decl, clang_type, Type::ResolveState::Full);
|
|
type_sp->SetEncodingType(element_type);
|
|
const clang::Type *type = ClangUtil::GetQualType(clang_type).getTypePtr();
|
|
m_ast.SetMetadataAsUserID(type, die.GetID());
|
|
return type_sp;
|
|
}
|
|
|
|
TypeSP DWARFASTParserClang::ParsePointerToMemberType(
|
|
const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs) {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
Type *pointee_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true);
|
|
Type *class_type =
|
|
dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true);
|
|
|
|
// Check to make sure pointers are not NULL before attempting to
|
|
// dereference them.
|
|
if ((class_type == nullptr) || (pointee_type == nullptr))
|
|
return nullptr;
|
|
|
|
CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType();
|
|
CompilerType class_clang_type = class_type->GetForwardCompilerType();
|
|
|
|
CompilerType clang_type = TypeSystemClang::CreateMemberPointerType(
|
|
class_clang_type, pointee_clang_type);
|
|
|
|
if (std::optional<uint64_t> clang_type_size =
|
|
clang_type.GetByteSize(nullptr)) {
|
|
return dwarf->MakeType(die.GetID(), attrs.name, *clang_type_size, nullptr,
|
|
LLDB_INVALID_UID, Type::eEncodingIsUID, nullptr,
|
|
clang_type, Type::ResolveState::Forward);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DWARFASTParserClang::ParseInheritance(
|
|
const DWARFDIE &die, const DWARFDIE &parent_die,
|
|
const CompilerType class_clang_type, const AccessType default_accessibility,
|
|
const lldb::ModuleSP &module_sp,
|
|
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
|
|
ClangASTImporter::LayoutInfo &layout_info) {
|
|
auto ast =
|
|
class_clang_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>();
|
|
if (ast == nullptr)
|
|
return;
|
|
|
|
// TODO: implement DW_TAG_inheritance type parsing.
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
if (attributes.Size() == 0)
|
|
return;
|
|
|
|
DWARFFormValue encoding_form;
|
|
AccessType accessibility = default_accessibility;
|
|
bool is_virtual = false;
|
|
bool is_base_of_class = true;
|
|
off_t member_byte_offset = 0;
|
|
|
|
for (uint32_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_type:
|
|
encoding_form = form_value;
|
|
break;
|
|
case DW_AT_data_member_location:
|
|
if (auto maybe_offset =
|
|
ExtractDataMemberLocation(die, form_value, module_sp))
|
|
member_byte_offset = *maybe_offset;
|
|
break;
|
|
|
|
case DW_AT_accessibility:
|
|
accessibility =
|
|
DWARFASTParser::GetAccessTypeFromDWARF(form_value.Unsigned());
|
|
break;
|
|
|
|
case DW_AT_virtuality:
|
|
is_virtual = form_value.Boolean();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Type *base_class_type = die.ResolveTypeUID(encoding_form.Reference());
|
|
if (base_class_type == nullptr) {
|
|
module_sp->ReportError("{0:x16}: DW_TAG_inheritance failed to "
|
|
"resolve the base class at {1:x16}"
|
|
" from enclosing type {2:x16}. \nPlease file "
|
|
"a bug and attach the file at the start of "
|
|
"this error message",
|
|
die.GetOffset(),
|
|
encoding_form.Reference().GetOffset(),
|
|
parent_die.GetOffset());
|
|
return;
|
|
}
|
|
|
|
CompilerType base_class_clang_type = base_class_type->GetFullCompilerType();
|
|
assert(base_class_clang_type);
|
|
if (TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type)) {
|
|
ast->SetObjCSuperClass(class_clang_type, base_class_clang_type);
|
|
return;
|
|
}
|
|
std::unique_ptr<clang::CXXBaseSpecifier> result =
|
|
ast->CreateBaseClassSpecifier(base_class_clang_type.GetOpaqueQualType(),
|
|
accessibility, is_virtual,
|
|
is_base_of_class);
|
|
if (!result)
|
|
return;
|
|
|
|
base_classes.push_back(std::move(result));
|
|
|
|
if (is_virtual) {
|
|
// Do not specify any offset for virtual inheritance. The DWARF
|
|
// produced by clang doesn't give us a constant offset, but gives
|
|
// us a DWARF expressions that requires an actual object in memory.
|
|
// the DW_AT_data_member_location for a virtual base class looks
|
|
// like:
|
|
// DW_AT_data_member_location( DW_OP_dup, DW_OP_deref,
|
|
// DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref,
|
|
// DW_OP_plus )
|
|
// Given this, there is really no valid response we can give to
|
|
// clang for virtual base class offsets, and this should eventually
|
|
// be removed from LayoutRecordType() in the external
|
|
// AST source in clang.
|
|
} else {
|
|
layout_info.base_offsets.insert(std::make_pair(
|
|
ast->GetAsCXXRecordDecl(base_class_clang_type.GetOpaqueQualType()),
|
|
clang::CharUnits::fromQuantity(member_byte_offset)));
|
|
}
|
|
}
|
|
|
|
TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType(
|
|
const SymbolContext &sc, const DWARFDIE &die, TypeSP type_sp) {
|
|
if (!type_sp)
|
|
return type_sp;
|
|
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die);
|
|
dw_tag_t sc_parent_tag = sc_parent_die.Tag();
|
|
|
|
SymbolContextScope *symbol_context_scope = nullptr;
|
|
if (sc_parent_tag == DW_TAG_compile_unit ||
|
|
sc_parent_tag == DW_TAG_partial_unit) {
|
|
symbol_context_scope = sc.comp_unit;
|
|
} else if (sc.function != nullptr && sc_parent_die) {
|
|
symbol_context_scope =
|
|
sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID());
|
|
if (symbol_context_scope == nullptr)
|
|
symbol_context_scope = sc.function;
|
|
} else {
|
|
symbol_context_scope = sc.module_sp.get();
|
|
}
|
|
|
|
if (symbol_context_scope != nullptr)
|
|
type_sp->SetSymbolContextScope(symbol_context_scope);
|
|
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
return type_sp;
|
|
}
|
|
|
|
std::string
|
|
DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) {
|
|
if (!die.IsValid())
|
|
return "";
|
|
const char *name = die.GetName();
|
|
if (!name)
|
|
return "";
|
|
std::string qualified_name;
|
|
DWARFDIE parent_decl_ctx_die = die.GetParentDeclContextDIE();
|
|
// TODO: change this to get the correct decl context parent....
|
|
while (parent_decl_ctx_die) {
|
|
// The name may not contain template parameters due to
|
|
// -gsimple-template-names; we must reconstruct the full name from child
|
|
// template parameter dies via GetDIEClassTemplateParams().
|
|
const dw_tag_t parent_tag = parent_decl_ctx_die.Tag();
|
|
switch (parent_tag) {
|
|
case DW_TAG_namespace: {
|
|
if (const char *namespace_name = parent_decl_ctx_die.GetName()) {
|
|
qualified_name.insert(0, "::");
|
|
qualified_name.insert(0, namespace_name);
|
|
} else {
|
|
qualified_name.insert(0, "(anonymous namespace)::");
|
|
}
|
|
parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
|
|
break;
|
|
}
|
|
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type: {
|
|
if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) {
|
|
qualified_name.insert(
|
|
0, GetDIEClassTemplateParams(parent_decl_ctx_die).AsCString(""));
|
|
qualified_name.insert(0, "::");
|
|
qualified_name.insert(0, class_union_struct_name);
|
|
}
|
|
parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
parent_decl_ctx_die.Clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (qualified_name.empty())
|
|
qualified_name.append("::");
|
|
|
|
qualified_name.append(name);
|
|
qualified_name.append(GetDIEClassTemplateParams(die).AsCString(""));
|
|
|
|
return qualified_name;
|
|
}
|
|
|
|
TypeSP
|
|
DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
ParsedDWARFTypeAttributes &attrs) {
|
|
TypeSP type_sp;
|
|
CompilerType clang_type;
|
|
const dw_tag_t tag = die.Tag();
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU());
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
|
|
// UniqueDWARFASTType is large, so don't create a local variables on the
|
|
// stack, put it on the heap. This function is often called recursively and
|
|
// clang isn't good at sharing the stack space for variables in different
|
|
// blocks.
|
|
auto unique_ast_entry_up = std::make_unique<UniqueDWARFASTType>();
|
|
|
|
ConstString unique_typename(attrs.name);
|
|
Declaration unique_decl(attrs.decl);
|
|
|
|
if (attrs.name) {
|
|
if (Language::LanguageIsCPlusPlus(cu_language)) {
|
|
// For C++, we rely solely upon the one definition rule that says
|
|
// only one thing can exist at a given decl context. We ignore the
|
|
// file and line that things are declared on.
|
|
std::string qualified_name = GetCPlusPlusQualifiedName(die);
|
|
if (!qualified_name.empty())
|
|
unique_typename = ConstString(qualified_name);
|
|
unique_decl.Clear();
|
|
}
|
|
|
|
if (dwarf->GetUniqueDWARFASTTypeMap().Find(
|
|
unique_typename, die, unique_decl, attrs.byte_size.value_or(-1),
|
|
*unique_ast_entry_up)) {
|
|
type_sp = unique_ast_entry_up->m_type_sp;
|
|
if (type_sp) {
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
LinkDeclContextToDIE(
|
|
GetCachedClangDeclContextForDIE(unique_ast_entry_up->m_die), die);
|
|
return type_sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(),
|
|
DW_TAG_value_to_name(tag), type_name_cstr);
|
|
|
|
int tag_decl_kind = -1;
|
|
AccessType default_accessibility = eAccessNone;
|
|
if (tag == DW_TAG_structure_type) {
|
|
tag_decl_kind = llvm::to_underlying(clang::TagTypeKind::Struct);
|
|
default_accessibility = eAccessPublic;
|
|
} else if (tag == DW_TAG_union_type) {
|
|
tag_decl_kind = llvm::to_underlying(clang::TagTypeKind::Union);
|
|
default_accessibility = eAccessPublic;
|
|
} else if (tag == DW_TAG_class_type) {
|
|
tag_decl_kind = llvm::to_underlying(clang::TagTypeKind::Class);
|
|
default_accessibility = eAccessPrivate;
|
|
}
|
|
|
|
if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name &&
|
|
!die.HasChildren() && cu_language == eLanguageTypeObjC) {
|
|
// Work around an issue with clang at the moment where forward
|
|
// declarations for objective C classes are emitted as:
|
|
// DW_TAG_structure_type [2]
|
|
// DW_AT_name( "ForwardObjcClass" )
|
|
// DW_AT_byte_size( 0x00 )
|
|
// DW_AT_decl_file( "..." )
|
|
// DW_AT_decl_line( 1 )
|
|
//
|
|
// Note that there is no DW_AT_declaration and there are no children,
|
|
// and the byte size is zero.
|
|
attrs.is_forward_declaration = true;
|
|
}
|
|
|
|
if (attrs.class_language == eLanguageTypeObjC ||
|
|
attrs.class_language == eLanguageTypeObjC_plus_plus) {
|
|
if (!attrs.is_complete_objc_class &&
|
|
die.Supports_DW_AT_APPLE_objc_complete_type()) {
|
|
// We have a valid eSymbolTypeObjCClass class symbol whose name
|
|
// matches the current objective C class that we are trying to find
|
|
// and this DIE isn't the complete definition (we checked
|
|
// is_complete_objc_class above and know it is false), so the real
|
|
// definition is in here somewhere
|
|
type_sp =
|
|
dwarf->FindCompleteObjCDefinitionTypeForDIE(die, attrs.name, true);
|
|
|
|
if (!type_sp) {
|
|
SymbolFileDWARFDebugMap *debug_map_symfile =
|
|
dwarf->GetDebugMapSymfile();
|
|
if (debug_map_symfile) {
|
|
// We weren't able to find a full declaration in this DWARF,
|
|
// see if we have a declaration anywhere else...
|
|
type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE(
|
|
die, attrs.name, true);
|
|
}
|
|
}
|
|
|
|
if (type_sp) {
|
|
if (log) {
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF({0:p}) - {1:x16}: {2} type "
|
|
"\"{3}\" is an "
|
|
"incomplete objc type, complete type is {4:x8}",
|
|
static_cast<void *>(this), die.GetOffset(),
|
|
DW_TAG_value_to_name(tag), attrs.name.GetCString(),
|
|
type_sp->GetID());
|
|
}
|
|
|
|
// We found a real definition for this type elsewhere so lets use
|
|
// it and cache the fact that we found a complete type for this
|
|
// die
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
return type_sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (attrs.is_forward_declaration) {
|
|
// We have a forward declaration to a type and we need to try and
|
|
// find a full declaration. We look in the current type index just in
|
|
// case we have a forward declaration followed by an actual
|
|
// declarations in the DWARF. If this fails, we need to look
|
|
// elsewhere...
|
|
if (log) {
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF({0:p}) - {1:x16}: {2} type \"{3}\" is a "
|
|
"forward declaration, trying to find complete type",
|
|
static_cast<void *>(this), die.GetOffset(), DW_TAG_value_to_name(tag),
|
|
attrs.name.GetCString());
|
|
}
|
|
|
|
// See if the type comes from a Clang module and if so, track down
|
|
// that type.
|
|
type_sp = ParseTypeFromClangModule(sc, die, log);
|
|
if (type_sp)
|
|
return type_sp;
|
|
|
|
// type_sp = FindDefinitionTypeForDIE (dwarf_cu, die,
|
|
// type_name_const_str);
|
|
type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die);
|
|
|
|
if (!type_sp) {
|
|
SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile();
|
|
if (debug_map_symfile) {
|
|
// We weren't able to find a full declaration in this DWARF, see
|
|
// if we have a declaration anywhere else...
|
|
type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(die);
|
|
}
|
|
}
|
|
|
|
if (type_sp) {
|
|
if (log) {
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF({0:p}) - {1:x16}: {2} type \"{3}\" is a "
|
|
"forward declaration, complete type is {4:x8}",
|
|
static_cast<void *>(this), die.GetOffset(),
|
|
DW_TAG_value_to_name(tag), attrs.name.GetCString(),
|
|
type_sp->GetID());
|
|
}
|
|
|
|
// We found a real definition for this type elsewhere so lets use
|
|
// it and cache the fact that we found a complete type for this die
|
|
dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get();
|
|
clang::DeclContext *defn_decl_ctx =
|
|
GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID()));
|
|
if (defn_decl_ctx)
|
|
LinkDeclContextToDIE(defn_decl_ctx, die);
|
|
return type_sp;
|
|
}
|
|
}
|
|
assert(tag_decl_kind != -1);
|
|
UNUSED_IF_ASSERT_DISABLED(tag_decl_kind);
|
|
bool clang_type_was_created = false;
|
|
clang_type = CompilerType(
|
|
m_ast.weak_from_this(),
|
|
dwarf->GetForwardDeclDIEToCompilerType().lookup(die.GetDIE()));
|
|
if (!clang_type) {
|
|
clang::DeclContext *decl_ctx =
|
|
GetClangDeclContextContainingDIE(die, nullptr);
|
|
|
|
PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), decl_ctx, die,
|
|
attrs.name.GetCString());
|
|
|
|
if (attrs.accessibility == eAccessNone && decl_ctx) {
|
|
// Check the decl context that contains this class/struct/union. If
|
|
// it is a class we must give it an accessibility.
|
|
const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind();
|
|
if (DeclKindIsCXXClass(containing_decl_kind))
|
|
attrs.accessibility = default_accessibility;
|
|
}
|
|
|
|
ClangASTMetadata metadata;
|
|
metadata.SetUserID(die.GetID());
|
|
metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die));
|
|
|
|
TypeSystemClang::TemplateParameterInfos template_param_infos;
|
|
if (ParseTemplateParameterInfos(die, template_param_infos)) {
|
|
clang::ClassTemplateDecl *class_template_decl =
|
|
m_ast.ParseClassTemplateDecl(
|
|
decl_ctx, GetOwningClangModule(die), attrs.accessibility,
|
|
attrs.name.GetCString(), tag_decl_kind, template_param_infos);
|
|
if (!class_template_decl) {
|
|
if (log) {
|
|
dwarf->GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF({0:p}) - {1:x16}: {2} type \"{3}\" "
|
|
"clang::ClassTemplateDecl failed to return a decl.",
|
|
static_cast<void *>(this), die.GetOffset(),
|
|
DW_TAG_value_to_name(tag), attrs.name.GetCString());
|
|
}
|
|
return TypeSP();
|
|
}
|
|
|
|
clang::ClassTemplateSpecializationDecl *class_specialization_decl =
|
|
m_ast.CreateClassTemplateSpecializationDecl(
|
|
decl_ctx, GetOwningClangModule(die), class_template_decl,
|
|
tag_decl_kind, template_param_infos);
|
|
clang_type = m_ast.CreateClassTemplateSpecializationType(
|
|
class_specialization_decl);
|
|
clang_type_was_created = true;
|
|
|
|
m_ast.SetMetadata(class_template_decl, metadata);
|
|
m_ast.SetMetadata(class_specialization_decl, metadata);
|
|
}
|
|
|
|
if (!clang_type_was_created) {
|
|
clang_type_was_created = true;
|
|
clang_type = m_ast.CreateRecordType(
|
|
decl_ctx, GetOwningClangModule(die), attrs.accessibility,
|
|
attrs.name.GetCString(), tag_decl_kind, attrs.class_language,
|
|
&metadata, attrs.exports_symbols);
|
|
}
|
|
}
|
|
|
|
// Store a forward declaration to this class type in case any
|
|
// parameters in any class methods need it for the clang types for
|
|
// function prototypes.
|
|
LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die);
|
|
type_sp = dwarf->MakeType(
|
|
die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID,
|
|
Type::eEncodingIsUID, &attrs.decl, clang_type,
|
|
Type::ResolveState::Forward,
|
|
TypePayloadClang(OptionalClangModuleID(), attrs.is_complete_objc_class));
|
|
|
|
// Add our type to the unique type map so we don't end up creating many
|
|
// copies of the same type over and over in the ASTContext for our
|
|
// module
|
|
unique_ast_entry_up->m_type_sp = type_sp;
|
|
unique_ast_entry_up->m_die = die;
|
|
unique_ast_entry_up->m_declaration = unique_decl;
|
|
unique_ast_entry_up->m_byte_size = attrs.byte_size.value_or(0);
|
|
dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename,
|
|
*unique_ast_entry_up);
|
|
|
|
if (!attrs.is_forward_declaration) {
|
|
// Always start the definition for a class type so that if the class
|
|
// has child classes or types that require the class to be created
|
|
// for use as their decl contexts the class will be ready to accept
|
|
// these child definitions.
|
|
if (!die.HasChildren()) {
|
|
// No children for this struct/union/class, lets finish it
|
|
if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) {
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(clang_type);
|
|
} else {
|
|
dwarf->GetObjectFile()->GetModule()->ReportError(
|
|
|
|
"DWARF DIE at {0:x16} named \"{1}\" was not able to start "
|
|
"its "
|
|
"definition.\nPlease file a bug and attach the file at the "
|
|
"start of this error message",
|
|
die.GetOffset(), attrs.name.GetCString());
|
|
}
|
|
|
|
// Setting authority byte size and alignment for empty structures.
|
|
//
|
|
// If the byte size or alignmenet of the record is specified then
|
|
// overwrite the ones that would be computed by Clang.
|
|
// This is only needed as LLDB's TypeSystemClang is always in C++ mode,
|
|
// but some compilers such as GCC and Clang give empty structs a size of 0
|
|
// in C mode (in contrast to the size of 1 for empty structs that would be
|
|
// computed in C++ mode).
|
|
if (attrs.byte_size || attrs.alignment) {
|
|
clang::RecordDecl *record_decl =
|
|
TypeSystemClang::GetAsRecordDecl(clang_type);
|
|
if (record_decl) {
|
|
ClangASTImporter::LayoutInfo layout;
|
|
layout.bit_size = attrs.byte_size.value_or(0) * 8;
|
|
layout.alignment = attrs.alignment.value_or(0) * 8;
|
|
GetClangASTImporter().SetRecordLayout(record_decl, layout);
|
|
}
|
|
}
|
|
} else if (clang_type_was_created) {
|
|
// Start the definition if the class is not objective C since the
|
|
// underlying decls respond to isCompleteDefinition(). Objective
|
|
// C decls don't respond to isCompleteDefinition() so we can't
|
|
// start the declaration definition right away. For C++
|
|
// class/union/structs we want to start the definition in case the
|
|
// class is needed as the declaration context for a contained class
|
|
// or type without the need to complete that type..
|
|
|
|
if (attrs.class_language != eLanguageTypeObjC &&
|
|
attrs.class_language != eLanguageTypeObjC_plus_plus)
|
|
TypeSystemClang::StartTagDeclarationDefinition(clang_type);
|
|
|
|
// Leave this as a forward declaration until we need to know the
|
|
// details of the type. lldb_private::Type will automatically call
|
|
// the SymbolFile virtual function
|
|
// "SymbolFileDWARF::CompleteType(Type *)" When the definition
|
|
// needs to be defined.
|
|
assert(!dwarf->GetForwardDeclCompilerTypeToDIE().count(
|
|
ClangUtil::RemoveFastQualifiers(clang_type)
|
|
.GetOpaqueQualType()) &&
|
|
"Type already in the forward declaration map!");
|
|
// Can't assume m_ast.GetSymbolFile() is actually a
|
|
// SymbolFileDWARF, it can be a SymbolFileDWARFDebugMap for Apple
|
|
// binaries.
|
|
dwarf->GetForwardDeclDIEToCompilerType()[die.GetDIE()] =
|
|
clang_type.GetOpaqueQualType();
|
|
dwarf->GetForwardDeclCompilerTypeToDIE().try_emplace(
|
|
ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType(),
|
|
*die.GetDIERef());
|
|
m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true);
|
|
}
|
|
}
|
|
|
|
// If we made a clang type, set the trivial abi if applicable: We only
|
|
// do this for pass by value - which implies the Trivial ABI. There
|
|
// isn't a way to assert that something that would normally be pass by
|
|
// value is pass by reference, so we ignore that attribute if set.
|
|
if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) {
|
|
clang::CXXRecordDecl *record_decl =
|
|
m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
|
|
if (record_decl && record_decl->getDefinition()) {
|
|
record_decl->setHasTrivialSpecialMemberForCall();
|
|
}
|
|
}
|
|
|
|
if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) {
|
|
clang::CXXRecordDecl *record_decl =
|
|
m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
|
|
if (record_decl)
|
|
record_decl->setArgPassingRestrictions(
|
|
clang::RecordArgPassingKind::CannotPassInRegs);
|
|
}
|
|
return type_sp;
|
|
}
|
|
|
|
// DWARF parsing functions
|
|
|
|
class DWARFASTParserClang::DelayedAddObjCClassProperty {
|
|
public:
|
|
DelayedAddObjCClassProperty(
|
|
const CompilerType &class_opaque_type, const char *property_name,
|
|
const CompilerType &property_opaque_type, // The property type is only
|
|
// required if you don't have an
|
|
// ivar decl
|
|
const char *property_setter_name, const char *property_getter_name,
|
|
uint32_t property_attributes, const ClangASTMetadata *metadata)
|
|
: m_class_opaque_type(class_opaque_type), m_property_name(property_name),
|
|
m_property_opaque_type(property_opaque_type),
|
|
m_property_setter_name(property_setter_name),
|
|
m_property_getter_name(property_getter_name),
|
|
m_property_attributes(property_attributes) {
|
|
if (metadata != nullptr) {
|
|
m_metadata_up = std::make_unique<ClangASTMetadata>();
|
|
*m_metadata_up = *metadata;
|
|
}
|
|
}
|
|
|
|
DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) {
|
|
*this = rhs;
|
|
}
|
|
|
|
DelayedAddObjCClassProperty &
|
|
operator=(const DelayedAddObjCClassProperty &rhs) {
|
|
m_class_opaque_type = rhs.m_class_opaque_type;
|
|
m_property_name = rhs.m_property_name;
|
|
m_property_opaque_type = rhs.m_property_opaque_type;
|
|
m_property_setter_name = rhs.m_property_setter_name;
|
|
m_property_getter_name = rhs.m_property_getter_name;
|
|
m_property_attributes = rhs.m_property_attributes;
|
|
|
|
if (rhs.m_metadata_up) {
|
|
m_metadata_up = std::make_unique<ClangASTMetadata>();
|
|
*m_metadata_up = *rhs.m_metadata_up;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool Finalize() {
|
|
return TypeSystemClang::AddObjCClassProperty(
|
|
m_class_opaque_type, m_property_name, m_property_opaque_type,
|
|
/*ivar_decl=*/nullptr, m_property_setter_name, m_property_getter_name,
|
|
m_property_attributes, m_metadata_up.get());
|
|
}
|
|
|
|
private:
|
|
CompilerType m_class_opaque_type;
|
|
const char *m_property_name;
|
|
CompilerType m_property_opaque_type;
|
|
const char *m_property_setter_name;
|
|
const char *m_property_getter_name;
|
|
uint32_t m_property_attributes;
|
|
std::unique_ptr<ClangASTMetadata> m_metadata_up;
|
|
};
|
|
|
|
bool DWARFASTParserClang::ParseTemplateDIE(
|
|
const DWARFDIE &die,
|
|
TypeSystemClang::TemplateParameterInfos &template_param_infos) {
|
|
const dw_tag_t tag = die.Tag();
|
|
bool is_template_template_argument = false;
|
|
|
|
switch (tag) {
|
|
case DW_TAG_GNU_template_parameter_pack: {
|
|
template_param_infos.SetParameterPack(
|
|
std::make_unique<TypeSystemClang::TemplateParameterInfos>());
|
|
for (DWARFDIE child_die : die.children()) {
|
|
if (!ParseTemplateDIE(child_die, template_param_infos.GetParameterPack()))
|
|
return false;
|
|
}
|
|
if (const char *name = die.GetName()) {
|
|
template_param_infos.SetPackName(name);
|
|
}
|
|
return true;
|
|
}
|
|
case DW_TAG_GNU_template_template_param:
|
|
is_template_template_argument = true;
|
|
[[fallthrough]];
|
|
case DW_TAG_template_type_parameter:
|
|
case DW_TAG_template_value_parameter: {
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
if (attributes.Size() == 0)
|
|
return true;
|
|
|
|
const char *name = nullptr;
|
|
const char *template_name = nullptr;
|
|
CompilerType clang_type;
|
|
uint64_t uval64 = 0;
|
|
bool uval64_valid = false;
|
|
bool is_default_template_arg = false;
|
|
DWARFFormValue form_value;
|
|
for (size_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
|
|
switch (attr) {
|
|
case DW_AT_name:
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value))
|
|
name = form_value.AsCString();
|
|
break;
|
|
|
|
case DW_AT_GNU_template_name:
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value))
|
|
template_name = form_value.AsCString();
|
|
break;
|
|
|
|
case DW_AT_type:
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
Type *lldb_type = die.ResolveTypeUID(form_value.Reference());
|
|
if (lldb_type)
|
|
clang_type = lldb_type->GetForwardCompilerType();
|
|
}
|
|
break;
|
|
|
|
case DW_AT_const_value:
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
uval64_valid = true;
|
|
uval64 = form_value.Unsigned();
|
|
}
|
|
break;
|
|
case DW_AT_default_value:
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value))
|
|
is_default_template_arg = form_value.Boolean();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
clang::ASTContext &ast = m_ast.getASTContext();
|
|
if (!clang_type)
|
|
clang_type = m_ast.GetBasicType(eBasicTypeVoid);
|
|
|
|
if (!is_template_template_argument) {
|
|
bool is_signed = false;
|
|
// Get the signed value for any integer or enumeration if available
|
|
clang_type.IsIntegerOrEnumerationType(is_signed);
|
|
|
|
if (name && !name[0])
|
|
name = nullptr;
|
|
|
|
if (tag == DW_TAG_template_value_parameter && uval64_valid) {
|
|
std::optional<uint64_t> size = clang_type.GetBitSize(nullptr);
|
|
if (!size)
|
|
return false;
|
|
llvm::APInt apint(*size, uval64, is_signed);
|
|
template_param_infos.InsertArg(
|
|
name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed),
|
|
ClangUtil::GetQualType(clang_type),
|
|
is_default_template_arg));
|
|
} else {
|
|
template_param_infos.InsertArg(
|
|
name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type),
|
|
/*isNullPtr*/ false,
|
|
is_default_template_arg));
|
|
}
|
|
} else {
|
|
auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name);
|
|
template_param_infos.InsertArg(
|
|
name, clang::TemplateArgument(clang::TemplateName(tplt_type),
|
|
is_default_template_arg));
|
|
}
|
|
}
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DWARFASTParserClang::ParseTemplateParameterInfos(
|
|
const DWARFDIE &parent_die,
|
|
TypeSystemClang::TemplateParameterInfos &template_param_infos) {
|
|
|
|
if (!parent_die)
|
|
return false;
|
|
|
|
for (DWARFDIE die : parent_die.children()) {
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
switch (tag) {
|
|
case DW_TAG_template_type_parameter:
|
|
case DW_TAG_template_value_parameter:
|
|
case DW_TAG_GNU_template_parameter_pack:
|
|
case DW_TAG_GNU_template_template_param:
|
|
ParseTemplateDIE(die, template_param_infos);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return !template_param_infos.IsEmpty() ||
|
|
template_param_infos.hasParameterPack();
|
|
}
|
|
|
|
bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
|
|
lldb_private::Type *type,
|
|
CompilerType &clang_type) {
|
|
const dw_tag_t tag = die.Tag();
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
|
|
ClangASTImporter::LayoutInfo layout_info;
|
|
|
|
if (die.HasChildren()) {
|
|
const bool type_is_objc_object_or_interface =
|
|
TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type);
|
|
if (type_is_objc_object_or_interface) {
|
|
// For objective C we don't start the definition when the class is
|
|
// created.
|
|
TypeSystemClang::StartTagDeclarationDefinition(clang_type);
|
|
}
|
|
|
|
AccessType default_accessibility = eAccessNone;
|
|
if (tag == DW_TAG_structure_type) {
|
|
default_accessibility = eAccessPublic;
|
|
} else if (tag == DW_TAG_union_type) {
|
|
default_accessibility = eAccessPublic;
|
|
} else if (tag == DW_TAG_class_type) {
|
|
default_accessibility = eAccessPrivate;
|
|
}
|
|
|
|
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases;
|
|
// Parse members and base classes first
|
|
std::vector<DWARFDIE> member_function_dies;
|
|
|
|
DelayedPropertyList delayed_properties;
|
|
ParseChildMembers(die, clang_type, bases, member_function_dies,
|
|
delayed_properties, default_accessibility, layout_info);
|
|
|
|
// Now parse any methods if there were any...
|
|
for (const DWARFDIE &die : member_function_dies)
|
|
dwarf->ResolveType(die);
|
|
|
|
if (type_is_objc_object_or_interface) {
|
|
ConstString class_name(clang_type.GetTypeName());
|
|
if (class_name) {
|
|
dwarf->GetObjCMethods(class_name, [&](DWARFDIE method_die) {
|
|
method_die.ResolveType();
|
|
return true;
|
|
});
|
|
|
|
for (DelayedAddObjCClassProperty &property : delayed_properties)
|
|
property.Finalize();
|
|
}
|
|
}
|
|
|
|
if (!bases.empty()) {
|
|
// Make sure all base classes refer to complete types and not forward
|
|
// declarations. If we don't do this, clang will crash with an
|
|
// assertion in the call to clang_type.TransferBaseClasses()
|
|
for (const auto &base_class : bases) {
|
|
clang::TypeSourceInfo *type_source_info =
|
|
base_class->getTypeSourceInfo();
|
|
if (type_source_info)
|
|
TypeSystemClang::RequireCompleteType(
|
|
m_ast.GetType(type_source_info->getType()));
|
|
}
|
|
|
|
m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(),
|
|
std::move(bases));
|
|
}
|
|
}
|
|
|
|
m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType());
|
|
TypeSystemClang::BuildIndirectFields(clang_type);
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(clang_type);
|
|
|
|
if (!layout_info.field_offsets.empty() || !layout_info.base_offsets.empty() ||
|
|
!layout_info.vbase_offsets.empty()) {
|
|
if (type)
|
|
layout_info.bit_size = type->GetByteSize(nullptr).value_or(0) * 8;
|
|
if (layout_info.bit_size == 0)
|
|
layout_info.bit_size =
|
|
die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
|
|
if (layout_info.alignment == 0)
|
|
layout_info.alignment =
|
|
die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_alignment, 0) * 8;
|
|
|
|
clang::CXXRecordDecl *record_decl =
|
|
m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType());
|
|
if (record_decl)
|
|
GetClangASTImporter().SetRecordLayout(record_decl, layout_info);
|
|
}
|
|
|
|
return (bool)clang_type;
|
|
}
|
|
|
|
bool DWARFASTParserClang::CompleteEnumType(const DWARFDIE &die,
|
|
lldb_private::Type *type,
|
|
CompilerType &clang_type) {
|
|
if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) {
|
|
if (die.HasChildren()) {
|
|
bool is_signed = false;
|
|
clang_type.IsIntegerType(is_signed);
|
|
ParseChildEnumerators(clang_type, is_signed,
|
|
type->GetByteSize(nullptr).value_or(0), die);
|
|
}
|
|
TypeSystemClang::CompleteTagDeclarationDefinition(clang_type);
|
|
}
|
|
return (bool)clang_type;
|
|
}
|
|
|
|
bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die,
|
|
lldb_private::Type *type,
|
|
CompilerType &clang_type) {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(
|
|
dwarf->GetObjectFile()->GetModule()->GetMutex());
|
|
|
|
// Disable external storage for this type so we don't get anymore
|
|
// clang::ExternalASTSource queries for this type.
|
|
m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false);
|
|
|
|
if (!die)
|
|
return false;
|
|
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
assert(clang_type);
|
|
switch (tag) {
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_class_type:
|
|
return CompleteRecordType(die, type, clang_type);
|
|
case DW_TAG_enumeration_type:
|
|
return CompleteEnumType(die, type, clang_type);
|
|
default:
|
|
assert(false && "not a forward clang type decl!");
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void DWARFASTParserClang::EnsureAllDIEsInDeclContextHaveBeenParsed(
|
|
lldb_private::CompilerDeclContext decl_context) {
|
|
auto opaque_decl_ctx =
|
|
(clang::DeclContext *)decl_context.GetOpaqueDeclContext();
|
|
for (auto it = m_decl_ctx_to_die.find(opaque_decl_ctx);
|
|
it != m_decl_ctx_to_die.end() && it->first == opaque_decl_ctx;
|
|
it = m_decl_ctx_to_die.erase(it))
|
|
for (DWARFDIE decl : it->second.children())
|
|
GetClangDeclForDIE(decl);
|
|
}
|
|
|
|
CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) {
|
|
clang::Decl *clang_decl = GetClangDeclForDIE(die);
|
|
if (clang_decl != nullptr)
|
|
return m_ast.GetCompilerDecl(clang_decl);
|
|
return {};
|
|
}
|
|
|
|
CompilerDeclContext
|
|
DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) {
|
|
clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die);
|
|
if (clang_decl_ctx)
|
|
return m_ast.CreateDeclContext(clang_decl_ctx);
|
|
return {};
|
|
}
|
|
|
|
CompilerDeclContext
|
|
DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) {
|
|
clang::DeclContext *clang_decl_ctx =
|
|
GetClangDeclContextContainingDIE(die, nullptr);
|
|
if (clang_decl_ctx)
|
|
return m_ast.CreateDeclContext(clang_decl_ctx);
|
|
return {};
|
|
}
|
|
|
|
size_t DWARFASTParserClang::ParseChildEnumerators(
|
|
lldb_private::CompilerType &clang_type, bool is_signed,
|
|
uint32_t enumerator_byte_size, const DWARFDIE &parent_die) {
|
|
if (!parent_die)
|
|
return 0;
|
|
|
|
size_t enumerators_added = 0;
|
|
|
|
for (DWARFDIE die : parent_die.children()) {
|
|
const dw_tag_t tag = die.Tag();
|
|
if (tag != DW_TAG_enumerator)
|
|
continue;
|
|
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
if (attributes.Size() == 0)
|
|
continue;
|
|
|
|
const char *name = nullptr;
|
|
bool got_value = false;
|
|
int64_t enum_value = 0;
|
|
Declaration decl;
|
|
|
|
for (size_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_const_value:
|
|
got_value = true;
|
|
if (is_signed)
|
|
enum_value = form_value.Signed();
|
|
else
|
|
enum_value = form_value.Unsigned();
|
|
break;
|
|
|
|
case DW_AT_name:
|
|
name = form_value.AsCString();
|
|
break;
|
|
|
|
case DW_AT_description:
|
|
default:
|
|
case DW_AT_decl_file:
|
|
decl.SetFile(
|
|
attributes.CompileUnitAtIndex(i)->GetFile(form_value.Unsigned()));
|
|
break;
|
|
case DW_AT_decl_line:
|
|
decl.SetLine(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_decl_column:
|
|
decl.SetColumn(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_sibling:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (name && name[0] && got_value) {
|
|
m_ast.AddEnumerationValueToEnumerationType(
|
|
clang_type, decl, name, enum_value, enumerator_byte_size * 8);
|
|
++enumerators_added;
|
|
}
|
|
}
|
|
return enumerators_added;
|
|
}
|
|
|
|
ConstString
|
|
DWARFASTParserClang::ConstructDemangledNameFromDWARF(const DWARFDIE &die) {
|
|
bool is_static = false;
|
|
bool is_variadic = false;
|
|
bool has_template_params = false;
|
|
unsigned type_quals = 0;
|
|
std::vector<CompilerType> param_types;
|
|
std::vector<clang::ParmVarDecl *> param_decls;
|
|
StreamString sstr;
|
|
|
|
DWARFDeclContext decl_ctx = SymbolFileDWARF::GetDWARFDeclContext(die);
|
|
sstr << decl_ctx.GetQualifiedName();
|
|
|
|
clang::DeclContext *containing_decl_ctx =
|
|
GetClangDeclContextContainingDIE(die, nullptr);
|
|
ParseChildParameters(containing_decl_ctx, die, true, is_static, is_variadic,
|
|
has_template_params, param_types, param_decls,
|
|
type_quals);
|
|
sstr << "(";
|
|
for (size_t i = 0; i < param_types.size(); i++) {
|
|
if (i > 0)
|
|
sstr << ", ";
|
|
sstr << param_types[i].GetTypeName();
|
|
}
|
|
if (is_variadic)
|
|
sstr << ", ...";
|
|
sstr << ")";
|
|
if (type_quals & clang::Qualifiers::Const)
|
|
sstr << " const";
|
|
|
|
return ConstString(sstr.GetString());
|
|
}
|
|
|
|
Function *
|
|
DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit,
|
|
const DWARFDIE &die,
|
|
const AddressRange &func_range) {
|
|
assert(func_range.GetBaseAddress().IsValid());
|
|
DWARFRangeList func_ranges;
|
|
const char *name = nullptr;
|
|
const char *mangled = nullptr;
|
|
std::optional<int> decl_file;
|
|
std::optional<int> decl_line;
|
|
std::optional<int> decl_column;
|
|
std::optional<int> call_file;
|
|
std::optional<int> call_line;
|
|
std::optional<int> call_column;
|
|
DWARFExpressionList frame_base;
|
|
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
if (tag != DW_TAG_subprogram)
|
|
return nullptr;
|
|
|
|
if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line,
|
|
decl_column, call_file, call_line, call_column,
|
|
&frame_base)) {
|
|
Mangled func_name;
|
|
if (mangled)
|
|
func_name.SetValue(ConstString(mangled));
|
|
else if ((die.GetParent().Tag() == DW_TAG_compile_unit ||
|
|
die.GetParent().Tag() == DW_TAG_partial_unit) &&
|
|
Language::LanguageIsCPlusPlus(
|
|
SymbolFileDWARF::GetLanguage(*die.GetCU())) &&
|
|
!Language::LanguageIsObjC(
|
|
SymbolFileDWARF::GetLanguage(*die.GetCU())) &&
|
|
name && strcmp(name, "main") != 0) {
|
|
// If the mangled name is not present in the DWARF, generate the
|
|
// demangled name using the decl context. We skip if the function is
|
|
// "main" as its name is never mangled.
|
|
func_name.SetValue(ConstructDemangledNameFromDWARF(die));
|
|
} else
|
|
func_name.SetValue(ConstString(name));
|
|
|
|
FunctionSP func_sp;
|
|
std::unique_ptr<Declaration> decl_up;
|
|
if (decl_file || decl_line || decl_column)
|
|
decl_up = std::make_unique<Declaration>(
|
|
die.GetCU()->GetFile(decl_file ? *decl_file : 0),
|
|
decl_line ? *decl_line : 0, decl_column ? *decl_column : 0);
|
|
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
// Supply the type _only_ if it has already been parsed
|
|
Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE());
|
|
|
|
assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);
|
|
|
|
const user_id_t func_user_id = die.GetID();
|
|
func_sp =
|
|
std::make_shared<Function>(&comp_unit,
|
|
func_user_id, // UserID is the DIE offset
|
|
func_user_id, func_name, func_type,
|
|
func_range); // first address range
|
|
|
|
if (func_sp.get() != nullptr) {
|
|
if (frame_base.IsValid())
|
|
func_sp->GetFrameBaseExpression() = frame_base;
|
|
comp_unit.AddFunction(func_sp);
|
|
return func_sp.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
/// Parsed form of all attributes that are relevant for parsing Objective-C
|
|
/// properties.
|
|
struct PropertyAttributes {
|
|
explicit PropertyAttributes(const DWARFDIE &die);
|
|
const char *prop_name = nullptr;
|
|
const char *prop_getter_name = nullptr;
|
|
const char *prop_setter_name = nullptr;
|
|
/// \see clang::ObjCPropertyAttribute
|
|
uint32_t prop_attributes = 0;
|
|
};
|
|
|
|
struct DiscriminantValue {
|
|
explicit DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp);
|
|
|
|
uint32_t byte_offset;
|
|
uint32_t byte_size;
|
|
DWARFFormValue type_ref;
|
|
};
|
|
|
|
struct VariantMember {
|
|
explicit VariantMember(DWARFDIE &die, ModuleSP module_sp);
|
|
bool IsDefault() const;
|
|
|
|
std::optional<uint32_t> discr_value;
|
|
DWARFFormValue type_ref;
|
|
ConstString variant_name;
|
|
uint32_t byte_offset;
|
|
ConstString GetName() const;
|
|
};
|
|
|
|
struct VariantPart {
|
|
explicit VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
|
|
ModuleSP module_sp);
|
|
|
|
std::vector<VariantMember> &members();
|
|
|
|
DiscriminantValue &discriminant();
|
|
|
|
private:
|
|
std::vector<VariantMember> _members;
|
|
DiscriminantValue _discriminant;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
ConstString VariantMember::GetName() const { return this->variant_name; }
|
|
|
|
bool VariantMember::IsDefault() const { return !discr_value; }
|
|
|
|
VariantMember::VariantMember(DWARFDIE &die, lldb::ModuleSP module_sp) {
|
|
assert(die.Tag() == llvm::dwarf::DW_TAG_variant);
|
|
this->discr_value =
|
|
die.GetAttributeValueAsOptionalUnsigned(DW_AT_discr_value);
|
|
|
|
for (auto child_die : die.children()) {
|
|
switch (child_die.Tag()) {
|
|
case llvm::dwarf::DW_TAG_member: {
|
|
DWARFAttributes attributes = child_die.GetAttributes();
|
|
for (std::size_t i = 0; i < attributes.Size(); ++i) {
|
|
DWARFFormValue form_value;
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_name:
|
|
variant_name = ConstString(form_value.AsCString());
|
|
break;
|
|
case DW_AT_type:
|
|
type_ref = form_value;
|
|
break;
|
|
|
|
case DW_AT_data_member_location:
|
|
if (auto maybe_offset =
|
|
ExtractDataMemberLocation(die, form_value, module_sp))
|
|
byte_offset = *maybe_offset;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
DiscriminantValue::DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp) {
|
|
auto referenced_die = die.GetReferencedDIE(DW_AT_discr);
|
|
DWARFAttributes attributes = referenced_die.GetAttributes();
|
|
for (std::size_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_type:
|
|
type_ref = form_value;
|
|
break;
|
|
case DW_AT_data_member_location:
|
|
if (auto maybe_offset =
|
|
ExtractDataMemberLocation(die, form_value, module_sp))
|
|
byte_offset = *maybe_offset;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VariantPart::VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
|
|
lldb::ModuleSP module_sp)
|
|
: _members(), _discriminant(die, module_sp) {
|
|
|
|
for (auto child : die.children()) {
|
|
if (child.Tag() == llvm::dwarf::DW_TAG_variant) {
|
|
_members.push_back(VariantMember(child, module_sp));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<VariantMember> &VariantPart::members() { return this->_members; }
|
|
|
|
DiscriminantValue &VariantPart::discriminant() { return this->_discriminant; }
|
|
|
|
DWARFASTParserClang::MemberAttributes::MemberAttributes(
|
|
const DWARFDIE &die, const DWARFDIE &parent_die, ModuleSP module_sp) {
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
for (size_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_name:
|
|
name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_type:
|
|
encoding_form = form_value;
|
|
break;
|
|
case DW_AT_bit_offset:
|
|
bit_offset = form_value.Signed();
|
|
break;
|
|
case DW_AT_bit_size:
|
|
bit_size = form_value.Unsigned();
|
|
break;
|
|
case DW_AT_byte_size:
|
|
byte_size = form_value.Unsigned();
|
|
break;
|
|
case DW_AT_const_value:
|
|
const_value_form = form_value;
|
|
break;
|
|
case DW_AT_data_bit_offset:
|
|
data_bit_offset = form_value.Unsigned();
|
|
break;
|
|
case DW_AT_data_member_location:
|
|
if (auto maybe_offset =
|
|
ExtractDataMemberLocation(die, form_value, module_sp))
|
|
member_byte_offset = *maybe_offset;
|
|
break;
|
|
|
|
case DW_AT_accessibility:
|
|
accessibility =
|
|
DWARFASTParser::GetAccessTypeFromDWARF(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_artificial:
|
|
is_artificial = form_value.Boolean();
|
|
break;
|
|
case DW_AT_declaration:
|
|
is_declaration = form_value.Boolean();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clang has a DWARF generation bug where sometimes it represents
|
|
// fields that are references with bad byte size and bit size/offset
|
|
// information such as:
|
|
//
|
|
// DW_AT_byte_size( 0x00 )
|
|
// DW_AT_bit_size( 0x40 )
|
|
// DW_AT_bit_offset( 0xffffffffffffffc0 )
|
|
//
|
|
// So check the bit offset to make sure it is sane, and if the values
|
|
// are not sane, remove them. If we don't do this then we will end up
|
|
// with a crash if we try to use this type in an expression when clang
|
|
// becomes unhappy with its recycled debug info.
|
|
if (byte_size.value_or(0) == 0 && bit_offset < 0) {
|
|
bit_size = 0;
|
|
bit_offset = 0;
|
|
}
|
|
}
|
|
|
|
PropertyAttributes::PropertyAttributes(const DWARFDIE &die) {
|
|
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
for (size_t i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_APPLE_property_name:
|
|
prop_name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_APPLE_property_getter:
|
|
prop_getter_name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_APPLE_property_setter:
|
|
prop_setter_name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_APPLE_property_attribute:
|
|
prop_attributes = form_value.Unsigned();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!prop_name)
|
|
return;
|
|
ConstString fixed_setter;
|
|
|
|
// Check if the property getter/setter were provided as full names.
|
|
// We want basenames, so we extract them.
|
|
if (prop_getter_name && prop_getter_name[0] == '-') {
|
|
std::optional<const ObjCLanguage::MethodName> prop_getter_method =
|
|
ObjCLanguage::MethodName::Create(prop_getter_name, true);
|
|
if (prop_getter_method)
|
|
prop_getter_name =
|
|
ConstString(prop_getter_method->GetSelector()).GetCString();
|
|
}
|
|
|
|
if (prop_setter_name && prop_setter_name[0] == '-') {
|
|
std::optional<const ObjCLanguage::MethodName> prop_setter_method =
|
|
ObjCLanguage::MethodName::Create(prop_setter_name, true);
|
|
if (prop_setter_method)
|
|
prop_setter_name =
|
|
ConstString(prop_setter_method->GetSelector()).GetCString();
|
|
}
|
|
|
|
// If the names haven't been provided, they need to be filled in.
|
|
if (!prop_getter_name)
|
|
prop_getter_name = prop_name;
|
|
if (!prop_setter_name && prop_name[0] &&
|
|
!(prop_attributes & DW_APPLE_PROPERTY_readonly)) {
|
|
StreamString ss;
|
|
|
|
ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]);
|
|
|
|
fixed_setter.SetString(ss.GetString());
|
|
prop_setter_name = fixed_setter.GetCString();
|
|
}
|
|
}
|
|
|
|
void DWARFASTParserClang::ParseObjCProperty(
|
|
const DWARFDIE &die, const DWARFDIE &parent_die,
|
|
const lldb_private::CompilerType &class_clang_type,
|
|
DelayedPropertyList &delayed_properties) {
|
|
// This function can only parse DW_TAG_APPLE_property.
|
|
assert(die.Tag() == DW_TAG_APPLE_property);
|
|
|
|
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
|
|
|
|
const MemberAttributes attrs(die, parent_die, module_sp);
|
|
const PropertyAttributes propAttrs(die);
|
|
|
|
if (!propAttrs.prop_name) {
|
|
module_sp->ReportError("{0:x8}: DW_TAG_APPLE_property has no name.",
|
|
die.GetID());
|
|
return;
|
|
}
|
|
|
|
Type *member_type = die.ResolveTypeUID(attrs.encoding_form.Reference());
|
|
if (!member_type) {
|
|
module_sp->ReportError(
|
|
"{0:x8}: DW_TAG_APPLE_property '{1}' refers to type {2:x16}"
|
|
" which was unable to be parsed",
|
|
die.GetID(), propAttrs.prop_name,
|
|
attrs.encoding_form.Reference().GetOffset());
|
|
return;
|
|
}
|
|
|
|
ClangASTMetadata metadata;
|
|
metadata.SetUserID(die.GetID());
|
|
delayed_properties.push_back(DelayedAddObjCClassProperty(
|
|
class_clang_type, propAttrs.prop_name,
|
|
member_type->GetLayoutCompilerType(), propAttrs.prop_setter_name,
|
|
propAttrs.prop_getter_name, propAttrs.prop_attributes, &metadata));
|
|
}
|
|
|
|
llvm::Expected<llvm::APInt> DWARFASTParserClang::ExtractIntFromFormValue(
|
|
const CompilerType &int_type, const DWARFFormValue &form_value) const {
|
|
clang::QualType qt = ClangUtil::GetQualType(int_type);
|
|
assert(qt->isIntegralOrEnumerationType());
|
|
auto ts_ptr = int_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>();
|
|
if (!ts_ptr)
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"TypeSystem not clang");
|
|
TypeSystemClang &ts = *ts_ptr;
|
|
clang::ASTContext &ast = ts.getASTContext();
|
|
|
|
const unsigned type_bits = ast.getIntWidth(qt);
|
|
const bool is_unsigned = qt->isUnsignedIntegerType();
|
|
|
|
// The maximum int size supported at the moment by this function. Limited
|
|
// by the uint64_t return type of DWARFFormValue::Signed/Unsigned.
|
|
constexpr std::size_t max_bit_size = 64;
|
|
|
|
// For values bigger than 64 bit (e.g. __int128_t values),
|
|
// DWARFFormValue's Signed/Unsigned functions will return wrong results so
|
|
// emit an error for now.
|
|
if (type_bits > max_bit_size) {
|
|
auto msg = llvm::formatv("Can only parse integers with up to {0} bits, but "
|
|
"given integer has {1} bits.",
|
|
max_bit_size, type_bits);
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str());
|
|
}
|
|
|
|
// Construct an APInt with the maximum bit size and the given integer.
|
|
llvm::APInt result(max_bit_size, form_value.Unsigned(), !is_unsigned);
|
|
|
|
// Calculate how many bits are required to represent the input value.
|
|
// For unsigned types, take the number of active bits in the APInt.
|
|
// For signed types, ask APInt how many bits are required to represent the
|
|
// signed integer.
|
|
const unsigned required_bits =
|
|
is_unsigned ? result.getActiveBits() : result.getSignificantBits();
|
|
|
|
// If the input value doesn't fit into the integer type, return an error.
|
|
if (required_bits > type_bits) {
|
|
std::string value_as_str = is_unsigned
|
|
? std::to_string(form_value.Unsigned())
|
|
: std::to_string(form_value.Signed());
|
|
auto msg = llvm::formatv("Can't store {0} value {1} in integer with {2} "
|
|
"bits.",
|
|
(is_unsigned ? "unsigned" : "signed"),
|
|
value_as_str, type_bits);
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str());
|
|
}
|
|
|
|
// Trim the result to the bit width our the int type.
|
|
if (result.getBitWidth() > type_bits)
|
|
result = result.trunc(type_bits);
|
|
return result;
|
|
}
|
|
|
|
void DWARFASTParserClang::CreateStaticMemberVariable(
|
|
const DWARFDIE &die, const MemberAttributes &attrs,
|
|
const lldb_private::CompilerType &class_clang_type) {
|
|
Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups);
|
|
assert(die.Tag() == DW_TAG_member || die.Tag() == DW_TAG_variable);
|
|
|
|
Type *var_type = die.ResolveTypeUID(attrs.encoding_form.Reference());
|
|
|
|
if (!var_type)
|
|
return;
|
|
|
|
auto accessibility =
|
|
attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility;
|
|
|
|
CompilerType ct = var_type->GetForwardCompilerType();
|
|
clang::VarDecl *v = TypeSystemClang::AddVariableToRecordType(
|
|
class_clang_type, attrs.name, ct, accessibility);
|
|
if (!v) {
|
|
LLDB_LOG(log, "Failed to add variable to the record type");
|
|
return;
|
|
}
|
|
|
|
bool unused;
|
|
// TODO: Support float/double static members as well.
|
|
if (!ct.IsIntegerOrEnumerationType(unused) || !attrs.const_value_form)
|
|
return;
|
|
|
|
llvm::Expected<llvm::APInt> const_value_or_err =
|
|
ExtractIntFromFormValue(ct, *attrs.const_value_form);
|
|
if (!const_value_or_err) {
|
|
LLDB_LOG_ERROR(log, const_value_or_err.takeError(),
|
|
"Failed to add const value to variable {1}: {0}",
|
|
v->getQualifiedNameAsString());
|
|
return;
|
|
}
|
|
|
|
TypeSystemClang::SetIntegerInitializerForVariable(v, *const_value_or_err);
|
|
}
|
|
|
|
void DWARFASTParserClang::ParseSingleMember(
|
|
const DWARFDIE &die, const DWARFDIE &parent_die,
|
|
const lldb_private::CompilerType &class_clang_type,
|
|
lldb::AccessType default_accessibility,
|
|
lldb_private::ClangASTImporter::LayoutInfo &layout_info,
|
|
FieldInfo &last_field_info) {
|
|
// This function can only parse DW_TAG_member.
|
|
assert(die.Tag() == DW_TAG_member);
|
|
|
|
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
|
|
const dw_tag_t tag = die.Tag();
|
|
// Get the parent byte size so we can verify any members will fit
|
|
const uint64_t parent_byte_size =
|
|
parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX);
|
|
const uint64_t parent_bit_size =
|
|
parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8;
|
|
|
|
const MemberAttributes attrs(die, parent_die, module_sp);
|
|
|
|
// Handle static members, which are typically members without
|
|
// locations. However, GCC doesn't emit DW_AT_data_member_location
|
|
// for any union members (regardless of linkage).
|
|
// Non-normative text pre-DWARFv5 recommends marking static
|
|
// data members with an DW_AT_external flag. Clang emits this consistently
|
|
// whereas GCC emits it only for static data members if not part of an
|
|
// anonymous namespace. The flag that is consistently emitted for static
|
|
// data members is DW_AT_declaration, so we check it instead.
|
|
// The following block is only necessary to support DWARFv4 and earlier.
|
|
// Starting with DWARFv5, static data members are marked DW_AT_variable so we
|
|
// can consistently detect them on both GCC and Clang without below heuristic.
|
|
if (attrs.member_byte_offset == UINT32_MAX &&
|
|
attrs.data_bit_offset == UINT64_MAX && attrs.is_declaration) {
|
|
CreateStaticMemberVariable(die, attrs, class_clang_type);
|
|
return;
|
|
}
|
|
|
|
Type *member_type = die.ResolveTypeUID(attrs.encoding_form.Reference());
|
|
if (!member_type) {
|
|
if (attrs.name)
|
|
module_sp->ReportError(
|
|
"{0:x8}: DW_TAG_member '{1}' refers to type {2:x16}"
|
|
" which was unable to be parsed",
|
|
die.GetID(), attrs.name, attrs.encoding_form.Reference().GetOffset());
|
|
else
|
|
module_sp->ReportError("{0:x8}: DW_TAG_member refers to type {1:x16}"
|
|
" which was unable to be parsed",
|
|
die.GetID(),
|
|
attrs.encoding_form.Reference().GetOffset());
|
|
return;
|
|
}
|
|
|
|
const uint64_t character_width = 8;
|
|
const uint64_t word_width = 32;
|
|
CompilerType member_clang_type = member_type->GetLayoutCompilerType();
|
|
|
|
const auto accessibility = attrs.accessibility == eAccessNone
|
|
? default_accessibility
|
|
: attrs.accessibility;
|
|
|
|
uint64_t field_bit_offset = (attrs.member_byte_offset == UINT32_MAX
|
|
? 0
|
|
: (attrs.member_byte_offset * 8ULL));
|
|
|
|
if (attrs.bit_size > 0) {
|
|
FieldInfo this_field_info;
|
|
this_field_info.bit_offset = field_bit_offset;
|
|
this_field_info.bit_size = attrs.bit_size;
|
|
|
|
if (attrs.data_bit_offset != UINT64_MAX) {
|
|
this_field_info.bit_offset = attrs.data_bit_offset;
|
|
} else {
|
|
auto byte_size = attrs.byte_size;
|
|
if (!byte_size)
|
|
byte_size = member_type->GetByteSize(nullptr);
|
|
|
|
ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
|
|
if (objfile->GetByteOrder() == eByteOrderLittle) {
|
|
this_field_info.bit_offset += byte_size.value_or(0) * 8;
|
|
this_field_info.bit_offset -= (attrs.bit_offset + attrs.bit_size);
|
|
} else {
|
|
this_field_info.bit_offset += attrs.bit_offset;
|
|
}
|
|
}
|
|
|
|
// The ObjC runtime knows the byte offset but we still need to provide
|
|
// the bit-offset in the layout. It just means something different then
|
|
// what it does in C and C++. So we skip this check for ObjC types.
|
|
//
|
|
// We also skip this for fields of a union since they will all have a
|
|
// zero offset.
|
|
if (!TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type) &&
|
|
!(parent_die.Tag() == DW_TAG_union_type &&
|
|
this_field_info.bit_offset == 0) &&
|
|
((this_field_info.bit_offset >= parent_bit_size) ||
|
|
(last_field_info.IsBitfield() &&
|
|
!last_field_info.NextBitfieldOffsetIsValid(
|
|
this_field_info.bit_offset)))) {
|
|
ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
|
|
objfile->GetModule()->ReportWarning(
|
|
"{0:x16}: {1} bitfield named \"{2}\" has invalid "
|
|
"bit offset ({3:x8}) member will be ignored. Please file a bug "
|
|
"against the "
|
|
"compiler and include the preprocessed output for {4}\n",
|
|
die.GetID(), DW_TAG_value_to_name(tag), attrs.name,
|
|
this_field_info.bit_offset, GetUnitName(parent_die).c_str());
|
|
return;
|
|
}
|
|
|
|
// Update the field bit offset we will report for layout
|
|
field_bit_offset = this_field_info.bit_offset;
|
|
|
|
// Objective-C has invalid DW_AT_bit_offset values in older
|
|
// versions of clang, so we have to be careful and only insert
|
|
// unnamed bitfields if we have a new enough clang.
|
|
bool detect_unnamed_bitfields = true;
|
|
|
|
if (TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type))
|
|
detect_unnamed_bitfields =
|
|
die.GetCU()->Supports_unnamed_objc_bitfields();
|
|
|
|
if (detect_unnamed_bitfields) {
|
|
std::optional<FieldInfo> unnamed_field_info;
|
|
uint64_t last_field_end =
|
|
last_field_info.bit_offset + last_field_info.bit_size;
|
|
|
|
if (!last_field_info.IsBitfield()) {
|
|
// The last field was not a bit-field...
|
|
// but if it did take up the entire word then we need to extend
|
|
// last_field_end so the bit-field does not step into the last
|
|
// fields padding.
|
|
if (last_field_end != 0 && ((last_field_end % word_width) != 0))
|
|
last_field_end += word_width - (last_field_end % word_width);
|
|
}
|
|
|
|
if (ShouldCreateUnnamedBitfield(last_field_info, last_field_end,
|
|
this_field_info, layout_info)) {
|
|
unnamed_field_info = FieldInfo{};
|
|
unnamed_field_info->bit_size =
|
|
this_field_info.bit_offset - last_field_end;
|
|
unnamed_field_info->bit_offset = last_field_end;
|
|
}
|
|
|
|
if (unnamed_field_info) {
|
|
clang::FieldDecl *unnamed_bitfield_decl =
|
|
TypeSystemClang::AddFieldToRecordType(
|
|
class_clang_type, llvm::StringRef(),
|
|
m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint,
|
|
word_width),
|
|
accessibility, unnamed_field_info->bit_size);
|
|
|
|
layout_info.field_offsets.insert(std::make_pair(
|
|
unnamed_bitfield_decl, unnamed_field_info->bit_offset));
|
|
}
|
|
}
|
|
|
|
last_field_info = this_field_info;
|
|
last_field_info.SetIsBitfield(true);
|
|
} else {
|
|
last_field_info.bit_offset = field_bit_offset;
|
|
|
|
if (std::optional<uint64_t> clang_type_size =
|
|
member_type->GetByteSize(nullptr)) {
|
|
last_field_info.bit_size = *clang_type_size * character_width;
|
|
}
|
|
|
|
last_field_info.SetIsBitfield(false);
|
|
}
|
|
|
|
// Don't turn artificial members such as vtable pointers into real FieldDecls
|
|
// in our AST. Clang will re-create those articial members and they would
|
|
// otherwise just overlap in the layout with the FieldDecls we add here.
|
|
// This needs to be done after updating FieldInfo which keeps track of where
|
|
// field start/end so we don't later try to fill the space of this
|
|
// artificial member with (unnamed bitfield) padding.
|
|
if (attrs.is_artificial && ShouldIgnoreArtificialField(attrs.name)) {
|
|
last_field_info.SetIsArtificial(true);
|
|
return;
|
|
}
|
|
|
|
if (!member_clang_type.IsCompleteType())
|
|
member_clang_type.GetCompleteType();
|
|
|
|
{
|
|
// Older versions of clang emit the same DWARF for array[0] and array[1]. If
|
|
// the current field is at the end of the structure, then there is
|
|
// definitely no room for extra elements and we override the type to
|
|
// array[0]. This was fixed by f454dfb6b5af.
|
|
CompilerType member_array_element_type;
|
|
uint64_t member_array_size;
|
|
bool member_array_is_incomplete;
|
|
|
|
if (member_clang_type.IsArrayType(&member_array_element_type,
|
|
&member_array_size,
|
|
&member_array_is_incomplete) &&
|
|
!member_array_is_incomplete) {
|
|
uint64_t parent_byte_size =
|
|
parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX);
|
|
|
|
if (attrs.member_byte_offset >= parent_byte_size) {
|
|
if (member_array_size != 1 &&
|
|
(member_array_size != 0 ||
|
|
attrs.member_byte_offset > parent_byte_size)) {
|
|
module_sp->ReportError(
|
|
"{0:x8}: DW_TAG_member '{1}' refers to type {2:x16}"
|
|
" which extends beyond the bounds of {3:x8}",
|
|
die.GetID(), attrs.name,
|
|
attrs.encoding_form.Reference().GetOffset(), parent_die.GetID());
|
|
}
|
|
|
|
member_clang_type =
|
|
m_ast.CreateArrayType(member_array_element_type, 0, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
TypeSystemClang::RequireCompleteType(member_clang_type);
|
|
|
|
clang::FieldDecl *field_decl = TypeSystemClang::AddFieldToRecordType(
|
|
class_clang_type, attrs.name, member_clang_type, accessibility,
|
|
attrs.bit_size);
|
|
|
|
m_ast.SetMetadataAsUserID(field_decl, die.GetID());
|
|
|
|
layout_info.field_offsets.insert(
|
|
std::make_pair(field_decl, field_bit_offset));
|
|
}
|
|
|
|
bool DWARFASTParserClang::ParseChildMembers(
|
|
const DWARFDIE &parent_die, CompilerType &class_clang_type,
|
|
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
|
|
std::vector<DWARFDIE> &member_function_dies,
|
|
DelayedPropertyList &delayed_properties,
|
|
const AccessType default_accessibility,
|
|
ClangASTImporter::LayoutInfo &layout_info) {
|
|
if (!parent_die)
|
|
return false;
|
|
|
|
FieldInfo last_field_info;
|
|
|
|
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
|
|
auto ts = class_clang_type.GetTypeSystem();
|
|
auto ast = ts.dyn_cast_or_null<TypeSystemClang>();
|
|
if (ast == nullptr)
|
|
return false;
|
|
|
|
for (DWARFDIE die : parent_die.children()) {
|
|
dw_tag_t tag = die.Tag();
|
|
|
|
switch (tag) {
|
|
case DW_TAG_APPLE_property:
|
|
ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties);
|
|
break;
|
|
|
|
case DW_TAG_variant_part:
|
|
if (die.GetCU()->GetDWARFLanguageType() == eLanguageTypeRust) {
|
|
ParseRustVariantPart(die, parent_die, class_clang_type,
|
|
default_accessibility, layout_info);
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_variable: {
|
|
const MemberAttributes attrs(die, parent_die, module_sp);
|
|
CreateStaticMemberVariable(die, attrs, class_clang_type);
|
|
} break;
|
|
case DW_TAG_member:
|
|
ParseSingleMember(die, parent_die, class_clang_type,
|
|
default_accessibility, layout_info, last_field_info);
|
|
break;
|
|
|
|
case DW_TAG_subprogram:
|
|
// Let the type parsing code handle this one for us.
|
|
member_function_dies.push_back(die);
|
|
break;
|
|
|
|
case DW_TAG_inheritance:
|
|
ParseInheritance(die, parent_die, class_clang_type, default_accessibility,
|
|
module_sp, base_classes, layout_info);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t DWARFASTParserClang::ParseChildParameters(
|
|
clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die,
|
|
bool skip_artificial, bool &is_static, bool &is_variadic,
|
|
bool &has_template_params, std::vector<CompilerType> &function_param_types,
|
|
std::vector<clang::ParmVarDecl *> &function_param_decls,
|
|
unsigned &type_quals) {
|
|
if (!parent_die)
|
|
return 0;
|
|
|
|
size_t arg_idx = 0;
|
|
for (DWARFDIE die : parent_die.children()) {
|
|
const dw_tag_t tag = die.Tag();
|
|
switch (tag) {
|
|
case DW_TAG_formal_parameter: {
|
|
DWARFAttributes attributes = die.GetAttributes();
|
|
if (attributes.Size() == 0) {
|
|
arg_idx++;
|
|
break;
|
|
}
|
|
|
|
const char *name = nullptr;
|
|
DWARFFormValue param_type_die_form;
|
|
bool is_artificial = false;
|
|
// one of None, Auto, Register, Extern, Static, PrivateExtern
|
|
|
|
clang::StorageClass storage = clang::SC_None;
|
|
uint32_t i;
|
|
for (i = 0; i < attributes.Size(); ++i) {
|
|
const dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_name:
|
|
name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_type:
|
|
param_type_die_form = form_value;
|
|
break;
|
|
case DW_AT_artificial:
|
|
is_artificial = form_value.Boolean();
|
|
break;
|
|
case DW_AT_location:
|
|
case DW_AT_const_value:
|
|
case DW_AT_default_value:
|
|
case DW_AT_description:
|
|
case DW_AT_endianity:
|
|
case DW_AT_is_optional:
|
|
case DW_AT_segment:
|
|
case DW_AT_variable_parameter:
|
|
default:
|
|
case DW_AT_abstract_origin:
|
|
case DW_AT_sibling:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool skip = false;
|
|
if (skip_artificial && is_artificial) {
|
|
// In order to determine if a C++ member function is "const" we
|
|
// have to look at the const-ness of "this"...
|
|
if (arg_idx == 0 &&
|
|
DeclKindIsCXXClass(containing_decl_ctx->getDeclKind()) &&
|
|
// Often times compilers omit the "this" name for the
|
|
// specification DIEs, so we can't rely upon the name being in
|
|
// the formal parameter DIE...
|
|
(name == nullptr || ::strcmp(name, "this") == 0)) {
|
|
Type *this_type = die.ResolveTypeUID(param_type_die_form.Reference());
|
|
if (this_type) {
|
|
uint32_t encoding_mask = this_type->GetEncodingMask();
|
|
if (encoding_mask & Type::eEncodingIsPointerUID) {
|
|
is_static = false;
|
|
|
|
if (encoding_mask & (1u << Type::eEncodingIsConstUID))
|
|
type_quals |= clang::Qualifiers::Const;
|
|
if (encoding_mask & (1u << Type::eEncodingIsVolatileUID))
|
|
type_quals |= clang::Qualifiers::Volatile;
|
|
}
|
|
}
|
|
}
|
|
skip = true;
|
|
}
|
|
|
|
if (!skip) {
|
|
Type *type = die.ResolveTypeUID(param_type_die_form.Reference());
|
|
if (type) {
|
|
function_param_types.push_back(type->GetForwardCompilerType());
|
|
|
|
clang::ParmVarDecl *param_var_decl = m_ast.CreateParameterDeclaration(
|
|
containing_decl_ctx, GetOwningClangModule(die), name,
|
|
type->GetForwardCompilerType(), storage);
|
|
assert(param_var_decl);
|
|
function_param_decls.push_back(param_var_decl);
|
|
|
|
m_ast.SetMetadataAsUserID(param_var_decl, die.GetID());
|
|
}
|
|
}
|
|
arg_idx++;
|
|
} break;
|
|
|
|
case DW_TAG_unspecified_parameters:
|
|
is_variadic = true;
|
|
break;
|
|
|
|
case DW_TAG_template_type_parameter:
|
|
case DW_TAG_template_value_parameter:
|
|
case DW_TAG_GNU_template_parameter_pack:
|
|
// The one caller of this was never using the template_param_infos, and
|
|
// the local variable was taking up a large amount of stack space in
|
|
// SymbolFileDWARF::ParseType() so this was removed. If we ever need the
|
|
// template params back, we can add them back.
|
|
// ParseTemplateDIE (dwarf_cu, die, template_param_infos);
|
|
has_template_params = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return arg_idx;
|
|
}
|
|
|
|
clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) {
|
|
if (!die)
|
|
return nullptr;
|
|
|
|
switch (die.Tag()) {
|
|
case DW_TAG_variable:
|
|
case DW_TAG_constant:
|
|
case DW_TAG_formal_parameter:
|
|
case DW_TAG_imported_declaration:
|
|
case DW_TAG_imported_module:
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE());
|
|
if (cache_pos != m_die_to_decl.end())
|
|
return cache_pos->second;
|
|
|
|
if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) {
|
|
clang::Decl *decl = GetClangDeclForDIE(spec_die);
|
|
m_die_to_decl[die.GetDIE()] = decl;
|
|
return decl;
|
|
}
|
|
|
|
if (DWARFDIE abstract_origin_die =
|
|
die.GetReferencedDIE(DW_AT_abstract_origin)) {
|
|
clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die);
|
|
m_die_to_decl[die.GetDIE()] = decl;
|
|
return decl;
|
|
}
|
|
|
|
clang::Decl *decl = nullptr;
|
|
switch (die.Tag()) {
|
|
case DW_TAG_variable:
|
|
case DW_TAG_constant:
|
|
case DW_TAG_formal_parameter: {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
Type *type = GetTypeForDIE(die);
|
|
if (dwarf && type) {
|
|
const char *name = die.GetName();
|
|
clang::DeclContext *decl_context =
|
|
TypeSystemClang::DeclContextGetAsDeclContext(
|
|
dwarf->GetDeclContextContainingUID(die.GetID()));
|
|
decl = m_ast.CreateVariableDeclaration(
|
|
decl_context, GetOwningClangModule(die), name,
|
|
ClangUtil::GetQualType(type->GetForwardCompilerType()));
|
|
}
|
|
break;
|
|
}
|
|
case DW_TAG_imported_declaration: {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
|
|
if (imported_uid) {
|
|
CompilerDecl imported_decl = SymbolFileDWARF::GetDecl(imported_uid);
|
|
if (imported_decl) {
|
|
clang::DeclContext *decl_context =
|
|
TypeSystemClang::DeclContextGetAsDeclContext(
|
|
dwarf->GetDeclContextContainingUID(die.GetID()));
|
|
if (clang::NamedDecl *clang_imported_decl =
|
|
llvm::dyn_cast<clang::NamedDecl>(
|
|
(clang::Decl *)imported_decl.GetOpaqueDecl()))
|
|
decl = m_ast.CreateUsingDeclaration(
|
|
decl_context, OptionalClangModuleID(), clang_imported_decl);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case DW_TAG_imported_module: {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import);
|
|
|
|
if (imported_uid) {
|
|
CompilerDeclContext imported_decl_ctx =
|
|
SymbolFileDWARF::GetDeclContext(imported_uid);
|
|
if (imported_decl_ctx) {
|
|
clang::DeclContext *decl_context =
|
|
TypeSystemClang::DeclContextGetAsDeclContext(
|
|
dwarf->GetDeclContextContainingUID(die.GetID()));
|
|
if (clang::NamespaceDecl *ns_decl =
|
|
TypeSystemClang::DeclContextGetAsNamespaceDecl(
|
|
imported_decl_ctx))
|
|
decl = m_ast.CreateUsingDirectiveDeclaration(
|
|
decl_context, OptionalClangModuleID(), ns_decl);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_die_to_decl[die.GetDIE()] = decl;
|
|
|
|
return decl;
|
|
}
|
|
|
|
clang::DeclContext *
|
|
DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) {
|
|
if (die) {
|
|
clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die);
|
|
if (decl_ctx)
|
|
return decl_ctx;
|
|
|
|
bool try_parsing_type = true;
|
|
switch (die.Tag()) {
|
|
case DW_TAG_compile_unit:
|
|
case DW_TAG_partial_unit:
|
|
decl_ctx = m_ast.GetTranslationUnitDecl();
|
|
try_parsing_type = false;
|
|
break;
|
|
|
|
case DW_TAG_namespace:
|
|
decl_ctx = ResolveNamespaceDIE(die);
|
|
try_parsing_type = false;
|
|
break;
|
|
|
|
case DW_TAG_imported_declaration:
|
|
decl_ctx = ResolveImportedDeclarationDIE(die);
|
|
try_parsing_type = false;
|
|
break;
|
|
|
|
case DW_TAG_lexical_block:
|
|
decl_ctx = GetDeclContextForBlock(die);
|
|
try_parsing_type = false;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (decl_ctx == nullptr && try_parsing_type) {
|
|
Type *type = die.GetDWARF()->ResolveType(die);
|
|
if (type)
|
|
decl_ctx = GetCachedClangDeclContextForDIE(die);
|
|
}
|
|
|
|
if (decl_ctx) {
|
|
LinkDeclContextToDIE(decl_ctx, die);
|
|
return decl_ctx;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
OptionalClangModuleID
|
|
DWARFASTParserClang::GetOwningClangModule(const DWARFDIE &die) {
|
|
if (!die.IsValid())
|
|
return {};
|
|
|
|
for (DWARFDIE parent = die.GetParent(); parent.IsValid();
|
|
parent = parent.GetParent()) {
|
|
const dw_tag_t tag = parent.Tag();
|
|
if (tag == DW_TAG_module) {
|
|
DWARFDIE module_die = parent;
|
|
auto it = m_die_to_module.find(module_die.GetDIE());
|
|
if (it != m_die_to_module.end())
|
|
return it->second;
|
|
const char *name =
|
|
module_die.GetAttributeValueAsString(DW_AT_name, nullptr);
|
|
if (!name)
|
|
return {};
|
|
|
|
OptionalClangModuleID id =
|
|
m_ast.GetOrCreateClangModule(name, GetOwningClangModule(module_die));
|
|
m_die_to_module.insert({module_die.GetDIE(), id});
|
|
return id;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static bool IsSubroutine(const DWARFDIE &die) {
|
|
switch (die.Tag()) {
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) {
|
|
for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) {
|
|
if (IsSubroutine(candidate)) {
|
|
if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
|
|
return candidate;
|
|
} else {
|
|
return DWARFDIE();
|
|
}
|
|
}
|
|
}
|
|
assert(0 && "Shouldn't call GetContainingFunctionWithAbstractOrigin on "
|
|
"something not in a function");
|
|
return DWARFDIE();
|
|
}
|
|
|
|
static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) {
|
|
for (DWARFDIE candidate : context.children()) {
|
|
if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return DWARFDIE();
|
|
}
|
|
|
|
static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block,
|
|
const DWARFDIE &function) {
|
|
assert(IsSubroutine(function));
|
|
for (DWARFDIE context = block; context != function.GetParent();
|
|
context = context.GetParent()) {
|
|
assert(!IsSubroutine(context) || context == function);
|
|
if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) {
|
|
return child;
|
|
}
|
|
}
|
|
return DWARFDIE();
|
|
}
|
|
|
|
clang::DeclContext *
|
|
DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) {
|
|
assert(die.Tag() == DW_TAG_lexical_block);
|
|
DWARFDIE containing_function_with_abstract_origin =
|
|
GetContainingFunctionWithAbstractOrigin(die);
|
|
if (!containing_function_with_abstract_origin) {
|
|
return (clang::DeclContext *)ResolveBlockDIE(die);
|
|
}
|
|
DWARFDIE child = FindFirstChildWithAbstractOrigin(
|
|
die, containing_function_with_abstract_origin);
|
|
CompilerDeclContext decl_context =
|
|
GetDeclContextContainingUIDFromDWARF(child);
|
|
return (clang::DeclContext *)decl_context.GetOpaqueDeclContext();
|
|
}
|
|
|
|
clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) {
|
|
if (die && die.Tag() == DW_TAG_lexical_block) {
|
|
clang::BlockDecl *decl =
|
|
llvm::cast_or_null<clang::BlockDecl>(m_die_to_decl_ctx[die.GetDIE()]);
|
|
|
|
if (!decl) {
|
|
DWARFDIE decl_context_die;
|
|
clang::DeclContext *decl_context =
|
|
GetClangDeclContextContainingDIE(die, &decl_context_die);
|
|
decl =
|
|
m_ast.CreateBlockDeclaration(decl_context, GetOwningClangModule(die));
|
|
|
|
if (decl)
|
|
LinkDeclContextToDIE((clang::DeclContext *)decl, die);
|
|
}
|
|
|
|
return decl;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
clang::NamespaceDecl *
|
|
DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) {
|
|
if (die && die.Tag() == DW_TAG_namespace) {
|
|
// See if we already parsed this namespace DIE and associated it with a
|
|
// uniqued namespace declaration
|
|
clang::NamespaceDecl *namespace_decl =
|
|
static_cast<clang::NamespaceDecl *>(m_die_to_decl_ctx[die.GetDIE()]);
|
|
if (namespace_decl)
|
|
return namespace_decl;
|
|
else {
|
|
const char *namespace_name = die.GetName();
|
|
clang::DeclContext *containing_decl_ctx =
|
|
GetClangDeclContextContainingDIE(die, nullptr);
|
|
bool is_inline =
|
|
die.GetAttributeValueAsUnsigned(DW_AT_export_symbols, 0) != 0;
|
|
|
|
namespace_decl = m_ast.GetUniqueNamespaceDeclaration(
|
|
namespace_name, containing_decl_ctx, GetOwningClangModule(die),
|
|
is_inline);
|
|
|
|
if (namespace_decl)
|
|
LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die);
|
|
return namespace_decl;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
clang::NamespaceDecl *
|
|
DWARFASTParserClang::ResolveImportedDeclarationDIE(const DWARFDIE &die) {
|
|
assert(die && die.Tag() == DW_TAG_imported_declaration);
|
|
|
|
// See if we cached a NamespaceDecl for this imported declaration
|
|
// already
|
|
auto it = m_die_to_decl_ctx.find(die.GetDIE());
|
|
if (it != m_die_to_decl_ctx.end())
|
|
return static_cast<clang::NamespaceDecl *>(it->getSecond());
|
|
|
|
clang::NamespaceDecl *namespace_decl = nullptr;
|
|
|
|
const DWARFDIE imported_uid =
|
|
die.GetAttributeValueAsReferenceDIE(DW_AT_import);
|
|
if (!imported_uid)
|
|
return nullptr;
|
|
|
|
switch (imported_uid.Tag()) {
|
|
case DW_TAG_imported_declaration:
|
|
namespace_decl = ResolveImportedDeclarationDIE(imported_uid);
|
|
break;
|
|
case DW_TAG_namespace:
|
|
namespace_decl = ResolveNamespaceDIE(imported_uid);
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
if (!namespace_decl)
|
|
return nullptr;
|
|
|
|
LinkDeclContextToDIE(namespace_decl, die);
|
|
|
|
return namespace_decl;
|
|
}
|
|
|
|
clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE(
|
|
const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) {
|
|
SymbolFileDWARF *dwarf = die.GetDWARF();
|
|
|
|
DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die);
|
|
|
|
if (decl_ctx_die_copy)
|
|
*decl_ctx_die_copy = decl_ctx_die;
|
|
|
|
if (decl_ctx_die) {
|
|
clang::DeclContext *clang_decl_ctx =
|
|
GetClangDeclContextForDIE(decl_ctx_die);
|
|
if (clang_decl_ctx)
|
|
return clang_decl_ctx;
|
|
}
|
|
return m_ast.GetTranslationUnitDecl();
|
|
}
|
|
|
|
clang::DeclContext *
|
|
DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) {
|
|
if (die) {
|
|
DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE());
|
|
if (pos != m_die_to_decl_ctx.end())
|
|
return pos->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx,
|
|
const DWARFDIE &die) {
|
|
m_die_to_decl_ctx[die.GetDIE()] = decl_ctx;
|
|
// There can be many DIEs for a single decl context
|
|
// m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE());
|
|
m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die));
|
|
}
|
|
|
|
bool DWARFASTParserClang::CopyUniqueClassMethodTypes(
|
|
const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die,
|
|
lldb_private::Type *class_type, std::vector<DWARFDIE> &failures) {
|
|
if (!class_type || !src_class_die || !dst_class_die)
|
|
return false;
|
|
if (src_class_die.Tag() != dst_class_die.Tag())
|
|
return false;
|
|
|
|
// We need to complete the class type so we can get all of the method types
|
|
// parsed so we can then unique those types to their equivalent counterparts
|
|
// in "dst_cu" and "dst_class_die"
|
|
class_type->GetFullCompilerType();
|
|
|
|
auto gather = [](DWARFDIE die, UniqueCStringMap<DWARFDIE> &map,
|
|
UniqueCStringMap<DWARFDIE> &map_artificial) {
|
|
if (die.Tag() != DW_TAG_subprogram)
|
|
return;
|
|
// Make sure this is a declaration and not a concrete instance by looking
|
|
// for DW_AT_declaration set to 1. Sometimes concrete function instances are
|
|
// placed inside the class definitions and shouldn't be included in the list
|
|
// of things that are tracking here.
|
|
if (die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) != 1)
|
|
return;
|
|
|
|
if (const char *name = die.GetMangledName()) {
|
|
ConstString const_name(name);
|
|
if (die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
|
|
map_artificial.Append(const_name, die);
|
|
else
|
|
map.Append(const_name, die);
|
|
}
|
|
};
|
|
|
|
UniqueCStringMap<DWARFDIE> src_name_to_die;
|
|
UniqueCStringMap<DWARFDIE> dst_name_to_die;
|
|
UniqueCStringMap<DWARFDIE> src_name_to_die_artificial;
|
|
UniqueCStringMap<DWARFDIE> dst_name_to_die_artificial;
|
|
for (DWARFDIE src_die = src_class_die.GetFirstChild(); src_die.IsValid();
|
|
src_die = src_die.GetSibling()) {
|
|
gather(src_die, src_name_to_die, src_name_to_die_artificial);
|
|
}
|
|
for (DWARFDIE dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid();
|
|
dst_die = dst_die.GetSibling()) {
|
|
gather(dst_die, dst_name_to_die, dst_name_to_die_artificial);
|
|
}
|
|
const uint32_t src_size = src_name_to_die.GetSize();
|
|
const uint32_t dst_size = dst_name_to_die.GetSize();
|
|
|
|
// Is everything kosher so we can go through the members at top speed?
|
|
bool fast_path = true;
|
|
|
|
if (src_size != dst_size)
|
|
fast_path = false;
|
|
|
|
uint32_t idx;
|
|
|
|
if (fast_path) {
|
|
for (idx = 0; idx < src_size; ++idx) {
|
|
DWARFDIE src_die = src_name_to_die.GetValueAtIndexUnchecked(idx);
|
|
DWARFDIE dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
|
|
|
|
if (src_die.Tag() != dst_die.Tag())
|
|
fast_path = false;
|
|
|
|
const char *src_name = src_die.GetMangledName();
|
|
const char *dst_name = dst_die.GetMangledName();
|
|
|
|
// Make sure the names match
|
|
if (src_name == dst_name || (strcmp(src_name, dst_name) == 0))
|
|
continue;
|
|
|
|
fast_path = false;
|
|
}
|
|
}
|
|
|
|
DWARFASTParserClang *src_dwarf_ast_parser =
|
|
static_cast<DWARFASTParserClang *>(
|
|
SymbolFileDWARF::GetDWARFParser(*src_class_die.GetCU()));
|
|
DWARFASTParserClang *dst_dwarf_ast_parser =
|
|
static_cast<DWARFASTParserClang *>(
|
|
SymbolFileDWARF::GetDWARFParser(*dst_class_die.GetCU()));
|
|
auto link = [&](DWARFDIE src, DWARFDIE dst) {
|
|
SymbolFileDWARF::DIEToTypePtr &die_to_type =
|
|
dst_class_die.GetDWARF()->GetDIEToType();
|
|
clang::DeclContext *dst_decl_ctx =
|
|
dst_dwarf_ast_parser->m_die_to_decl_ctx[dst.GetDIE()];
|
|
if (dst_decl_ctx)
|
|
src_dwarf_ast_parser->LinkDeclContextToDIE(dst_decl_ctx, src);
|
|
|
|
if (Type *src_child_type = die_to_type[src.GetDIE()])
|
|
die_to_type[dst.GetDIE()] = src_child_type;
|
|
};
|
|
|
|
// Now do the work of linking the DeclContexts and Types.
|
|
if (fast_path) {
|
|
// We can do this quickly. Just run across the tables index-for-index
|
|
// since we know each node has matching names and tags.
|
|
for (idx = 0; idx < src_size; ++idx) {
|
|
link(src_name_to_die.GetValueAtIndexUnchecked(idx),
|
|
dst_name_to_die.GetValueAtIndexUnchecked(idx));
|
|
}
|
|
} else {
|
|
// We must do this slowly. For each member of the destination, look up a
|
|
// member in the source with the same name, check its tag, and unique them
|
|
// if everything matches up. Report failures.
|
|
|
|
if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) {
|
|
src_name_to_die.Sort();
|
|
|
|
for (idx = 0; idx < dst_size; ++idx) {
|
|
ConstString dst_name = dst_name_to_die.GetCStringAtIndex(idx);
|
|
DWARFDIE dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx);
|
|
DWARFDIE src_die = src_name_to_die.Find(dst_name, DWARFDIE());
|
|
|
|
if (src_die && (src_die.Tag() == dst_die.Tag()))
|
|
link(src_die, dst_die);
|
|
else
|
|
failures.push_back(dst_die);
|
|
}
|
|
}
|
|
}
|
|
|
|
const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize();
|
|
const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize();
|
|
|
|
if (src_size_artificial && dst_size_artificial) {
|
|
dst_name_to_die_artificial.Sort();
|
|
|
|
for (idx = 0; idx < src_size_artificial; ++idx) {
|
|
ConstString src_name_artificial =
|
|
src_name_to_die_artificial.GetCStringAtIndex(idx);
|
|
DWARFDIE src_die =
|
|
src_name_to_die_artificial.GetValueAtIndexUnchecked(idx);
|
|
DWARFDIE dst_die =
|
|
dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE());
|
|
|
|
// Both classes have the artificial types, link them
|
|
if (dst_die)
|
|
link(src_die, dst_die);
|
|
}
|
|
}
|
|
|
|
if (dst_size_artificial) {
|
|
for (idx = 0; idx < dst_size_artificial; ++idx) {
|
|
failures.push_back(
|
|
dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx));
|
|
}
|
|
}
|
|
|
|
return !failures.empty();
|
|
}
|
|
|
|
bool DWARFASTParserClang::ShouldCreateUnnamedBitfield(
|
|
FieldInfo const &last_field_info, uint64_t last_field_end,
|
|
FieldInfo const &this_field_info,
|
|
lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const {
|
|
// If we have a gap between the last_field_end and the current
|
|
// field we have an unnamed bit-field.
|
|
if (this_field_info.bit_offset <= last_field_end)
|
|
return false;
|
|
|
|
// If we have a base class, we assume there is no unnamed
|
|
// bit-field if either of the following is true:
|
|
// (a) this is the first field since the gap can be
|
|
// attributed to the members from the base class.
|
|
// FIXME: This assumption is not correct if the first field of
|
|
// the derived class is indeed an unnamed bit-field. We currently
|
|
// do not have the machinary to track the offset of the last field
|
|
// of classes we have seen before, so we are not handling this case.
|
|
// (b) Or, the first member of the derived class was a vtable pointer.
|
|
// In this case we don't want to create an unnamed bitfield either
|
|
// since those will be inserted by clang later.
|
|
const bool have_base = layout_info.base_offsets.size() != 0;
|
|
const bool this_is_first_field =
|
|
last_field_info.bit_offset == 0 && last_field_info.bit_size == 0;
|
|
const bool first_field_is_vptr =
|
|
last_field_info.bit_offset == 0 && last_field_info.IsArtificial();
|
|
|
|
if (have_base && (this_is_first_field || first_field_is_vptr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void DWARFASTParserClang::ParseRustVariantPart(
|
|
DWARFDIE &die, const DWARFDIE &parent_die, CompilerType &class_clang_type,
|
|
const lldb::AccessType default_accesibility,
|
|
ClangASTImporter::LayoutInfo &layout_info) {
|
|
assert(die.Tag() == llvm::dwarf::DW_TAG_variant_part);
|
|
assert(SymbolFileDWARF::GetLanguage(*die.GetCU()) ==
|
|
LanguageType::eLanguageTypeRust);
|
|
|
|
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
|
|
|
|
VariantPart variants(die, parent_die, module_sp);
|
|
|
|
auto discriminant_type =
|
|
die.ResolveTypeUID(variants.discriminant().type_ref.Reference());
|
|
|
|
auto decl_context = m_ast.GetDeclContextForType(class_clang_type);
|
|
|
|
auto inner_holder = m_ast.CreateRecordType(
|
|
decl_context, OptionalClangModuleID(), lldb::eAccessPublic,
|
|
std::string(
|
|
llvm::formatv("{0}$Inner", class_clang_type.GetTypeName(false))),
|
|
llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeRust);
|
|
m_ast.StartTagDeclarationDefinition(inner_holder);
|
|
m_ast.SetIsPacked(inner_holder);
|
|
|
|
for (auto member : variants.members()) {
|
|
|
|
auto has_discriminant = !member.IsDefault();
|
|
|
|
auto member_type = die.ResolveTypeUID(member.type_ref.Reference());
|
|
|
|
auto field_type = m_ast.CreateRecordType(
|
|
m_ast.GetDeclContextForType(inner_holder), OptionalClangModuleID(),
|
|
lldb::eAccessPublic,
|
|
std::string(llvm::formatv("{0}$Variant", member.GetName())),
|
|
llvm::to_underlying(clang::TagTypeKind::Struct),
|
|
lldb::eLanguageTypeRust);
|
|
|
|
m_ast.StartTagDeclarationDefinition(field_type);
|
|
auto offset = member.byte_offset;
|
|
|
|
if (has_discriminant) {
|
|
m_ast.AddFieldToRecordType(
|
|
field_type, "$discr$", discriminant_type->GetFullCompilerType(),
|
|
lldb::eAccessPublic, variants.discriminant().byte_offset);
|
|
offset += discriminant_type->GetByteSize(nullptr).value_or(0);
|
|
}
|
|
|
|
m_ast.AddFieldToRecordType(field_type, "value",
|
|
member_type->GetFullCompilerType(),
|
|
lldb::eAccessPublic, offset * 8);
|
|
|
|
m_ast.CompleteTagDeclarationDefinition(field_type);
|
|
|
|
auto name = has_discriminant
|
|
? llvm::formatv("$variant${0}", member.discr_value.value())
|
|
: std::string("$variant$");
|
|
|
|
auto variant_decl =
|
|
m_ast.AddFieldToRecordType(inner_holder, llvm::StringRef(name),
|
|
field_type, default_accesibility, 0);
|
|
|
|
layout_info.field_offsets.insert({variant_decl, 0});
|
|
}
|
|
|
|
auto inner_field = m_ast.AddFieldToRecordType(class_clang_type,
|
|
llvm::StringRef("$variants$"),
|
|
inner_holder, eAccessPublic, 0);
|
|
|
|
m_ast.CompleteTagDeclarationDefinition(inner_holder);
|
|
|
|
layout_info.field_offsets.insert({inner_field, 0});
|
|
}
|