#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // // Note that namespace collisions can only occur on invalid models (there are // exactly two use cases of it - verification and post-importer fixes), as such // there's no need to expose this logic outside of `libModel` - as it's expected // that all the dependencies work exclusively on valid models. // // If you find yourself with a model containing duplicated identifiers after // an analysis, instead of exposing this, take a look at `fix-model` pass (or // one of its components) instead. #include #include #include "revng/Model/Binary.h" #include "revng/Model/TypePathHelpers.h" namespace model { template using ConstIf = std::conditional_t; template struct NamespaceEntry { using StoredNameType = ConstIf; StoredNameType *Name; /// This is only used for improving error messages. It should be made optional /// if it ever impacts performance. std::string ModelPath; NamespaceEntry(StoredNameType &Name, std::string ModelPath) : Name(&Name), ModelPath(ModelPath) {} }; template using Namespace = std::map>>; template struct Namespaces { Namespace Global; std::vector> Local; }; /// Note that a valid model is guaranteed to not have any namespace collisions. /// As such this logic is only needed when verifying (or fixing) a model. template BinaryType> llvm::Expected>> collectNamespaces(BinaryType &Binary) { Namespaces> Result; for (ConstOrNot auto &F : Binary.ImportedDynamicFunctions()) if (not F.Name().empty()) Result.Global[F.Name()].emplace_back(F.Name(), detail::path(F)); // Dynamic functions are a bit special in that we cannot afford to change // their names no matter what. As such, if we run into any dynamic function // related collisions, we abort right away. std::string ProblemNameList; constexpr llvm::StringRef Separator = ", "; for (auto &&[Name, List] : Result.Global) { if (List.size() > 1) { ProblemNameList += "`" + Name.str() + "` ("; for (const auto &Collision : List) ProblemNameList += *Collision.Name + Separator.str(); ProblemNameList.resize(ProblemNameList.size() - Separator.size()); ProblemNameList += ")" + Separator.str(); } } if (ProblemNameList.size() > 0) { ProblemNameList.resize(ProblemNameList.size() - Separator.size()); return revng::createError("Dynamic function names must never collide (" + std::move(ProblemNameList) + ")."); } for (ConstOrNot auto &F : Binary.Functions()) if (not F.Name().empty()) Result.Global[F.Name()].emplace_back(F.Name(), detail::path(F)); for (ConstOrNot auto &S : Binary.Segments()) if (not S.Name().empty()) Result.Global[S.Name()].emplace_back(S.Name(), detail::path(S)); for (auto &Def : Binary.TypeDefinitions()) { if (not Def->Name().empty()) Result.Global[Def->Name()].emplace_back(Def->Name(), detail::path(*Def)); if (auto *Enum = llvm::dyn_cast(Def.get())) for (auto &Entry : Enum->Entries()) if (not Entry.Name().empty()) Result.Global[Entry.Name()].emplace_back(Entry.Name(), detail::path(*Enum, Entry)); } std::unordered_set ProcessedPrototypes; for (ConstOrNot auto &Function : Binary.Functions()) { auto &Variables = Result.Local.emplace_back(); for (ConstOrNot auto &Var : Function.LocalVariables()) { revng_assert(not Var.Name().empty()); Variables[Var.Name()].emplace_back(Var.Name(), detail::variablePath(Function, Var)); } for (ConstOrNot auto &Label : Function.GotoLabels()) { revng_assert(not Label.Name().empty()); Variables[Label.Name()].emplace_back(Label.Name(), detail::gotoLabelPath(Function, Label)); } if (auto *RFT = Function.rawPrototype()) { for (auto &Arg : RFT->Arguments()) if (not Arg.Name().empty()) Variables[Arg.Name()].emplace_back(Arg.Name(), detail::argumentPath(*RFT, Arg)); ProcessedPrototypes.emplace(RFT); } else if (auto *CFT = Function.cabiPrototype()) { for (auto &Arg : CFT->Arguments()) if (not Arg.Name().empty()) Variables[Arg.Name()].emplace_back(Arg.Name(), detail::argumentPath(*CFT, Arg)); ProcessedPrototypes.emplace(CFT); } else { revng_assert(Function.Prototype().isEmpty()); } } for (auto &Definition : Binary.TypeDefinitions()) { ConstOrNot auto *D = Definition.get(); if (llvm::isa(D)) { // Skip enums since all their entries are a part of the global namespace } else if (llvm::isa(D)) { // Skip typedefs since they don't spawn a new namespace } else if (auto *RFT = llvm::dyn_cast(D)) { if (not ProcessedPrototypes.contains(RFT)) { auto &Arguments = Result.Local.emplace_back(); for (auto &Arg : RFT->Arguments()) if (not Arg.Name().empty()) Arguments[Arg.Name()].emplace_back(Arg.Name(), detail::argumentPath(*RFT, Arg)); } auto &RVs = Result.Local.emplace_back(); for (auto &ReturnV : RFT->ReturnValues()) if (not ReturnV.Name().empty()) RVs[ReturnV.Name()].emplace_back(ReturnV.Name(), detail::returnValuePath(*RFT, ReturnV)); } else if (auto *CFT = llvm::dyn_cast(D)) { if (not ProcessedPrototypes.contains(CFT)) { auto &Arguments = Result.Local.emplace_back(); for (auto &Arg : CFT->Arguments()) if (not Arg.Name().empty()) Arguments[Arg.Name()].emplace_back(Arg.Name(), detail::argumentPath(*CFT, Arg)); } } else if (auto *S = llvm::dyn_cast(D)) { auto &Fields = Result.Local.emplace_back(); for (auto &Field : S->Fields()) if (not Field.Name().empty()) Fields[Field.Name()].emplace_back(Field.Name(), detail::path(*S, Field)); } else if (auto *U = llvm::dyn_cast(D)) { auto &Fields = Result.Local.emplace_back(); for (auto &Field : U->Fields()) if (not Field.Name().empty()) Fields[Field.Name()].emplace_back(Field.Name(), detail::path(*U, Field)); } else { revng_abort("Unsupported type definition kind."); } } return Result; } } // namespace model