mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #14050 from ethereum/contract-ast-internal-function-ids
Annotate internal function IDs
This commit is contained in:
commit
6bc6ae94a6
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
1540
test/libsolidity/ASTJSON/ast_internal_function_id_export.json
Normal file
1540
test/libsolidity/ASTJSON/ast_internal_function_id_export.json
Normal file
File diff suppressed because it is too large
Load Diff
48
test/libsolidity/ASTJSON/ast_internal_function_id_export.sol
Normal file
48
test/libsolidity/ASTJSON/ast_internal_function_id_export.sol
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
Loading…
Reference in New Issue
Block a user