#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; for (auto &&[Name, List] : Result.Global) { if (List.size() > 1) { ProblemNameList += "`" + Name.str() + "` ("; for (const auto &Collision : List) ProblemNameList += *Collision.Name; ProblemNameList.resize(ProblemNameList.size() - 2); ProblemNameList += "), "; } } if (ProblemNameList.size() > 0) { ProblemNameList.resize(ProblemNameList.size() - 2); 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)); } 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)) { auto &Arguments = Result.Local.emplace_back(); for (auto &Argument : RFT->Arguments()) if (not Argument.Name().empty()) Arguments[Argument.Name()].emplace_back(Argument.Name(), detail::path(*RFT, Argument)); auto &RVs = Result.Local.emplace_back(); for (auto &ReturnValue : RFT->ReturnValues()) if (not ReturnValue.Name().empty()) RVs[ReturnValue.Name()].emplace_back(ReturnValue.Name(), detail::path(*RFT, ReturnValue)); } else if (auto *CFT = llvm::dyn_cast(D)) { auto &Arguments = Result.Local.emplace_back(); for (auto &Argument : CFT->Arguments()) if (not Argument.Name().empty()) Arguments[Argument.Name()].emplace_back(Argument.Name(), detail::path(*CFT, Argument)); // TODO: don't forget about local variables once those are in the model. } 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