Annotate function ID of functions that may be called via the internal dispatch.

Co-authored-by: Daniel <daniel@ekpyron.org>
This commit is contained in:
Rodrigo Q. Saramago 2023-03-15 14:38:03 +01:00
parent e7ec40b1af
commit a0e62bbd3d
No known key found for this signature in database
GPG Key ID: 9B36B2525704A359
11 changed files with 1631 additions and 27 deletions

View File

@ -12,6 +12,10 @@ Compiler Features:
Bugfixes:
AST Changes:
* AST: add the ``internalFunctionID`` field to the AST nodes of functions that may be called via the internal dispatch. These IDs are always generated, but they are only used in via-IR code generation.
### 0.8.19 (2023-02-22)
Language Features:

View File

@ -178,6 +178,7 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation
struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
util::SetOnce<uint64_t> internalFunctionID;
};
struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation

View File

@ -468,6 +468,10 @@ bool ASTJsonExporter::visit(FunctionDefinition const& _node)
attributes.emplace_back("functionSelector", _node.externalIdentifierHex());
if (!_node.annotation().baseFunctions.empty())
attributes.emplace_back(make_pair("baseFunctions", getContainerIds(_node.annotation().baseFunctions, true)));
if (_node.annotation().internalFunctionID.set())
attributes.emplace_back("internalFunctionID", *_node.annotation().internalFunctionID);
setJsonNode(_node, "FunctionDefinition", std::move(attributes));
return false;
}

View File

@ -177,17 +177,3 @@ ABIFunctions IRGenerationContext::abiFunctions()
{
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
}
uint64_t IRGenerationContext::internalFunctionID(FunctionDefinition const& _function, bool _requirePresent)
{
auto [iterator, inserted] = m_functionIDs.try_emplace(_function.id(), m_functionIDs.size() + 1);
if (_requirePresent)
solAssert(!inserted, "");
return iterator->second;
}
void IRGenerationContext::copyFunctionIDsFrom(IRGenerationContext const& _other)
{
solAssert(m_functionIDs.empty(), "");
m_functionIDs = _other.m_functionIDs;
}

View File

@ -163,14 +163,6 @@ public:
bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; }
/// @returns the runtime ID to be used for the function in the dispatch routine
/// and for internal function pointers.
/// @param _requirePresent if false, generates a new ID if not yet done.
uint64_t internalFunctionID(FunctionDefinition const& _function, bool _requirePresent);
/// Copies the internal function IDs from the @a _other. For use in transferring
/// function IDs from constructor code to deployed code.
void copyFunctionIDsFrom(IRGenerationContext const& _other);
std::map<std::string, unsigned> const& sourceIndices() const { return m_sourceIndices; }
void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); }
std::set<std::string> const& usedSourceNames() const { return m_usedSourceNames; }
@ -219,8 +211,6 @@ private:
/// the code contains a call via a pointer even though a specific function is never assigned to it.
/// It will fail at runtime but the code must still compile.
InternalDispatchMap m_internalDispatchMap;
/// Map used by @a internalFunctionID.
std::map<int64_t, uint64_t> m_functionIDs;
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;

View File

@ -313,7 +313,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin
solAssert(m_context.functionCollector().contains(IRNames::function(*function)), "");
cases.emplace_back(map<string, string>{
{"funID", to_string(m_context.internalFunctionID(*function, true))},
{"funID", to_string(*function->annotation().internalFunctionID)},
{"name", IRNames::function(*function)}
});
}
@ -1117,7 +1117,6 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon
m_context.debugInfoSelection(),
m_context.soliditySourceProvider()
);
newContext.copyFunctionIDsFrom(m_context);
m_context = std::move(newContext);
m_context.setMostDerivedContract(_contract);

View File

@ -2807,7 +2807,7 @@ void IRGeneratorForStatements::assignInternalFunctionIDIfNotCalledDirectly(
return;
define(IRVariable(_expression).part("functionIdentifier")) <<
to_string(m_context.internalFunctionID(_referencedFunction, false)) <<
to_string(*_referencedFunction.annotation().internalFunctionID) <<
"\n";
m_context.addToInternalDispatch(_referencedFunction);
}

View File

@ -526,6 +526,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
createAndAssignCallGraphs();
annotateInternalFunctionIDs();
findAndReportCyclicContractDependencies();
}
@ -1245,6 +1246,34 @@ void CompilerStack::storeContractDefinitions()
}
}
void CompilerStack::annotateInternalFunctionIDs()
{
uint64_t internalFunctionID = 1;
for (Source const* source: m_sourceOrder)
{
if (!source->ast)
continue;
for (ContractDefinition const* contract: ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes()))
{
ContractDefinitionAnnotation& annotation = contract->annotation();
if (auto const* deployTimeInternalDispatch = util::valueOrNullptr((*annotation.deployedCallGraph)->edges, CallGraph::SpecialNode::InternalDispatch))
for (auto const& node: *deployTimeInternalDispatch)
if (auto const* callable = get_if<CallableDeclaration const*>(&node))
if (auto const* function = dynamic_cast<FunctionDefinition const*>(*callable))
if (!function->annotation().internalFunctionID.set())
function->annotation().internalFunctionID = internalFunctionID++;
if (auto const* creationTimeInternalDispatch = util::valueOrNullptr((*annotation.creationCallGraph)->edges, CallGraph::SpecialNode::InternalDispatch))
for (auto const& node: *creationTimeInternalDispatch)
if (auto const* callable = get_if<CallableDeclaration const*>(&node))
if (auto const* function = dynamic_cast<FunctionDefinition const*>(*callable))
// Make sure the function already got an ID since it also occurs in the deploy-time internal dispatch.
solAssert(function->annotation().internalFunctionID.set());
}
}
}
namespace
{
bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)

View File

@ -415,6 +415,9 @@ private:
/// Store the contract definitions in m_contracts.
void storeContractDefinitions();
/// Annotate internal dispatch function Ids
void annotateInternalFunctionIDs();
/// @returns true if the source is requested to be compiled.
bool isRequestedSource(std::string const& _sourceName) const;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
function free1() {}
function free2() {}
function free3() {}
library L {
function ext() external {}
function inr1() internal {}
function inr2() internal {}
function inr3() internal {}
function access() public {
free1;
inr1;
L.ext;
}
function expression() public {
(free2)();
(inr2)();
}
}
contract C {
function ext1() external {}
function ext2() external {}
function ext3() external {}
function inr1() internal {}
function inr2() internal {}
function inr3() internal {}
function access() public {
this.ext1;
inr1;
free1;
L.inr1;
L.ext;
}
function expression() public {
(this.ext2)();
(inr2)();
(free2)();
(L.inr2)();
(L.ext)();
}
}
contract D is C {
constructor() {
access();
expression();
}
}
// ----