mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
parent
e7ec40b1af
commit
a0e62bbd3d
@ -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:
|
||||
|
@ -178,6 +178,7 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation
|
||||
|
||||
struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
{
|
||||
util::SetOnce<uint64_t> internalFunctionID;
|
||||
};
|
||||
|
||||
struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
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