Merge pull request #14050 from ethereum/contract-ast-internal-function-ids

Annotate internal function IDs
This commit is contained in:
Daniel 2023-04-12 14:11:39 +02:00 committed by GitHub
commit 6bc6ae94a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1631 additions and 27 deletions

View File

@ -18,6 +18,10 @@ Bugfixes:
* Antlr Grammar: Stricter rules for function definitions. The grammar will no longer accept as valid free functions having specifiers which are exclusive to contract functions. * Antlr Grammar: Stricter rules for function definitions. The grammar will no longer accept as valid free functions having specifiers which are exclusive to contract functions.
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) ### 0.8.19 (2023-02-22)
Language Features: Language Features:

View File

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

View File

@ -469,6 +469,10 @@ bool ASTJsonExporter::visit(FunctionDefinition const& _node)
attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); attributes.emplace_back("functionSelector", _node.externalIdentifierHex());
if (!_node.annotation().baseFunctions.empty()) if (!_node.annotation().baseFunctions.empty())
attributes.emplace_back(make_pair("baseFunctions", getContainerIds(_node.annotation().baseFunctions, true))); 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)); setJsonNode(_node, "FunctionDefinition", std::move(attributes));
return false; return false;
} }

View File

@ -177,17 +177,3 @@ ABIFunctions IRGenerationContext::abiFunctions()
{ {
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions); 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; } bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; } 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; } std::map<std::string, unsigned> const& sourceIndices() const { return m_sourceIndices; }
void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); } void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); }
std::set<std::string> const& usedSourceNames() const { return m_usedSourceNames; } 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. /// 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. /// It will fail at runtime but the code must still compile.
InternalDispatchMap m_internalDispatchMap; InternalDispatchMap m_internalDispatchMap;
/// Map used by @a internalFunctionID.
std::map<int64_t, uint64_t> m_functionIDs;
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects; 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)), ""); solAssert(m_context.functionCollector().contains(IRNames::function(*function)), "");
cases.emplace_back(map<string, string>{ cases.emplace_back(map<string, string>{
{"funID", to_string(m_context.internalFunctionID(*function, true))}, {"funID", to_string(*function->annotation().internalFunctionID)},
{"name", IRNames::function(*function)} {"name", IRNames::function(*function)}
}); });
} }
@ -1117,7 +1117,6 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon
m_context.debugInfoSelection(), m_context.debugInfoSelection(),
m_context.soliditySourceProvider() m_context.soliditySourceProvider()
); );
newContext.copyFunctionIDsFrom(m_context);
m_context = std::move(newContext); m_context = std::move(newContext);
m_context.setMostDerivedContract(_contract); m_context.setMostDerivedContract(_contract);

View File

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

View File

@ -526,6 +526,7 @@ bool CompilerStack::analyze()
if (noErrors) if (noErrors)
{ {
createAndAssignCallGraphs(); createAndAssignCallGraphs();
annotateInternalFunctionIDs();
findAndReportCyclicContractDependencies(); 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 namespace
{ {
bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features) bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features)

View File

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