| #include "PdbAstBuilderClang.h" | |
| #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" | |
| #include "llvm/DebugInfo/CodeView/Formatters.h" | |
| #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" | |
| #include "llvm/DebugInfo/CodeView/RecordName.h" | |
| #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" | |
| #include "llvm/DebugInfo/CodeView/SymbolRecord.h" | |
| #include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" | |
| #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" | |
| #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" | |
| #include "llvm/DebugInfo/PDB/Native/DbiStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/TpiStream.h" | |
| #include "llvm/Demangle/MicrosoftDemangle.h" | |
| #include "PdbUtil.h" | |
| #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" | |
| #include "Plugins/ExpressionParser/Clang/ClangUtil.h" | |
| #include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" | |
| #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | |
| #include "SymbolFileNativePDB.h" | |
| #include "UdtRecordCompleter.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include <optional> | |
| #include <string_view> | |
| using namespace lldb_private; | |
| using namespace lldb_private::npdb; | |
| using namespace llvm::codeview; | |
| using namespace llvm::pdb; | |
| namespace { | |
| struct CreateMethodDecl : public TypeVisitorCallbacks { | |
| CreateMethodDecl(PdbIndex &m_index, TypeSystemClang &m_clang, | |
| TypeIndex func_type_index, | |
| clang::FunctionDecl *&function_decl, | |
| lldb::opaque_compiler_type_t parent_ty, | |
| llvm::StringRef proc_name, ConstString mangled_name, | |
| CompilerType func_ct) | |
| : m_index(m_index), m_clang(m_clang), func_type_index(func_type_index), | |
| function_decl(function_decl), parent_ty(parent_ty), | |
| proc_name(proc_name), mangled_name(mangled_name), func_ct(func_ct) {} | |
| PdbIndex &m_index; | |
| TypeSystemClang &m_clang; | |
| TypeIndex func_type_index; | |
| clang::FunctionDecl *&function_decl; | |
| lldb::opaque_compiler_type_t parent_ty; | |
| llvm::StringRef proc_name; | |
| ConstString mangled_name; | |
| CompilerType func_ct; | |
| llvm::Error visitKnownMember(CVMemberRecord &cvr, | |
| OverloadedMethodRecord &overloaded) override { | |
| TypeIndex method_list_idx = overloaded.MethodList; | |
| CVType method_list_type = m_index.tpi().getType(method_list_idx); | |
| assert(method_list_type.kind() == LF_METHODLIST); | |
| MethodOverloadListRecord method_list; | |
| llvm::Error err = TypeDeserializer::deserializeAs<MethodOverloadListRecord>( | |
| method_list_type, method_list); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as MethodOverloadList: {0}", | |
| method_list_idx); | |
| // Continue even if we couldn't deserialize. | |
| return llvm::Error::success(); | |
| } | |
| for (const OneMethodRecord &method : method_list.Methods) { | |
| if (method.getType().getIndex() == func_type_index.getIndex()) | |
| AddMethod(overloaded.Name, method.getOptions(), method.Attrs); | |
| } | |
| return llvm::Error::success(); | |
| } | |
| llvm::Error visitKnownMember(CVMemberRecord &cvr, | |
| OneMethodRecord &record) override { | |
| AddMethod(record.getName(), record.getOptions(), record.Attrs); | |
| return llvm::Error::success(); | |
| } | |
| void AddMethod(llvm::StringRef name, MethodOptions options, | |
| MemberAttributes attrs) { | |
| if (name != proc_name || function_decl) | |
| return; | |
| bool is_virtual = attrs.isVirtual(); | |
| bool is_static = attrs.isStatic(); | |
| bool is_artificial = (options & MethodOptions::CompilerGenerated) == | |
| MethodOptions::CompilerGenerated; | |
| function_decl = m_clang.AddMethodToCXXRecordType( | |
| parent_ty, proc_name, mangled_name, func_ct, | |
| /*is_virtual=*/is_virtual, /*is_static=*/is_static, | |
| /*is_inline=*/false, /*is_explicit=*/false, | |
| /*is_attr_used=*/false, /*is_artificial=*/is_artificial); | |
| } | |
| }; | |
| } // namespace | |
| static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { | |
| switch (cr.Kind) { | |
| case TypeRecordKind::Class: | |
| return clang::TagTypeKind::Class; | |
| case TypeRecordKind::Struct: | |
| return clang::TagTypeKind::Struct; | |
| case TypeRecordKind::Union: | |
| return clang::TagTypeKind::Union; | |
| case TypeRecordKind::Interface: | |
| return clang::TagTypeKind::Interface; | |
| case TypeRecordKind::Enum: | |
| return clang::TagTypeKind::Enum; | |
| default: | |
| assert(false && "Invalid tag record kind!"); | |
| return clang::TagTypeKind::Struct; | |
| } | |
| } | |
| static bool IsCVarArgsFunction(llvm::ArrayRef<TypeIndex> args) { | |
| if (args.empty()) | |
| return false; | |
| return args.back() == TypeIndex::None(); | |
| } | |
| static bool | |
| AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) { | |
| for (llvm::ms_demangle::Node *n : scopes) { | |
| auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n); | |
| if (idn->TemplateParams) | |
| return true; | |
| } | |
| return false; | |
| } | |
| static std::optional<clang::CallingConv> | |
| TranslateCallingConvention(llvm::codeview::CallingConvention conv) { | |
| using CC = llvm::codeview::CallingConvention; | |
| switch (conv) { | |
| case CC::NearC: | |
| case CC::FarC: | |
| return clang::CallingConv::CC_C; | |
| case CC::NearPascal: | |
| case CC::FarPascal: | |
| return clang::CallingConv::CC_X86Pascal; | |
| case CC::NearFast: | |
| case CC::FarFast: | |
| return clang::CallingConv::CC_X86FastCall; | |
| case CC::NearStdCall: | |
| case CC::FarStdCall: | |
| return clang::CallingConv::CC_X86StdCall; | |
| case CC::ThisCall: | |
| return clang::CallingConv::CC_X86ThisCall; | |
| case CC::NearVector: | |
| return clang::CallingConv::CC_X86VectorCall; | |
| default: | |
| return std::nullopt; | |
| } | |
| } | |
| static bool IsAnonymousNamespaceName(llvm::StringRef name) { | |
| return name == "`anonymous namespace'" || name == "`anonymous-namespace'"; | |
| } | |
| PdbAstBuilderClang::PdbAstBuilderClang(TypeSystemClang &clang) | |
| : m_clang(clang) {} | |
| lldb_private::CompilerDeclContext PdbAstBuilderClang::GetTranslationUnitDecl() { | |
| return ToCompilerDeclContext(m_clang.GetTranslationUnitDecl()); | |
| } | |
| std::pair<clang::DeclContext *, std::string> | |
| PdbAstBuilderClang::CreateDeclInfoForType(const TagRecord &record, | |
| TypeIndex ti) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| // FIXME: Move this to GetDeclContextContainingUID. | |
| if (!record.hasUniqueName()) | |
| return CreateDeclInfoForUndecoratedName(record.Name); | |
| llvm::ms_demangle::Demangler demangler; | |
| std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); | |
| llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); | |
| if (demangler.Error) | |
| return CreateDeclInfoForUndecoratedName(record.Name); | |
| llvm::ms_demangle::IdentifierNode *idn = | |
| ttn->QualifiedName->getUnqualifiedIdentifier(); | |
| std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier); | |
| llvm::ms_demangle::NodeArrayNode *name_components = | |
| ttn->QualifiedName->Components; | |
| llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes, | |
| name_components->Count - 1); | |
| clang::DeclContext *context = m_clang.GetTranslationUnitDecl(); | |
| // If this type doesn't have a parent type in the debug info, then the best we | |
| // can do is to say that it's either a series of namespaces (if the scope is | |
| // non-empty), or the translation unit (if the scope is empty). | |
| std::optional<TypeIndex> parent_index = pdb->GetParentType(ti); | |
| if (!parent_index) { | |
| if (scopes.empty()) | |
| return {context, uname}; | |
| // If there is no parent in the debug info, but some of the scopes have | |
| // template params, then this is a case of bad debug info. See, for | |
| // example, llvm.org/pr39607. We don't want to create an ambiguity between | |
| // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at | |
| // global scope with the fully qualified name. | |
| if (AnyScopesHaveTemplateParams(scopes)) | |
| return {context, std::string(record.Name)}; | |
| for (llvm::ms_demangle::Node *scope : scopes) { | |
| auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope); | |
| std::string str = nii->toString(); | |
| context = GetOrCreateNamespaceDecl(str.c_str(), *context); | |
| } | |
| return {context, uname}; | |
| } | |
| // Otherwise, all we need to do is get the parent type of this type and | |
| // recurse into our lazy type creation / AST reconstruction logic to get an | |
| // LLDB TypeSP for the parent. This will cause the AST to automatically get | |
| // the right DeclContext created for any parent. | |
| clang::QualType parent_qt = GetOrCreateClangType(*parent_index); | |
| if (parent_qt.isNull()) | |
| return {nullptr, ""}; | |
| context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl()); | |
| return {context, uname}; | |
| } | |
| static bool isLocalVariableType(SymbolKind K) { | |
| switch (K) { | |
| case S_REGISTER: | |
| case S_REGREL32: | |
| case S_REGREL32_INDIR: | |
| case S_LOCAL: | |
| return true; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| clang::Decl *PdbAstBuilderClang::GetOrCreateSymbolForId(PdbCompilandSymId id) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol cvs = index.ReadSymbolRecord(id); | |
| if (isLocalVariableType(cvs.kind())) { | |
| clang::DeclContext *scope = GetParentClangDeclContext(id); | |
| if (!scope) | |
| return nullptr; | |
| clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); | |
| PdbCompilandSymId scope_id = | |
| PdbSymUid(m_decl_to_status[scope_decl].uid).asCompilandSym(); | |
| return GetOrCreateVariableDecl(scope_id, id); | |
| } | |
| switch (cvs.kind()) { | |
| case S_GPROC32: | |
| case S_LPROC32: | |
| return GetOrCreateFunctionDecl(id); | |
| case S_GDATA32: | |
| case S_LDATA32: | |
| case S_GTHREAD32: | |
| case S_CONSTANT: | |
| // global variable | |
| return nullptr; | |
| case S_BLOCK32: | |
| return GetOrCreateBlockDecl(id); | |
| case S_INLINESITE: | |
| return GetOrCreateInlinedFunctionDecl(id); | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| CompilerDecl PdbAstBuilderClang::GetOrCreateDeclForUid(PdbSymUid uid) { | |
| if (clang::Decl *result = TryGetDecl(uid)) | |
| return ToCompilerDecl(result); | |
| clang::Decl *result = nullptr; | |
| switch (uid.kind()) { | |
| case PdbSymUidKind::CompilandSym: | |
| result = GetOrCreateSymbolForId(uid.asCompilandSym()); | |
| break; | |
| case PdbSymUidKind::Type: { | |
| clang::QualType qt = GetOrCreateClangType(uid.asTypeSym()); | |
| if (qt.isNull()) | |
| return CompilerDecl(); | |
| if (auto *tag = qt->getAsTagDecl()) { | |
| result = tag; | |
| break; | |
| } | |
| return CompilerDecl(); | |
| } | |
| default: | |
| return CompilerDecl(); | |
| } | |
| if (!result) | |
| return CompilerDecl(); | |
| m_uid_to_decl[toOpaqueUid(uid)] = result; | |
| return ToCompilerDecl(result); | |
| } | |
| clang::DeclContext * | |
| PdbAstBuilderClang::GetOrCreateClangDeclContextForUid(PdbSymUid uid) { | |
| if (uid.kind() == PdbSymUidKind::CompilandSym) { | |
| if (uid.asCompilandSym().offset == 0) | |
| return FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| } | |
| clang::Decl *decl = FromCompilerDecl(GetOrCreateDeclForUid(uid)); | |
| if (!decl) | |
| return nullptr; | |
| return clang::Decl::castToDeclContext(decl); | |
| } | |
| CompilerDeclContext | |
| PdbAstBuilderClang::GetOrCreateDeclContextForUid(PdbSymUid uid) { | |
| return ToCompilerDeclContext(GetOrCreateClangDeclContextForUid(uid)); | |
| } | |
| std::pair<clang::DeclContext *, std::string> | |
| PdbAstBuilderClang::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| MSVCUndecoratedNameParser parser(name); | |
| llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); | |
| auto *context = FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| llvm::StringRef uname = specs.back().GetBaseName(); | |
| specs = specs.drop_back(); | |
| if (specs.empty()) | |
| return {context, std::string(name)}; | |
| llvm::StringRef scope_name = specs.back().GetFullName(); | |
| // It might be a class name, try that first. | |
| std::vector<TypeIndex> types = index.tpi().findRecordsByName(scope_name); | |
| while (!types.empty()) { | |
| clang::QualType qt = GetOrCreateClangType(types.back()); | |
| if (qt.isNull()) | |
| continue; | |
| clang::TagDecl *tag = qt->getAsTagDecl(); | |
| if (tag) | |
| return {clang::TagDecl::castToDeclContext(tag), std::string(uname)}; | |
| types.pop_back(); | |
| } | |
| // If that fails, treat it as a series of namespaces. | |
| for (const MSVCUndecoratedNameSpecifier &spec : specs) { | |
| std::string ns_name = spec.GetBaseName().str(); | |
| context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context); | |
| } | |
| return {context, std::string(uname)}; | |
| } | |
| clang::DeclContext * | |
| PdbAstBuilderClang::GetOrCreateDeclContextForCompilandSymbol( | |
| PdbCompilandSymId uid) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol sym = index.ReadSymbolRecord(uid); | |
| llvm::StringRef symbol_name = getSymbolName(sym); | |
| std::optional<PdbTypeSymId> func_id = GetFunctionType(sym); | |
| if (!func_id || !symbol_name.contains("::")) | |
| return CreateDeclInfoForUndecoratedName(symbol_name).first; | |
| // Try to get the context from class type of an LF_MFUNCTION. | |
| // For some types, we might not find a class type. | |
| auto get_member_fn_context = [&]() -> clang::DeclContext * { | |
| TypeIndex id = func_id->index; | |
| if (func_id->is_ipi) { | |
| // Type from IPI, for example from S_INLINESITE | |
| std::optional<CVType> func_id_type = | |
| index.ipi().tryGetType(func_id->index); | |
| if (!func_id_type || func_id_type->kind() != LF_MFUNC_ID) | |
| return nullptr; | |
| MemberFuncIdRecord record; | |
| llvm::Error err = TypeDeserializer::deserializeAs<MemberFuncIdRecord>( | |
| *func_id_type, record); | |
| if (err) { | |
| llvm::consumeError(std::move(err)); | |
| return nullptr; | |
| } | |
| id = record.FunctionType; | |
| } | |
| std::optional<CVType> func_type = index.tpi().tryGetType(id); | |
| if (!func_type || func_type->kind() != LF_MFUNCTION) | |
| return nullptr; | |
| MemberFunctionRecord mfr(TypeRecordKind::MemberFunction); | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<MemberFunctionRecord>(*func_type, mfr); | |
| if (err || mfr.ClassType.isNoneType()) { | |
| llvm::consumeError(std::move(err)); | |
| return nullptr; | |
| } | |
| clang::QualType qt = GetOrCreateClangType(mfr.ClassType); | |
| if (qt.isNull()) | |
| return nullptr; | |
| clang::TagDecl *tag = qt->getAsTagDecl(); | |
| if (!tag) | |
| return nullptr; | |
| return clang::TagDecl::castToDeclContext(tag); | |
| }; | |
| clang::DeclContext *context = get_member_fn_context(); | |
| if (!context) | |
| return CreateDeclInfoForUndecoratedName(symbol_name).first; | |
| return context; | |
| } | |
| clang::DeclContext * | |
| PdbAstBuilderClang::GetParentClangDeclContext(PdbSymUid uid) { | |
| // We must do this *without* calling GetOrCreate on the current uid, as | |
| // that would be an infinite recursion. | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| switch (uid.kind()) { | |
| case PdbSymUidKind::CompilandSym: { | |
| std::optional<PdbCompilandSymId> scope = | |
| pdb->FindSymbolScope(uid.asCompilandSym()); | |
| if (scope) | |
| return GetOrCreateClangDeclContextForUid(*scope); | |
| return GetOrCreateDeclContextForCompilandSymbol(uid.asCompilandSym()); | |
| } | |
| case PdbSymUidKind::Type: { | |
| // It could be a namespace, class, or global. We don't support nested | |
| // functions yet. Anyway, we just need to consult the parent type map. | |
| PdbTypeSymId type_id = uid.asTypeSym(); | |
| std::optional<TypeIndex> parent_index = pdb->GetParentType(type_id.index); | |
| if (!parent_index) | |
| return FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| return GetOrCreateClangDeclContextForUid(PdbTypeSymId(*parent_index)); | |
| } | |
| case PdbSymUidKind::FieldListMember: | |
| // In this case the parent DeclContext is the one for the class that this | |
| // member is inside of. | |
| break; | |
| case PdbSymUidKind::GlobalSym: { | |
| // If this refers to a compiland symbol, just recurse in with that symbol. | |
| // The only other possibilities are S_CONSTANT and S_UDT, in which case we | |
| // need to parse the undecorated name to figure out the scope, then look | |
| // that up in the TPI stream. If it's found, it's a type, othewrise it's | |
| // a series of namespaces. | |
| // FIXME: do this. | |
| CVSymbol global = index.ReadSymbolRecord(uid.asGlobalSym()); | |
| switch (global.kind()) { | |
| case SymbolKind::S_GDATA32: | |
| case SymbolKind::S_LDATA32: | |
| return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first; | |
| case SymbolKind::S_PROCREF: | |
| case SymbolKind::S_LPROCREF: { | |
| ProcRefSym ref{global.kind()}; | |
| llvm::Error err = | |
| SymbolDeserializer::deserializeAs<ProcRefSym>(global, ref); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as ProcRef: {0}", | |
| uid.asGlobalSym()); | |
| return nullptr; | |
| } | |
| PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset}; | |
| return GetParentClangDeclContext(cu_sym_id); | |
| } | |
| case SymbolKind::S_CONSTANT: | |
| case SymbolKind::S_UDT: | |
| return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first; | |
| default: | |
| break; | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| return FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| } | |
| CompilerDeclContext PdbAstBuilderClang::GetParentDeclContext(PdbSymUid uid) { | |
| return ToCompilerDeclContext(GetParentClangDeclContext(uid)); | |
| } | |
| bool PdbAstBuilderClang::CompleteType(CompilerType ct) { | |
| if (GetClangASTImporter().CanImport(ct)) | |
| return GetClangASTImporter().CompleteType(ct); | |
| clang::QualType qt = FromCompilerType(ct); | |
| if (qt.isNull()) | |
| return false; | |
| clang::TagDecl *tag = qt->getAsTagDecl(); | |
| if (qt->isArrayType()) { | |
| const clang::Type *element_type = qt->getArrayElementTypeNoTypeQual(); | |
| tag = element_type->getAsTagDecl(); | |
| } | |
| if (!tag) | |
| return false; | |
| return CompleteTagDecl(*tag); | |
| } | |
| bool PdbAstBuilderClang::CompleteTagDecl(clang::TagDecl &tag) { | |
| auto status_iter = m_decl_to_status.find(&tag); | |
| if (status_iter == m_decl_to_status.end()) { | |
| // If this is not in our map, it's an error. | |
| assert(false && "completing unknown tag decl"); | |
| return false; | |
| } | |
| // If it's already complete, just return. | |
| DeclStatus &status = status_iter->second; | |
| if (status.resolved) | |
| return true; | |
| PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym(); | |
| PdbIndex &index = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()) | |
| ->GetIndex(); | |
| clang::QualType tag_qt = m_clang.getASTContext().getCanonicalTagType(&tag); | |
| TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); | |
| TypeIndex tag_ti = type_id.index; | |
| CVType cvt = index.tpi().getType(tag_ti); | |
| if (cvt.kind() == LF_MODIFIER) | |
| tag_ti = LookThroughModifierRecord(cvt); | |
| PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, index.tpi()); | |
| cvt = index.tpi().getType(best_ti.index); | |
| if (!IsTagRecord(cvt)) { | |
| assert(false && "completing tag record that's not a tag record"); | |
| return false; | |
| } | |
| if (IsForwardRefUdt(cvt)) { | |
| // If we can't find a full decl for this forward ref anywhere in the debug | |
| // info, then we have no way to complete it. | |
| return false; | |
| } | |
| TypeIndex field_list_ti = GetFieldListIndex(cvt); | |
| CVType field_list_cvt = index.tpi().getType(field_list_ti); | |
| if (field_list_cvt.kind() != LF_FIELDLIST) | |
| return false; | |
| FieldListRecord field_list; | |
| if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( | |
| field_list_cvt, field_list)) | |
| llvm::consumeError(std::move(error)); | |
| // Visit all members of this class, then perform any finalization necessary | |
| // to complete the class. | |
| CompilerType ct = ToCompilerType(tag_qt); | |
| UdtRecordCompleter completer(best_ti, ct, tag, *this, index, m_decl_to_status, | |
| m_cxx_record_map); | |
| llvm::Error error = | |
| llvm::codeview::visitMemberRecordStream(field_list.Data, completer); | |
| completer.complete(); | |
| m_decl_to_status[&tag].resolved = true; | |
| if (error) { | |
| llvm::consumeError(std::move(error)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateSimpleType(TypeIndex ti) { | |
| if (ti == TypeIndex::NullptrT()) | |
| return GetBasicType(lldb::eBasicTypeNullPtr); | |
| if (ti.getSimpleMode() != SimpleTypeMode::Direct) { | |
| clang::QualType direct_type = GetOrCreateClangType(ti.makeDirect()); | |
| if (direct_type.isNull()) | |
| return {}; | |
| return m_clang.getASTContext().getPointerType(direct_type); | |
| } | |
| if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) | |
| return {}; | |
| lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind()); | |
| if (bt == lldb::eBasicTypeInvalid) | |
| return {}; | |
| return GetBasicType(bt); | |
| } | |
| clang::QualType | |
| PdbAstBuilderClang::CreatePointerType(const PointerRecord &pointer) { | |
| clang::QualType pointee_type = GetOrCreateClangType(pointer.ReferentType); | |
| // This can happen for pointers to LF_VTSHAPE records, which we shouldn't | |
| // create in the AST. | |
| if (pointee_type.isNull()) | |
| return {}; | |
| if (pointer.isPointerToMember()) { | |
| MemberPointerInfo mpi = pointer.getMemberInfo(); | |
| clang::QualType class_type = GetOrCreateClangType(mpi.ContainingType); | |
| if (class_type.isNull()) | |
| return {}; | |
| if (clang::TagDecl *tag = class_type->getAsTagDecl()) { | |
| clang::MSInheritanceAttr::Spelling spelling; | |
| switch (mpi.Representation) { | |
| case llvm::codeview::PointerToMemberRepresentation::SingleInheritanceData: | |
| case llvm::codeview::PointerToMemberRepresentation:: | |
| SingleInheritanceFunction: | |
| spelling = | |
| clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; | |
| break; | |
| case llvm::codeview::PointerToMemberRepresentation:: | |
| MultipleInheritanceData: | |
| case llvm::codeview::PointerToMemberRepresentation:: | |
| MultipleInheritanceFunction: | |
| spelling = | |
| clang::MSInheritanceAttr::Spelling::Keyword_multiple_inheritance; | |
| break; | |
| case llvm::codeview::PointerToMemberRepresentation:: | |
| VirtualInheritanceData: | |
| case llvm::codeview::PointerToMemberRepresentation:: | |
| VirtualInheritanceFunction: | |
| spelling = | |
| clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance; | |
| break; | |
| case llvm::codeview::PointerToMemberRepresentation::Unknown: | |
| spelling = | |
| clang::MSInheritanceAttr::Spelling::Keyword_unspecified_inheritance; | |
| break; | |
| default: | |
| spelling = clang::MSInheritanceAttr::Spelling::SpellingNotCalculated; | |
| break; | |
| } | |
| tag->addAttr(clang::MSInheritanceAttr::CreateImplicit( | |
| m_clang.getASTContext(), spelling)); | |
| } | |
| return m_clang.getASTContext().getMemberPointerType( | |
| pointee_type, /*Qualifier=*/std::nullopt, | |
| class_type->getAsCXXRecordDecl()); | |
| } | |
| clang::QualType pointer_type; | |
| if (pointer.getMode() == PointerMode::LValueReference) | |
| pointer_type = m_clang.getASTContext().getLValueReferenceType(pointee_type); | |
| else if (pointer.getMode() == PointerMode::RValueReference) | |
| pointer_type = m_clang.getASTContext().getRValueReferenceType(pointee_type); | |
| else | |
| pointer_type = m_clang.getASTContext().getPointerType(pointee_type); | |
| if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None) | |
| pointer_type.addConst(); | |
| if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None) | |
| pointer_type.addVolatile(); | |
| if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None) | |
| pointer_type.addRestrict(); | |
| return pointer_type; | |
| } | |
| clang::QualType | |
| PdbAstBuilderClang::CreateModifierType(const ModifierRecord &modifier) { | |
| clang::QualType unmodified_type = GetOrCreateClangType(modifier.ModifiedType); | |
| if (unmodified_type.isNull()) | |
| return {}; | |
| if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None) | |
| unmodified_type.addConst(); | |
| if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None) | |
| unmodified_type.addVolatile(); | |
| return unmodified_type; | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateRecordType(PdbTypeSymId id, | |
| const TagRecord &record) { | |
| clang::DeclContext *context = nullptr; | |
| std::string uname; | |
| std::tie(context, uname) = CreateDeclInfoForType(record, id.index); | |
| if (!context) | |
| return {}; | |
| clang::TagTypeKind ttk = TranslateUdtKind(record); | |
| ClangASTMetadata metadata; | |
| metadata.SetUserID(toOpaqueUid(id)); | |
| metadata.SetIsDynamicCXXType(false); | |
| CompilerType ct = m_clang.CreateRecordType( | |
| context, OptionalClangModuleID(), uname, llvm::to_underlying(ttk), | |
| lldb::eLanguageTypeC_plus_plus, metadata); | |
| if (!ct.IsValid()) { | |
| LLDB_LOG(GetLog(LLDBLog::Symbols), "failed to create record type for {0}", | |
| id); | |
| return {}; | |
| } | |
| TypeSystemClang::StartTagDeclarationDefinition(ct); | |
| // Even if it's possible, don't complete it at this point. Just mark it | |
| // forward resolved, and if/when LLDB needs the full definition, it can | |
| // ask us. | |
| clang::QualType result = | |
| clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); | |
| TypeSystemClang::SetHasExternalStorage(result.getAsOpaquePtr(), true); | |
| return result; | |
| } | |
| clang::Decl *PdbAstBuilderClang::TryGetDecl(PdbSymUid uid) const { | |
| auto iter = m_uid_to_decl.find(toOpaqueUid(uid)); | |
| if (iter != m_uid_to_decl.end()) | |
| return iter->second; | |
| return nullptr; | |
| } | |
| clang::NamespaceDecl * | |
| PdbAstBuilderClang::GetOrCreateNamespaceDecl(const char *name, | |
| clang::DeclContext &context) { | |
| clang::NamespaceDecl *ns = m_clang.GetUniqueNamespaceDeclaration( | |
| IsAnonymousNamespaceName(name) ? nullptr : name, &context, | |
| OptionalClangModuleID()); | |
| m_known_namespaces.insert(ns); | |
| m_parent_to_namespaces[&context].insert(ns); | |
| return ns; | |
| } | |
| clang::BlockDecl * | |
| PdbAstBuilderClang::GetOrCreateBlockDecl(PdbCompilandSymId block_id) { | |
| if (clang::Decl *decl = TryGetDecl(block_id)) | |
| return llvm::dyn_cast<clang::BlockDecl>(decl); | |
| clang::DeclContext *scope = GetParentClangDeclContext(block_id); | |
| clang::BlockDecl *block_decl = | |
| m_clang.CreateBlockDeclaration(scope, OptionalClangModuleID()); | |
| m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl}); | |
| DeclStatus status; | |
| status.resolved = true; | |
| status.uid = toOpaqueUid(block_id); | |
| m_decl_to_status.insert({block_decl, status}); | |
| return block_decl; | |
| } | |
| clang::VarDecl * | |
| PdbAstBuilderClang::CreateVariableDecl(PdbSymUid uid, CVSymbol sym, | |
| clang::DeclContext &scope) { | |
| VariableInfo var_info = GetVariableNameInfo(sym); | |
| clang::QualType qt = GetOrCreateClangType(var_info.type); | |
| if (qt.isNull()) | |
| return nullptr; | |
| clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration( | |
| &scope, OptionalClangModuleID(), var_info.name.str().c_str(), qt); | |
| m_uid_to_decl[toOpaqueUid(uid)] = var_decl; | |
| DeclStatus status; | |
| status.resolved = true; | |
| status.uid = toOpaqueUid(uid); | |
| m_decl_to_status.insert({var_decl, status}); | |
| return var_decl; | |
| } | |
| clang::VarDecl * | |
| PdbAstBuilderClang::GetOrCreateVariableDecl(PdbCompilandSymId scope_id, | |
| PdbCompilandSymId var_id) { | |
| if (clang::Decl *decl = TryGetDecl(var_id)) | |
| return llvm::dyn_cast<clang::VarDecl>(decl); | |
| clang::DeclContext *scope = GetOrCreateClangDeclContextForUid(scope_id); | |
| if (!scope) | |
| return nullptr; | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol sym = index.ReadSymbolRecord(var_id); | |
| return CreateVariableDecl(PdbSymUid(var_id), sym, *scope); | |
| } | |
| clang::VarDecl * | |
| PdbAstBuilderClang::GetOrCreateVariableDecl(PdbGlobalSymId var_id) { | |
| if (clang::Decl *decl = TryGetDecl(var_id)) | |
| return llvm::dyn_cast<clang::VarDecl>(decl); | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol sym = index.ReadSymbolRecord(var_id); | |
| auto context = FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| return CreateVariableDecl(PdbSymUid(var_id), sym, *context); | |
| } | |
| CompilerType PdbAstBuilderClang::GetOrCreateTypedefType(PdbGlobalSymId id) { | |
| if (clang::Decl *decl = TryGetDecl(id)) { | |
| if (auto *tnd = llvm::dyn_cast<clang::TypedefNameDecl>(decl)) | |
| return ToCompilerType(m_clang.getASTContext().getTypeDeclType(tnd)); | |
| return CompilerType(); | |
| } | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol sym = index.ReadSymbolRecord(id); | |
| if (sym.kind() != S_UDT) { | |
| assert(false && "called on a non-udt type"); | |
| return {}; | |
| } | |
| llvm::Expected<UDTSym> udt = SymbolDeserializer::deserializeAs<UDTSym>(sym); | |
| if (!udt) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), udt.takeError(), | |
| "Failed to deserialize {1} as UDT: {0}", id); | |
| return CompilerType(); | |
| } | |
| clang::DeclContext *scope = GetParentClangDeclContext(id); | |
| PdbTypeSymId real_type_id{udt->Type, false}; | |
| clang::QualType qt = GetOrCreateClangType(real_type_id); | |
| if (qt.isNull() || !scope) | |
| return CompilerType(); | |
| std::string uname = std::string(DropNameScope(udt->Name)); | |
| CompilerType ct = ToCompilerType(qt).CreateTypedef( | |
| uname.c_str(), ToCompilerDeclContext(scope), 0); | |
| DeclStatus status; | |
| status.resolved = true; | |
| status.uid = toOpaqueUid(id); | |
| m_decl_to_status.insert({m_clang.GetAsTypedefDecl(ct), status}); | |
| return ct; | |
| } | |
| clang::QualType PdbAstBuilderClang::GetBasicType(lldb::BasicType type) { | |
| CompilerType ct = m_clang.GetBasicType(type); | |
| return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateType(PdbTypeSymId type) { | |
| if (type.index.isSimple()) | |
| return CreateSimpleType(type.index); | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVType cvt = index.tpi().getType(type.index); | |
| if (cvt.kind() == LF_MODIFIER) { | |
| ModifierRecord modifier; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Modifier: {0}", type.index); | |
| return {}; | |
| } | |
| return CreateModifierType(modifier); | |
| } | |
| if (cvt.kind() == LF_POINTER) { | |
| PointerRecord pointer; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Pointer: {0}", type.index); | |
| return {}; | |
| } | |
| return CreatePointerType(pointer); | |
| } | |
| if (IsTagRecord(cvt)) { | |
| CVTagRecord tag = CVTagRecord::create(cvt); | |
| if (tag.kind() == CVTagRecord::Union) | |
| return CreateRecordType(type.index, tag.asUnion()); | |
| if (tag.kind() == CVTagRecord::Enum) | |
| return CreateEnumType(type.index, tag.asEnum()); | |
| return CreateRecordType(type.index, tag.asClass()); | |
| } | |
| if (cvt.kind() == LF_ARRAY) { | |
| ArrayRecord ar; | |
| llvm::Error err = TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Array: {0}", type.index); | |
| return {}; | |
| } | |
| return CreateArrayType(ar); | |
| } | |
| if (cvt.kind() == LF_PROCEDURE) { | |
| ProcedureRecord pr; | |
| llvm::Error err = TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Procedure: {0}", type.index); | |
| return {}; | |
| } | |
| return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv, | |
| /*type_quals=*/0); | |
| } | |
| if (cvt.kind() == LF_MFUNCTION) { | |
| MemberFunctionRecord mfr; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as MemberFunction: {0}", | |
| type.index); | |
| return {}; | |
| } | |
| unsigned int type_quals = 0; | |
| if (!mfr.ThisType.isNoneType()) { | |
| clang::QualType this_type = GetOrCreateClangType(mfr.getThisType()); | |
| if (!this_type.isNull()) | |
| type_quals = this_type->getPointeeType().getLocalFastQualifiers(); | |
| } | |
| return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv, | |
| type_quals); | |
| } | |
| return {}; | |
| } | |
| clang::QualType PdbAstBuilderClang::GetOrCreateClangType(PdbTypeSymId type) { | |
| if (type.index.isNoneType()) | |
| return {}; | |
| lldb::user_id_t uid = toOpaqueUid(type); | |
| auto iter = m_uid_to_type.find(uid); | |
| if (iter != m_uid_to_type.end()) | |
| return iter->second; | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| PdbTypeSymId best_type = GetBestPossibleDecl(type, index.tpi()); | |
| clang::QualType qt; | |
| if (best_type.index != type.index) { | |
| // This is a forward decl. Call GetOrCreate on the full decl, then map the | |
| // forward decl id to the full decl QualType. | |
| clang::QualType qt = GetOrCreateClangType(best_type); | |
| if (qt.isNull()) | |
| return {}; | |
| m_uid_to_type[toOpaqueUid(type)] = qt; | |
| return qt; | |
| } | |
| // This is either a full decl, or a forward decl with no matching full decl | |
| // in the debug info. | |
| qt = CreateType(type); | |
| if (qt.isNull()) | |
| return {}; | |
| m_uid_to_type[toOpaqueUid(type)] = qt; | |
| if (IsTagRecord(type, index.tpi())) { | |
| clang::TagDecl *tag = qt->getAsTagDecl(); | |
| assert(m_decl_to_status.count(tag) == 0 && "type already created"); | |
| DeclStatus &status = m_decl_to_status[tag]; | |
| status.uid = uid; | |
| status.resolved = false; | |
| } | |
| return qt; | |
| } | |
| CompilerType PdbAstBuilderClang::GetOrCreateType(PdbTypeSymId type) { | |
| clang::QualType qt = GetOrCreateClangType(type); | |
| if (qt.isNull()) | |
| return {}; | |
| return ToCompilerType(qt); | |
| } | |
| clang::FunctionDecl *PdbAstBuilderClang::CreateFunctionDecl( | |
| PdbCompilandSymId func_id, llvm::StringRef func_name, TypeIndex func_ti, | |
| CompilerType func_ct, uint32_t param_count, | |
| clang::StorageClass func_storage, bool is_inline, | |
| clang::DeclContext *parent) { | |
| clang::FunctionDecl *function_decl = nullptr; | |
| if (parent->isRecord()) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| clang::CanQualType parent_qt = | |
| m_clang.getASTContext().getCanonicalTypeDeclType( | |
| llvm::cast<clang::TypeDecl>(parent)); | |
| lldb::opaque_compiler_type_t parent_opaque_ty = | |
| ToCompilerType(parent_qt).GetOpaqueQualType(); | |
| // FIXME: Remove this workaround. | |
| auto iter = m_cxx_record_map.find(parent_opaque_ty); | |
| if (iter != m_cxx_record_map.end()) { | |
| if (iter->getSecond().contains({func_name, func_ct})) { | |
| return nullptr; | |
| } | |
| } | |
| CVType cvt = index.tpi().getType(func_ti); | |
| MemberFunctionRecord func_record(static_cast<TypeRecordKind>(cvt.kind())); | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, func_record); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as MemberFunction: {0}", | |
| func_ti); | |
| return nullptr; | |
| } | |
| TypeIndex class_index = func_record.getClassType(); | |
| CVType parent_cvt = index.tpi().getType(class_index); | |
| if (!IsTagRecord(parent_cvt)) | |
| return nullptr; | |
| TagRecord tag_record = CVTagRecord::create(parent_cvt).asTag(); | |
| // If it's a forward reference, try to get the real TypeIndex. | |
| if (tag_record.isForwardRef()) { | |
| llvm::Expected<TypeIndex> eti = | |
| index.tpi().findFullDeclForForwardRef(class_index); | |
| if (eti) { | |
| CVType resolved_tag_record = index.tpi().getType(*eti); | |
| if (!IsTagRecord(resolved_tag_record)) | |
| return nullptr; | |
| tag_record = CVTagRecord::create(resolved_tag_record).asTag(); | |
| } else { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), eti.takeError(), | |
| "failed to find full decl for forward ref: {0}"); | |
| } | |
| } | |
| ConstString mangled_name( | |
| pdb->FindMangledFunctionName(func_id).value_or(llvm::StringRef())); | |
| if (!tag_record.FieldList.isSimple()) { | |
| CVType field_list_cvt = index.tpi().getType(tag_record.FieldList); | |
| FieldListRecord field_list; | |
| if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( | |
| field_list_cvt, field_list)) | |
| llvm::consumeError(std::move(error)); | |
| CreateMethodDecl process(index, m_clang, func_ti, function_decl, | |
| parent_opaque_ty, func_name, mangled_name, | |
| func_ct); | |
| if (llvm::Error err = visitMemberRecordStream(field_list.Data, process)) | |
| llvm::consumeError(std::move(err)); | |
| } | |
| if (!function_decl) { | |
| function_decl = m_clang.AddMethodToCXXRecordType( | |
| parent_opaque_ty, func_name, mangled_name, func_ct, | |
| /*is_virtual=*/false, /*is_static=*/false, | |
| /*is_inline=*/false, /*is_explicit=*/false, | |
| /*is_attr_used=*/false, /*is_artificial=*/false); | |
| } | |
| m_cxx_record_map[parent_opaque_ty].insert({func_name, func_ct}); | |
| } else { | |
| function_decl = m_clang.CreateFunctionDeclaration( | |
| parent, OptionalClangModuleID(), func_name, func_ct, func_storage, | |
| is_inline, /*asm_label=*/{}); | |
| CreateFunctionParameters(func_id, *function_decl, param_count); | |
| } | |
| return function_decl; | |
| } | |
| clang::FunctionDecl *PdbAstBuilderClang::GetOrCreateInlinedFunctionDecl( | |
| PdbCompilandSymId inlinesite_id) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CompilandIndexItem *cii = index.compilands().GetCompiland(inlinesite_id.modi); | |
| CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(inlinesite_id.offset); | |
| InlineSiteSym inline_site(static_cast<SymbolRecordKind>(sym.kind())); | |
| llvm::Error err = | |
| SymbolDeserializer::deserializeAs<InlineSiteSym>(sym, inline_site); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as InlineSite: {0}", | |
| inlinesite_id); | |
| return nullptr; | |
| } | |
| // Inlinee is the id index to the function id record that is inlined. | |
| PdbTypeSymId func_id(inline_site.Inlinee, true); | |
| // Look up the function decl by the id index to see if we have created a | |
| // function decl for a different inlinesite that refers the same function. | |
| if (clang::Decl *decl = TryGetDecl(func_id)) | |
| return llvm::dyn_cast<clang::FunctionDecl>(decl); | |
| clang::FunctionDecl *function_decl = | |
| CreateFunctionDeclFromId(func_id, inlinesite_id); | |
| if (function_decl == nullptr) | |
| return nullptr; | |
| // Use inline site id in m_decl_to_status because it's expected to be a | |
| // PdbCompilandSymId so that we can parse local variables info after it. | |
| uint64_t inlinesite_uid = toOpaqueUid(inlinesite_id); | |
| DeclStatus status; | |
| status.resolved = true; | |
| status.uid = inlinesite_uid; | |
| m_decl_to_status.insert({function_decl, status}); | |
| // Use the index in IPI stream as uid in m_uid_to_decl, because index in IPI | |
| // stream are unique and there could be multiple inline sites (different ids) | |
| // referring the same inline function. This avoid creating multiple same | |
| // inline function delcs. | |
| uint64_t func_uid = toOpaqueUid(func_id); | |
| assert(m_uid_to_decl.count(func_uid) == 0 && "already created"); | |
| m_uid_to_decl[func_uid] = function_decl; | |
| return function_decl; | |
| } | |
| clang::FunctionDecl * | |
| PdbAstBuilderClang::CreateFunctionDeclFromId(PdbTypeSymId func_tid, | |
| PdbCompilandSymId func_sid) { | |
| if (!func_tid.is_ipi) { | |
| assert(false && "called with non-ipi index"); | |
| return nullptr; | |
| } | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| std::optional<CVType> func_cvt = | |
| index.ipi().typeCollection().tryGetType(func_tid.index); | |
| if (!func_cvt) | |
| return nullptr; | |
| llvm::StringRef func_name; | |
| TypeIndex func_ti; | |
| clang::DeclContext *parent = nullptr; | |
| switch (func_cvt->kind()) { | |
| case LF_MFUNC_ID: { | |
| MemberFuncIdRecord mfr; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<MemberFuncIdRecord>(*func_cvt, mfr); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} (IPI) as MemberFuncId: {0}", | |
| func_tid.index); | |
| return nullptr; | |
| } | |
| func_name = mfr.getName(); | |
| func_ti = mfr.getFunctionType(); | |
| PdbTypeSymId class_type_id(mfr.ClassType, false); | |
| parent = GetOrCreateClangDeclContextForUid(class_type_id); | |
| break; | |
| } | |
| case LF_FUNC_ID: { | |
| FuncIdRecord fir; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<FuncIdRecord>(*func_cvt, fir); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} (IPI) as FuncId: {0}", | |
| func_tid.index); | |
| return nullptr; | |
| } | |
| func_name = fir.getName(); | |
| func_ti = fir.getFunctionType(); | |
| parent = FromCompilerDeclContext(GetTranslationUnitDecl()); | |
| if (!fir.ParentScope.isNoneType()) { | |
| CVType parent_cvt = index.ipi().getType(fir.ParentScope); | |
| if (parent_cvt.kind() == LF_STRING_ID) { | |
| StringIdRecord sir; | |
| err = TypeDeserializer::deserializeAs<StringIdRecord>(parent_cvt, sir); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} (IPI) as StringId: {0}", | |
| fir.ParentScope); | |
| return nullptr; | |
| } | |
| parent = GetOrCreateNamespaceDecl(sir.String.data(), *parent); | |
| } | |
| } | |
| break; | |
| } | |
| default: | |
| LLDB_LOG(GetLog(LLDBLog::Symbols), "{0} is not a function type", func_tid); | |
| return nullptr; | |
| } | |
| clang::QualType func_qt = GetOrCreateClangType(func_ti); | |
| if (func_qt.isNull() || !parent) | |
| return nullptr; | |
| CompilerType func_ct = ToCompilerType(func_qt); | |
| uint32_t param_count = | |
| llvm::cast<clang::FunctionProtoType>(func_qt)->getNumParams(); | |
| return CreateFunctionDecl(func_sid, func_name, func_ti, func_ct, param_count, | |
| clang::SC_None, true, parent); | |
| } | |
| clang::FunctionDecl * | |
| PdbAstBuilderClang::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { | |
| if (clang::Decl *decl = TryGetDecl(func_id)) | |
| return llvm::dyn_cast<clang::FunctionDecl>(decl); | |
| clang::DeclContext *parent = GetParentClangDeclContext(PdbSymUid(func_id)); | |
| if (!parent) | |
| return nullptr; | |
| std::string context_name; | |
| if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) { | |
| context_name = ns->getQualifiedNameAsString(); | |
| } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) { | |
| context_name = tag->getQualifiedNameAsString(); | |
| } | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol cvs = index.ReadSymbolRecord(func_id); | |
| ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind())); | |
| llvm::Error err = SymbolDeserializer::deserializeAs<ProcSym>(cvs, proc); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Proc: {0}", func_id); | |
| return nullptr; | |
| } | |
| PdbTypeSymId type_id(proc.FunctionType); | |
| clang::QualType qt = GetOrCreateClangType(type_id); | |
| if (qt.isNull()) | |
| return nullptr; | |
| clang::StorageClass storage = clang::SC_None; | |
| if (proc.Kind == SymbolRecordKind::ProcSym) | |
| storage = clang::SC_Static; | |
| const clang::FunctionProtoType *func_type = | |
| llvm::dyn_cast<clang::FunctionProtoType>(qt); | |
| if (!func_type) | |
| return nullptr; | |
| CompilerType func_ct = ToCompilerType(qt); | |
| llvm::StringRef proc_name = proc.Name; | |
| if (!context_name.empty() && !(proc_name.consume_front(context_name) && | |
| proc_name.consume_front("::"))) { | |
| // If we have some context, but the function name doesn't start with it, use | |
| // the basename. | |
| MSVCUndecoratedNameParser parser(proc.Name); | |
| llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs(parser.GetSpecifiers()); | |
| if (!specs.empty()) | |
| proc_name = specs.back().GetBaseName(); | |
| } | |
| clang::FunctionDecl *function_decl = | |
| CreateFunctionDecl(func_id, proc_name, proc.FunctionType, func_ct, | |
| func_type->getNumParams(), storage, false, parent); | |
| if (function_decl == nullptr) | |
| return nullptr; | |
| assert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0 && "already created"); | |
| m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; | |
| DeclStatus status; | |
| status.resolved = true; | |
| status.uid = toOpaqueUid(func_id); | |
| m_decl_to_status.insert({function_decl, status}); | |
| return function_decl; | |
| } | |
| void PdbAstBuilderClang::EnsureFunction(PdbCompilandSymId func_id) { | |
| GetOrCreateFunctionDecl(func_id); | |
| } | |
| void PdbAstBuilderClang::EnsureInlinedFunction( | |
| PdbCompilandSymId inlinesite_id) { | |
| GetOrCreateInlinedFunctionDecl(inlinesite_id); | |
| } | |
| void PdbAstBuilderClang::EnsureBlock(PdbCompilandSymId block_id) { | |
| GetOrCreateBlockDecl(block_id); | |
| } | |
| void PdbAstBuilderClang::EnsureVariable(PdbCompilandSymId scope_id, | |
| PdbCompilandSymId var_id) { | |
| GetOrCreateVariableDecl(scope_id, var_id); | |
| } | |
| void PdbAstBuilderClang::EnsureVariable(PdbGlobalSymId var_id) { | |
| GetOrCreateVariableDecl(var_id); | |
| } | |
| void PdbAstBuilderClang::CreateFunctionParameters( | |
| PdbCompilandSymId func_id, clang::FunctionDecl &function_decl, | |
| uint32_t param_count) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CompilandIndexItem *cii = index.compilands().GetCompiland(func_id.modi); | |
| CVSymbolArray scope = | |
| cii->m_debug_stream.getSymbolArrayForScope(func_id.offset); | |
| scope.drop_front(); | |
| auto begin = scope.begin(); | |
| auto end = scope.end(); | |
| std::vector<clang::ParmVarDecl *> params; | |
| for (uint32_t i = 0; i < param_count && begin != end;) { | |
| uint32_t record_offset = begin.offset(); | |
| PdbCompilandSymId sym_id(func_id.modi, record_offset); | |
| CVSymbol sym = *begin++; | |
| TypeIndex param_type; | |
| llvm::StringRef param_name; | |
| switch (sym.kind()) { | |
| case S_REGREL32: { | |
| RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); | |
| llvm::Error err = | |
| SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as RegRelative: {0}", sym_id); | |
| return; | |
| } | |
| param_type = reg.Type; | |
| param_name = reg.Name; | |
| break; | |
| } | |
| case S_REGREL32_INDIR: { | |
| RegRelativeIndirSym reg(SymbolRecordKind::RegRelativeIndirSym); | |
| llvm::Error err = | |
| SymbolDeserializer::deserializeAs<RegRelativeIndirSym>(sym, reg); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as RegRelativeIndir: {0}", | |
| sym_id); | |
| return; | |
| } | |
| param_type = reg.Type; | |
| param_name = reg.Name; | |
| break; | |
| } | |
| case S_REGISTER: { | |
| RegisterSym reg(SymbolRecordKind::RegisterSym); | |
| llvm::Error err = | |
| SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Register: {0}", sym_id); | |
| return; | |
| } | |
| param_type = reg.Index; | |
| param_name = reg.Name; | |
| break; | |
| } | |
| case S_LOCAL: { | |
| LocalSym local(SymbolRecordKind::LocalSym); | |
| llvm::Error err = SymbolDeserializer::deserializeAs<LocalSym>(sym, local); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as Local: {0}", sym_id); | |
| return; | |
| } | |
| if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None) | |
| continue; | |
| param_type = local.Type; | |
| param_name = local.Name; | |
| break; | |
| } | |
| case S_BLOCK32: | |
| case S_INLINESITE: | |
| case S_INLINESITE2: | |
| // All parameters should come before the first block/inlinesite. If that | |
| // isn't the case, then perhaps this is bad debug info that doesn't | |
| // contain information about all parameters. | |
| return; | |
| default: | |
| continue; | |
| } | |
| PdbCompilandSymId param_uid(func_id.modi, record_offset); | |
| clang::QualType qt = GetOrCreateClangType(param_type); | |
| if (qt.isNull()) | |
| return; | |
| CompilerType param_type_ct = m_clang.GetType(qt); | |
| clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration( | |
| &function_decl, OptionalClangModuleID(), param_name.str().c_str(), | |
| param_type_ct, clang::SC_None, true); | |
| assert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0 && | |
| "already created"); | |
| m_uid_to_decl[toOpaqueUid(param_uid)] = param; | |
| params.push_back(param); | |
| ++i; | |
| } | |
| if (!params.empty() && params.size() == param_count) | |
| function_decl.setParams(params); | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateEnumType(PdbTypeSymId id, | |
| const EnumRecord &er) { | |
| clang::DeclContext *decl_context = nullptr; | |
| std::string uname; | |
| std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index); | |
| if (!decl_context) | |
| return {}; | |
| clang::QualType underlying_type = GetOrCreateClangType(er.UnderlyingType); | |
| if (underlying_type.isNull()) | |
| return {}; | |
| Declaration declaration; | |
| CompilerType enum_ct = m_clang.CreateEnumerationType( | |
| uname, decl_context, OptionalClangModuleID(), declaration, | |
| ToCompilerType(underlying_type), er.isScoped()); | |
| TypeSystemClang::StartTagDeclarationDefinition(enum_ct); | |
| TypeSystemClang::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true); | |
| return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType()); | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateArrayType(const ArrayRecord &ar) { | |
| clang::QualType element_type = GetOrCreateClangType(ar.ElementType); | |
| TypeSystemClang::RequireCompleteType(ToCompilerType(element_type)); | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| uint64_t element_size = GetSizeOfType({ar.ElementType}, index.tpi()); | |
| if (element_type.isNull() || element_size == 0) | |
| return {}; | |
| uint64_t element_count = ar.Size / element_size; | |
| CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), | |
| element_count, false); | |
| return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); | |
| } | |
| clang::QualType PdbAstBuilderClang::CreateFunctionType( | |
| TypeIndex args_type_idx, TypeIndex return_type_idx, | |
| llvm::codeview::CallingConvention calling_convention, | |
| unsigned int type_quals) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| TpiStream &stream = index.tpi(); | |
| CVType args_cvt = stream.getType(args_type_idx); | |
| ArgListRecord args; | |
| llvm::Error err = | |
| TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args); | |
| if (err) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), | |
| "Failed to deserialize {1} as ArgList: {0}", args_type_idx); | |
| return {}; | |
| } | |
| llvm::ArrayRef<TypeIndex> arg_indices = llvm::ArrayRef(args.ArgIndices); | |
| bool is_variadic = IsCVarArgsFunction(arg_indices); | |
| if (is_variadic) | |
| arg_indices = arg_indices.drop_back(); | |
| std::vector<CompilerType> arg_types; | |
| arg_types.reserve(arg_indices.size()); | |
| for (TypeIndex arg_index : arg_indices) { | |
| clang::QualType arg_type = GetOrCreateClangType(arg_index); | |
| if (arg_type.isNull()) | |
| continue; | |
| arg_types.push_back(ToCompilerType(arg_type)); | |
| } | |
| clang::QualType return_type = GetOrCreateClangType(return_type_idx); | |
| if (return_type.isNull()) | |
| return {}; | |
| std::optional<clang::CallingConv> cc = | |
| TranslateCallingConvention(calling_convention); | |
| if (!cc) | |
| return {}; | |
| CompilerType return_ct = ToCompilerType(return_type); | |
| CompilerType func_sig_ast_type = m_clang.CreateFunctionType( | |
| return_ct, arg_types, is_variadic, type_quals, *cc); | |
| return clang::QualType::getFromOpaquePtr( | |
| func_sig_ast_type.GetOpaqueQualType()); | |
| } | |
| static bool isTagDecl(clang::DeclContext &context) { | |
| return llvm::isa<clang::TagDecl>(&context); | |
| } | |
| static bool isFunctionDecl(clang::DeclContext &context) { | |
| return llvm::isa<clang::FunctionDecl>(&context); | |
| } | |
| static bool isBlockDecl(clang::DeclContext &context) { | |
| return llvm::isa<clang::BlockDecl>(&context); | |
| } | |
| void PdbAstBuilderClang::ParseNamespace(clang::DeclContext &context) { | |
| clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(&context); | |
| if (m_parsed_namespaces.contains(ns)) | |
| return; | |
| std::string qname = ns->getQualifiedNameAsString(); | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| TypeIndex ti{index.tpi().TypeIndexBegin()}; | |
| for (const CVType &cvt : index.tpi().typeArray()) { | |
| PdbTypeSymId tid{ti}; | |
| ++ti; | |
| if (!IsTagRecord(cvt)) | |
| continue; | |
| CVTagRecord tag = CVTagRecord::create(cvt); | |
| // Call CreateDeclInfoForType unconditionally so that the namespace info | |
| // gets created. But only call CreateRecordType if the namespace name | |
| // matches. | |
| clang::DeclContext *context = nullptr; | |
| std::string uname; | |
| std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index); | |
| if (!context || !context->isNamespace()) | |
| continue; | |
| clang::NamespaceDecl *ns = llvm::cast<clang::NamespaceDecl>(context); | |
| llvm::StringRef ns_name = ns->getName(); | |
| if (ns_name.starts_with(qname)) { | |
| ns_name = ns_name.drop_front(qname.size()); | |
| if (ns_name.starts_with("::")) | |
| GetOrCreateClangType(tid); | |
| } | |
| } | |
| ParseAllFunctionsAndNonLocalVars(); | |
| m_parsed_namespaces.insert(ns); | |
| } | |
| void PdbAstBuilderClang::ParseAllTypes() { | |
| llvm::call_once(m_parse_all_types, [this]() { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| TypeIndex ti{index.tpi().TypeIndexBegin()}; | |
| for (const CVType &cvt : index.tpi().typeArray()) { | |
| PdbTypeSymId tid{ti}; | |
| ++ti; | |
| if (!IsTagRecord(cvt)) | |
| continue; | |
| GetOrCreateClangType(tid); | |
| } | |
| }); | |
| } | |
| void PdbAstBuilderClang::ParseAllFunctionsAndNonLocalVars() { | |
| llvm::call_once(m_parse_functions_and_non_local_vars, [this]() { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| uint32_t module_count = index.dbi().modules().getModuleCount(); | |
| for (uint16_t modi = 0; modi < module_count; ++modi) { | |
| CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(modi); | |
| const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray(); | |
| auto iter = symbols.begin(); | |
| while (iter != symbols.end()) { | |
| PdbCompilandSymId sym_id{modi, iter.offset()}; | |
| switch (iter->kind()) { | |
| case S_GPROC32: | |
| case S_LPROC32: | |
| GetOrCreateFunctionDecl(sym_id); | |
| iter = symbols.at(getScopeEndOffset(*iter)); | |
| break; | |
| case S_GDATA32: | |
| case S_GTHREAD32: | |
| case S_LDATA32: | |
| case S_LTHREAD32: | |
| GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id); | |
| ++iter; | |
| break; | |
| default: | |
| ++iter; | |
| continue; | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| static CVSymbolArray skipFunctionParameters(clang::Decl &decl, | |
| const CVSymbolArray &symbols) { | |
| clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl); | |
| if (!func_decl) | |
| return symbols; | |
| unsigned int params = func_decl->getNumParams(); | |
| if (params == 0) | |
| return symbols; | |
| CVSymbolArray result = symbols; | |
| while (!result.empty()) { | |
| if (params == 0) | |
| return result; | |
| CVSymbol sym = *result.begin(); | |
| result.drop_front(); | |
| if (!isLocalVariableType(sym.kind())) | |
| continue; | |
| --params; | |
| } | |
| return result; | |
| } | |
| void PdbAstBuilderClang::ParseBlockChildren(PdbCompilandSymId block_id) { | |
| SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( | |
| m_clang.GetSymbolFile()->GetBackingSymbolFile()); | |
| PdbIndex &index = pdb->GetIndex(); | |
| CVSymbol sym = index.ReadSymbolRecord(block_id); | |
| if (sym.kind() != S_GPROC32 && sym.kind() != S_LPROC32 && | |
| sym.kind() != S_BLOCK32 && sym.kind() != S_INLINESITE) { | |
| assert(false && "called on non-block"); | |
| return; | |
| } | |
| CompilandIndexItem &cii = | |
| index.compilands().GetOrCreateCompiland(block_id.modi); | |
| CVSymbolArray symbols = | |
| cii.m_debug_stream.getSymbolArrayForScope(block_id.offset); | |
| // Function parameters should already have been created when the function was | |
| // parsed. | |
| if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) | |
| symbols = | |
| skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols); | |
| symbols.drop_front(); | |
| auto begin = symbols.begin(); | |
| while (begin != symbols.end()) { | |
| PdbCompilandSymId child_sym_id(block_id.modi, begin.offset()); | |
| GetOrCreateSymbolForId(child_sym_id); | |
| if (begin->kind() == S_BLOCK32 || begin->kind() == S_INLINESITE) { | |
| ParseBlockChildren(child_sym_id); | |
| begin = symbols.at(getScopeEndOffset(*begin)); | |
| } | |
| ++begin; | |
| } | |
| } | |
| void PdbAstBuilderClang::ParseDeclsForSimpleContext( | |
| clang::DeclContext &context) { | |
| clang::Decl *decl = clang::Decl::castFromDeclContext(&context); | |
| if (!decl) { | |
| assert(false); | |
| return; | |
| } | |
| auto iter = m_decl_to_status.find(decl); | |
| if (iter == m_decl_to_status.end()) { | |
| assert(false && "cannot parse unknown decl"); | |
| return; | |
| } | |
| if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) { | |
| CompleteTagDecl(*tag); | |
| return; | |
| } | |
| if (isFunctionDecl(context) || isBlockDecl(context)) { | |
| PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym(); | |
| ParseBlockChildren(block_id); | |
| } | |
| } | |
| void PdbAstBuilderClang::ParseDeclsForContext(CompilerDeclContext context) { | |
| clang::DeclContext *dc = FromCompilerDeclContext(context); | |
| if (!dc) | |
| return; | |
| // Namespaces aren't explicitly represented in the debug info, and the only | |
| // way to parse them is to parse all type info, demangling every single type | |
| // and trying to reconstruct the DeclContext hierarchy this way. Since this | |
| // is an expensive operation, we have to special case it so that we do other | |
| // work (such as parsing the items that appear within the namespaces) at the | |
| // same time. | |
| if (dc->isTranslationUnit()) { | |
| ParseAllTypes(); | |
| ParseAllFunctionsAndNonLocalVars(); | |
| return; | |
| } | |
| if (dc->isNamespace()) { | |
| ParseNamespace(*dc); | |
| return; | |
| } | |
| if (isTagDecl(*dc) || isFunctionDecl(*dc) || isBlockDecl(*dc)) { | |
| ParseDeclsForSimpleContext(*dc); | |
| return; | |
| } | |
| } | |
| CompilerDecl PdbAstBuilderClang::ToCompilerDecl(clang::Decl *decl) { | |
| return m_clang.GetCompilerDecl(decl); | |
| } | |
| CompilerType PdbAstBuilderClang::ToCompilerType(clang::QualType qt) { | |
| return m_clang.GetType(qt); | |
| } | |
| clang::QualType PdbAstBuilderClang::FromCompilerType(CompilerType ct) { | |
| return ClangUtil::GetQualType(ct); | |
| } | |
| CompilerDeclContext | |
| PdbAstBuilderClang::ToCompilerDeclContext(clang::DeclContext *context) { | |
| return m_clang.CreateDeclContext(context); | |
| } | |
| clang::Decl *PdbAstBuilderClang::FromCompilerDecl(CompilerDecl decl) { | |
| if (decl.GetTypeSystem() != nullptr) | |
| return ClangUtil::GetDecl(decl); | |
| return nullptr; | |
| } | |
| clang::DeclContext * | |
| PdbAstBuilderClang::FromCompilerDeclContext(CompilerDeclContext context) { | |
| return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext()); | |
| } | |
| void PdbAstBuilderClang::Dump(Stream &stream, llvm::StringRef filter, | |
| bool show_color) { | |
| m_clang.Dump(stream.AsRawOstream(), filter, show_color); | |
| } | |
| CompilerDeclContext | |
| PdbAstBuilderClang::FindNamespaceDecl(CompilerDeclContext parent_ctx, | |
| llvm::StringRef name) { | |
| clang::DeclContext *parent = FromCompilerDeclContext(parent_ctx); | |
| NamespaceSet *set; | |
| if (parent) { | |
| auto it = m_parent_to_namespaces.find(parent); | |
| if (it == m_parent_to_namespaces.end()) | |
| return {}; | |
| set = &it->second; | |
| } else { | |
| // In this case, search through all known namespaces | |
| set = &m_known_namespaces; | |
| } | |
| assert(set); | |
| for (clang::NamespaceDecl *namespace_decl : *set) | |
| if (namespace_decl->getName() == name) | |
| return ToCompilerDeclContext(namespace_decl); | |
| for (clang::NamespaceDecl *namespace_decl : *set) | |
| if (namespace_decl->isAnonymousNamespace()) | |
| return FindNamespaceDecl(ToCompilerDeclContext(namespace_decl), name); | |
| return {}; | |
| } |