Files
revng-revng/lib/Model/NameBuilder.cpp
T
2024-11-06 15:43:13 +02:00

443 lines
15 KiB
C++

/// \file NameBuilder.cpp
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "revng/Model/Binary.h"
const model::NamingConfiguration &model::NameBuilder::configuration() const {
return Binary.Configuration().Naming();
}
static llvm::Error makeDuplicateSymbolError(const std::string &Name,
const std::string &FirstPath,
const std::string &SecondPath) {
std::string Result;
Result += "Duplicate global symbol \"";
Result += Name;
Result += "\":\n\n";
Result += " " + FirstPath + "\n";
Result += " " + SecondPath + "\n";
return revng::createError(std::move(Result));
}
template<typename T>
static std::string key(const T &Object) {
return getNameFromYAMLScalar(KeyedObjectTraits<T>::key(Object));
}
static std::string path(const model::Function &F) {
return "/Functions/" + key(F);
}
static std::string path(const model::DynamicFunction &F) {
return "/ImportedDynamicFunctions/" + key(F);
}
static std::string path(const model::TypeDefinition &T) {
return "/TypeDefinitions/" + key(T);
}
static std::string path(const model::EnumDefinition &D,
const model::EnumEntry &Entry) {
return path(static_cast<const model::TypeDefinition &>(D))
+ "/EnumDefinition/Entries/" + key(Entry);
}
static std::string path(const model::Segment &Segment) {
return "/Segments/" + key(Segment);
}
llvm::Error model::NameBuilder::isNameForbidden(llvm::StringRef Name) {
// Do these strictly for now, then soften them if needed.
if (Name.starts_with(configuration().structPaddingPrefix()))
return revng::createError("it is reserved for struct padding");
if (Name.starts_with(configuration().artificialReturnValuePrefix()))
return revng::createError("it is reserved for artificial return value "
"structs");
if (Name.starts_with(configuration().artificialArrayWrapperPrefix()))
return revng::createError("it is reserved for artificial array wrappers");
// TODO: more of these?
return llvm::Error::success();
}
llvm::Error model::NameBuilder::populateGlobalNamespace() {
revng_assert(not GlobalNamespace.has_value());
GlobalNamespace = GlobalNamespaceMap();
auto RegisterGlobalSymbol = [this](std::string Name,
std::string Path) -> llvm::Error {
if (not Name.empty()) {
if (llvm::Error Error = isNameForbidden(Name)) {
struct ExtractMessage {
std::string &Out;
llvm::Error operator()(const llvm::StringError &Error) {
Out = Error.getMessage();
return llvm::Error::success();
}
};
auto CatchAll = [](const llvm::ErrorInfoBase &) -> llvm::Error {
revng_abort("Unsupported error type.");
};
std::string Out;
llvm::handleAllErrors(std::move(Error),
ExtractMessage{ Out },
CatchAll);
revng_assert(not Out.empty());
return revng::createError("The name \"" + Name + "\" of \"" + Path
+ "\" is not allowed: " + std::move(Out)
+ ".\n");
}
auto [Iterator, Success] = GlobalNamespace->try_emplace(Name, Path);
if (not Success)
return makeDuplicateSymbolError(Name, Iterator->second, Path);
}
return llvm::Error::success();
};
// Namespacing rules:
//
// 1. each struct/union induces a namespace for its field names;
// 2. each prototype induces a namespace for its arguments (and local
// variables, but those are not part of the model yet);
// 3. the global namespace includes segment names, function names, dynamic
// function names, type names, entries of `enum`s and helper names;
//
// Verify needs to verify that each namespace has no internal clashes.
// Also, the global namespace clashes with everything.
//
// TODO: find a way to add all the helper names to the Result.
for (const Function &F : Binary.Functions())
if (auto Error = RegisterGlobalSymbol(F.CustomName().str().str(), path(F)))
return Error;
for (const DynamicFunction &DF : Binary.ImportedDynamicFunctions())
if (auto Error = RegisterGlobalSymbol(DF.CustomName().str().str(),
path(DF)))
return Error;
for (const model::UpcastableTypeDefinition &D : Binary.TypeDefinitions()) {
if (auto Error = RegisterGlobalSymbol(D->CustomName().str().str(),
path(*D)))
return Error;
if (auto *Enum = llvm::dyn_cast<model::EnumDefinition>(D.get()))
for (auto &Entry : Enum->Entries())
if (auto Error = RegisterGlobalSymbol(Entry.CustomName().str().str(),
path(*Enum, Entry)))
return Error;
}
for (const Segment &S : Binary.Segments())
if (auto Error = RegisterGlobalSymbol(S.CustomName().str().str(), path(S)))
return Error;
return llvm::Error::success();
}
static std::string toIdentifier(const MetaAddress &Address) {
return model::Identifier::sanitize(Address.toString()).str().str();
}
model::Identifier model::NameBuilder::name(const model::Segment &Segment) {
if (not Segment.CustomName().empty()) {
revng_assert(globalNamespace().contains(Segment.CustomName()),
"Cache is outdated.");
return Segment.CustomName();
} else {
auto Result = std::string(configuration().unnamedSegmentPrefix())
+ toIdentifier(Segment.StartAddress()) + "_"
+ std::to_string(Segment.VirtualSize());
while (globalNamespace().contains(Result))
Result += configuration().collisionResolutionSuffix();
return Identifier(Result);
}
}
model::Identifier model::NameBuilder::name(const model::Function &Function) {
if (not Function.CustomName().empty()) {
revng_assert(globalNamespace().contains(Function.CustomName()),
"Cache is outdated.");
return Function.CustomName();
} else {
auto Result = std::string(configuration().unnamedFunctionPrefix())
+ toIdentifier(Function.Entry());
while (globalNamespace().contains(Result))
Result += configuration().collisionResolutionSuffix();
return Identifier(Result);
}
}
model::Identifier
model::NameBuilder::name(const model::DynamicFunction &Function) {
if (not Function.CustomName().empty()) {
revng_assert(globalNamespace().contains(Function.CustomName()),
"Cache is outdated.");
return Function.CustomName();
} else {
auto Result = std::string(configuration().unnamedDynamicFunctionPrefix())
+ Function.OriginalName();
while (globalNamespace().contains(Result))
Result += configuration().collisionResolutionSuffix();
return Identifier(Result);
}
}
model::Identifier
model::NameBuilder::name(const model::TypeDefinition &Definition) {
if (not Definition.CustomName().empty()) {
revng_assert(globalNamespace().contains(Definition.CustomName()),
"Cache is outdated.");
return Definition.CustomName();
} else {
auto K = model::TypeDefinitionKind::automaticNamePrefix(Definition.Kind());
auto Result = std::string(configuration().unnamedTypeDefinitionPrefix())
+ K.str() + std::to_string(Definition.ID());
while (globalNamespace().contains(Result))
Result += configuration().collisionResolutionSuffix();
return Identifier(Result);
}
}
model::Identifier
model::NameBuilder::name(const model::EnumDefinition &Definition,
const model::EnumEntry &Entry) {
revng_assert(Definition.Entries().count(Entry.Value()) != 0);
model::Identifier Result;
// Decide on a name
if (Entry.CustomName().empty()) {
llvm::StringRef Prefix{ configuration().unnamedEnumEntryPrefix() };
(Prefix + name(Definition) + llvm::Twine(Entry.Value())).toVector(Result);
} else {
Result = Entry.CustomName();
revng_assert(globalNamespace().contains(Result), "Cache is outdated.");
return Result;
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &Entry, &Definition](const auto &Name) {
for (const auto &AnotherEntry : Definition.Entries())
if (Entry.key() != AnotherEntry.key())
if (not AnotherEntry.CustomName().empty())
if (AnotherEntry.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
model::Identifier
model::NameBuilder::name(const model::StructDefinition &Definition,
const model::StructField &Field) {
model::Identifier Result;
// Decide on a name
if (Field.CustomName().empty()) {
llvm::StringRef Prefix{ configuration().unnamedStructFieldPrefix() };
(Prefix + llvm::Twine(Field.Offset())).toVector(Result);
} else {
Result = Field.CustomName();
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &Field, &Definition](const auto &Name) {
for (const auto &AnotherField : Definition.Fields())
if (Field.key() != AnotherField.key())
if (not AnotherField.CustomName().empty())
if (AnotherField.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
model::Identifier
model::NameBuilder::name(const model::UnionDefinition &Definition,
const model::UnionField &Field) {
model::Identifier Result;
// Decide on a name
if (Field.CustomName().empty()) {
llvm::StringRef Prefix{ configuration().unnamedUnionFieldPrefix() };
(Prefix + llvm::Twine(Field.Index())).toVector(Result);
} else {
Result = Field.CustomName();
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &Field, &Definition](const auto &Name) {
for (const auto &AnotherField : Definition.Fields())
if (Field.key() != AnotherField.key())
if (not AnotherField.CustomName().empty())
if (AnotherField.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
using CFT = model::CABIFunctionDefinition;
model::Identifier
model::NameBuilder::argumentName(const CFT &Function,
const model::Argument &Argument) {
model::Identifier Result;
// Decide on a name
if (Argument.CustomName().empty()) {
llvm::StringRef Prefix{ configuration().unnamedFunctionArgumentPrefix() };
(Prefix + llvm::Twine(Argument.Index())).toVector(Result);
} else {
Result = Argument.CustomName();
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &Argument, &Function](const auto &Name) {
for (const auto &AnotherArgument : Function.Arguments())
if (Argument.key() != AnotherArgument.key())
if (not AnotherArgument.CustomName().empty())
if (AnotherArgument.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
using NTR = model::NamedTypedRegister;
using RFT = model::RawFunctionDefinition;
model::Identifier model::NameBuilder::argumentName(const RFT &Function,
const NTR &Argument) {
model::Identifier Result;
// Decide on a name
if (Argument.CustomName().empty()) {
llvm::StringRef Prefix = configuration().unnamedFunctionRegisterPrefix();
(Prefix + getRegisterName(Argument.Location())).toVector(Result);
} else {
Result = Argument.CustomName();
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &Argument, &Function](const auto &Name) {
for (const auto &AnotherArgument : Function.Arguments())
if (Argument.key() != AnotherArgument.key())
if (not AnotherArgument.CustomName().empty())
if (AnotherArgument.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
using NTR = model::NamedTypedRegister;
using RFT = model::RawFunctionDefinition;
model::Identifier model::NameBuilder::returnValueName(const RFT &Function,
const NTR &ReturnValue) {
model::Identifier Result;
// Decide on a name
if (ReturnValue.CustomName().empty()) {
llvm::StringRef Prefix = configuration().unnamedFunctionRegisterPrefix();
(Prefix + getRegisterName(ReturnValue.Location())).toVector(Result);
} else {
Result = ReturnValue.CustomName();
}
// Ensure it doesn't collide with anything
// TODO: factor these out?
auto NameCollides = [this, &ReturnValue, &Function](const auto &Name) {
for (const auto &AnotherReturnValue : Function.ReturnValues())
if (ReturnValue.key() != AnotherReturnValue.key())
if (not AnotherReturnValue.CustomName().empty())
if (AnotherReturnValue.CustomName() == Name)
return true;
return globalNamespace().contains(Name);
};
while (NameCollides(Result))
Result += configuration().collisionResolutionSuffix();
return Result;
}
using T = model::Type;
RecursiveCoroutine<std::string>
model::NameBuilder::artificialArrayWrapperNameImpl(const T &Type) {
if (auto *Array = llvm::dyn_cast<model::ArrayType>(&Type)) {
std::string Result = "array_" + std::to_string(Array->ElementCount())
+ "_of_";
Result += rc_recur artificialArrayWrapperNameImpl(*Array->ElementType());
rc_return Result;
} else if (auto *D = llvm::dyn_cast<model::DefinedType>(&Type)) {
std::string Result = (D->IsConst() ? "const_" : "");
rc_return std::move(Result += this->name(D->unwrap()).str().str());
} else if (auto *Pointer = llvm::dyn_cast<model::PointerType>(&Type)) {
std::string Result = (D->IsConst() ? "const_ptr_to_" : "ptr_to_");
Result += rc_recur artificialArrayWrapperNameImpl(*Pointer->PointeeType());
rc_return std::move(Result);
} else if (auto *Primitive = llvm::dyn_cast<model::PrimitiveType>(&Type)) {
std::string Result = (D->IsConst() ? "const_" : "");
rc_return std::move(Result += Primitive->getCName());
} else {
revng_abort("Unsupported model::Type.");
}
}
model::Identifier model::NameBuilder::artificialArrayWrapperFieldName() {
std::string Result{ configuration().artificialArrayWrapperFieldName() };
while (globalNamespace().contains(Result))
Result += configuration().collisionResolutionSuffix();
return Identifier(Result);
}