mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
443 lines
15 KiB
C++
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);
|
|
}
|