mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Generate internal dispatch only for functions that might actually get called via pointers
- This also adds support for internal library calls as a side-effect since they'll now be pulled into the internal dispatch automatically.
This commit is contained in:
parent
b7aa6cb1f7
commit
1a2e441bc5
@ -29,6 +29,8 @@
|
|||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
#include <libsolutil/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
@ -121,49 +123,55 @@ string IRGenerationContext::newYulVariable()
|
|||||||
return "_" + to_string(++m_varCounter);
|
return "_" + to_string(++m_varCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::generateInternalDispatchFunction(YulArity const& _arity)
|
void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _internalDispatch)
|
||||||
{
|
{
|
||||||
string funName = IRNames::internalDispatch(_arity);
|
solAssert(internalDispatchClean(), "");
|
||||||
return m_functions.createFunction(funName, [&]() {
|
|
||||||
Whiskers templ(R"(
|
|
||||||
function <functionName>(fun<?+in>, <in></+in>) <?+out>-> <out></+out> {
|
|
||||||
switch fun
|
|
||||||
<#cases>
|
|
||||||
case <funID>
|
|
||||||
{
|
|
||||||
<?+out> <out> :=</+out> <name>(<in>)
|
|
||||||
}
|
|
||||||
</cases>
|
|
||||||
default { invalid() }
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
templ("functionName", funName);
|
|
||||||
templ("in", suffixedVariableNameList("in_", 0, _arity.in));
|
|
||||||
templ("out", suffixedVariableNameList("out_", 0, _arity.out));
|
|
||||||
|
|
||||||
vector<map<string, string>> cases;
|
|
||||||
for (FunctionDefinition const* function: collectFunctionsOfArity(_arity))
|
|
||||||
{
|
|
||||||
solAssert(function, "");
|
|
||||||
solAssert(
|
|
||||||
YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == _arity,
|
|
||||||
"A single dispatch function can only handle functions of one arity"
|
|
||||||
);
|
|
||||||
solAssert(!function->isConstructor(), "");
|
|
||||||
// 0 is reserved for uninitialized function pointers
|
|
||||||
solAssert(function->id() != 0, "Unexpected function ID: 0");
|
|
||||||
|
|
||||||
cases.emplace_back(map<string, string>{
|
|
||||||
{"funID", to_string(function->id())},
|
|
||||||
{"name", IRNames::function(*function)}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
for (set<FunctionDefinition const*> const& functions: _internalDispatch | boost::adaptors::map_values)
|
||||||
|
for (auto function: functions)
|
||||||
enqueueFunctionForCodeGeneration(*function);
|
enqueueFunctionForCodeGeneration(*function);
|
||||||
}
|
|
||||||
|
|
||||||
templ("cases", move(cases));
|
m_internalDispatchMap = move(_internalDispatch);
|
||||||
return templ.render();
|
}
|
||||||
});
|
|
||||||
|
InternalDispatchMap IRGenerationContext::consumeInternalDispatchMap()
|
||||||
|
{
|
||||||
|
m_directInternalFunctionCalls.clear();
|
||||||
|
|
||||||
|
InternalDispatchMap internalDispatch = move(m_internalDispatchMap);
|
||||||
|
m_internalDispatchMap.clear();
|
||||||
|
return internalDispatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerationContext::internalFunctionCalledDirectly(Expression const& _expression)
|
||||||
|
{
|
||||||
|
solAssert(m_directInternalFunctionCalls.count(&_expression) == 0, "");
|
||||||
|
|
||||||
|
m_directInternalFunctionCalls.insert(&_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerationContext::internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
IRHelpers::referencedFunctionDeclaration(_expression) &&
|
||||||
|
_function.resolveVirtual(mostDerivedContract()) ==
|
||||||
|
IRHelpers::referencedFunctionDeclaration(_expression)->resolveVirtual(mostDerivedContract()),
|
||||||
|
"Function definition does not match the expression"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (m_directInternalFunctionCalls.count(&_expression) == 0)
|
||||||
|
{
|
||||||
|
FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal);
|
||||||
|
solAssert(functionType, "");
|
||||||
|
|
||||||
|
m_internalDispatchMap[YulArity::fromType(*functionType)].insert(&_function);
|
||||||
|
enqueueFunctionForCodeGeneration(_function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerationContext::internalFunctionCalledThroughDispatch(YulArity const& _arity)
|
||||||
|
{
|
||||||
|
m_internalDispatchMap.try_emplace(_arity);
|
||||||
}
|
}
|
||||||
|
|
||||||
YulUtilFunctions IRGenerationContext::utils()
|
YulUtilFunctions IRGenerationContext::utils()
|
||||||
@ -180,21 +188,3 @@ std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message
|
|||||||
{
|
{
|
||||||
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<FunctionDefinition const*> IRGenerationContext::collectFunctionsOfArity(YulArity const& _arity)
|
|
||||||
{
|
|
||||||
// UNIMPLEMENTED: Internal library calls via pointers are not implemented yet.
|
|
||||||
// We're not returning any internal library functions here even though it's possible
|
|
||||||
// to call them via pointers. Right now such calls end will up triggering the `default` case in
|
|
||||||
// the switch in the generated dispatch function.
|
|
||||||
set<FunctionDefinition const*> functions;
|
|
||||||
for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts)
|
|
||||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
|
||||||
if (
|
|
||||||
!function->isConstructor() &&
|
|
||||||
YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == _arity
|
|
||||||
)
|
|
||||||
functions.insert(function);
|
|
||||||
|
|
||||||
return functions;
|
|
||||||
}
|
|
||||||
|
@ -43,6 +43,8 @@ namespace solidity::frontend
|
|||||||
class YulUtilFunctions;
|
class YulUtilFunctions;
|
||||||
class ABIFunctions;
|
class ABIFunctions;
|
||||||
|
|
||||||
|
using InternalDispatchMap = std::map<YulArity, std::set<FunctionDefinition const*>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that contains contextual information during IR generation.
|
* Class that contains contextual information during IR generation.
|
||||||
*/
|
*/
|
||||||
@ -102,7 +104,26 @@ public:
|
|||||||
|
|
||||||
std::string newYulVariable();
|
std::string newYulVariable();
|
||||||
|
|
||||||
std::string generateInternalDispatchFunction(YulArity const& _arity);
|
void initializeInternalDispatch(InternalDispatchMap _internalDispatchMap);
|
||||||
|
InternalDispatchMap consumeInternalDispatchMap();
|
||||||
|
bool internalDispatchClean() const { return m_internalDispatchMap.empty() && m_directInternalFunctionCalls.empty(); }
|
||||||
|
|
||||||
|
/// Notifies the context that a function call that needs to go through internal dispatch was
|
||||||
|
/// encountered while visiting the AST. This ensures that the corresponding dispatch function
|
||||||
|
/// gets added to the dispatch map even if there are no entries in it (which may happen if
|
||||||
|
/// the code contains a call to an uninitialized function variable).
|
||||||
|
void internalFunctionCalledThroughDispatch(YulArity const& _arity);
|
||||||
|
|
||||||
|
/// Notifies the context that a direct function call (i.e. not through internal dispatch) was
|
||||||
|
/// encountered while visiting the AST. This lets the context know that the function should
|
||||||
|
/// not be added to the dispatch (unless there are also indirect calls to it elsewhere else).
|
||||||
|
void internalFunctionCalledDirectly(Expression const& _expression);
|
||||||
|
|
||||||
|
/// Notifies the context that a name representing an internal function has been found while
|
||||||
|
/// visiting the AST. If the name has not been reported as a direct call using
|
||||||
|
/// @a internalFunctionCalledDirectly(), it's assumed to represent function variable access
|
||||||
|
/// and the function gets added to internal dispatch.
|
||||||
|
void internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function);
|
||||||
|
|
||||||
/// @returns a new copy of the utility function generator (but using the same function set).
|
/// @returns a new copy of the utility function generator (but using the same function set).
|
||||||
YulUtilFunctions utils();
|
YulUtilFunctions utils();
|
||||||
@ -120,8 +141,6 @@ public:
|
|||||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<FunctionDefinition const*> collectFunctionsOfArity(YulArity const& _arity);
|
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
OptimiserSettings m_optimiserSettings;
|
OptimiserSettings m_optimiserSettings;
|
||||||
@ -147,6 +166,13 @@ private:
|
|||||||
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
|
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
|
||||||
std::set<FunctionDefinition const*> m_functionGenerationQueue;
|
std::set<FunctionDefinition const*> m_functionGenerationQueue;
|
||||||
|
|
||||||
|
/// Collection of functions that need to be callable via internal dispatch.
|
||||||
|
/// Note that having a key with an empty set of functions is a valid situation. It means that
|
||||||
|
/// 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;
|
||||||
|
std::set<Expression const*> m_directInternalFunctionCalls;
|
||||||
|
|
||||||
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
|
|
||||||
#include <liblangutil/SourceReferenceFormatter.h>
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -137,14 +139,22 @@ string IRGenerator::generate(
|
|||||||
t("deploy", deployCode(_contract));
|
t("deploy", deployCode(_contract));
|
||||||
generateImplicitConstructors(_contract);
|
generateImplicitConstructors(_contract);
|
||||||
generateQueuedFunctions();
|
generateQueuedFunctions();
|
||||||
|
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
||||||
t("functions", m_context.functionCollector().requestedFunctions());
|
t("functions", m_context.functionCollector().requestedFunctions());
|
||||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
|
|
||||||
resetContext(_contract);
|
resetContext(_contract);
|
||||||
|
|
||||||
|
// NOTE: Function pointers can be passed from creation code via storage variables. We need to
|
||||||
|
// get all the functions they could point to into the dispatch functions even if they're never
|
||||||
|
// referenced by name in the runtime code.
|
||||||
|
m_context.initializeInternalDispatch(move(internalDispatchMap));
|
||||||
|
|
||||||
// Do not register immutables to avoid assignment.
|
// Do not register immutables to avoid assignment.
|
||||||
t("RuntimeObject", IRNames::runtimeObject(_contract));
|
t("RuntimeObject", IRNames::runtimeObject(_contract));
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
generateQueuedFunctions();
|
generateQueuedFunctions();
|
||||||
|
generateInternalDispatchFunctions();
|
||||||
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
||||||
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
return t.render();
|
return t.render();
|
||||||
@ -164,6 +174,68 @@ void IRGenerator::generateQueuedFunctions()
|
|||||||
generateFunction(*m_context.dequeueFunctionForCodeGeneration());
|
generateFunction(*m_context.dequeueFunctionForCodeGeneration());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
m_context.functionGenerationQueueEmpty(),
|
||||||
|
"At this point all the enqueued functions should have been generated. "
|
||||||
|
"Otherwise the dispatch may be incomplete."
|
||||||
|
);
|
||||||
|
|
||||||
|
InternalDispatchMap internalDispatchMap = m_context.consumeInternalDispatchMap();
|
||||||
|
for (YulArity const& arity: internalDispatchMap | boost::adaptors::map_keys)
|
||||||
|
{
|
||||||
|
string funName = IRNames::internalDispatch(arity);
|
||||||
|
m_context.functionCollector().createFunction(funName, [&]() {
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>(fun<?+in>, <in></+in>) <?+out>-> <out></+out> {
|
||||||
|
switch fun
|
||||||
|
<#cases>
|
||||||
|
case <funID>
|
||||||
|
{
|
||||||
|
<?+out> <out> :=</+out> <name>(<in>)
|
||||||
|
}
|
||||||
|
</cases>
|
||||||
|
default { invalid() }
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", funName);
|
||||||
|
templ("in", suffixedVariableNameList("in_", 0, arity.in));
|
||||||
|
templ("out", suffixedVariableNameList("out_", 0, arity.out));
|
||||||
|
|
||||||
|
vector<map<string, string>> cases;
|
||||||
|
for (FunctionDefinition const* function: internalDispatchMap.at(arity))
|
||||||
|
{
|
||||||
|
solAssert(function, "");
|
||||||
|
solAssert(
|
||||||
|
YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == arity,
|
||||||
|
"A single dispatch function can only handle functions of one arity"
|
||||||
|
);
|
||||||
|
solAssert(!function->isConstructor(), "");
|
||||||
|
// 0 is reserved for uninitialized function pointers
|
||||||
|
solAssert(function->id() != 0, "Unexpected function ID: 0");
|
||||||
|
solAssert(m_context.functionCollector().contains(IRNames::function(*function)), "");
|
||||||
|
|
||||||
|
cases.emplace_back(map<string, string>{
|
||||||
|
{"funID", to_string(function->id())},
|
||||||
|
{"name", IRNames::function(*function)}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
templ("cases", move(cases));
|
||||||
|
return templ.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
solAssert(m_context.internalDispatchClean(), "");
|
||||||
|
solAssert(
|
||||||
|
m_context.functionGenerationQueueEmpty(),
|
||||||
|
"Internal dispatch generation must not add new functions to generation queue because they won't be proeessed."
|
||||||
|
);
|
||||||
|
|
||||||
|
return internalDispatchMap;
|
||||||
|
}
|
||||||
|
|
||||||
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
string functionName = IRNames::function(_function);
|
string functionName = IRNames::function(_function);
|
||||||
@ -556,6 +628,10 @@ void IRGenerator::resetContext(ContractDefinition const& _contract)
|
|||||||
m_context.functionCollector().requestedFunctions().empty(),
|
m_context.functionCollector().requestedFunctions().empty(),
|
||||||
"Reset context while it still had functions."
|
"Reset context while it still had functions."
|
||||||
);
|
);
|
||||||
|
solAssert(
|
||||||
|
m_context.internalDispatchClean(),
|
||||||
|
"Reset internal dispatch map without consuming it."
|
||||||
|
);
|
||||||
m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings);
|
m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings);
|
||||||
|
|
||||||
m_context.setMostDerivedContract(_contract);
|
m_context.setMostDerivedContract(_contract);
|
||||||
|
@ -65,6 +65,11 @@ private:
|
|||||||
/// Generates code for all the functions from the function generation queue.
|
/// Generates code for all the functions from the function generation queue.
|
||||||
/// The resulting code is stored in the function collector in IRGenerationContext.
|
/// The resulting code is stored in the function collector in IRGenerationContext.
|
||||||
void generateQueuedFunctions();
|
void generateQueuedFunctions();
|
||||||
|
/// Generates all the internal dispatch functions necessary to handle any function that could
|
||||||
|
/// possibly be called via a pointer.
|
||||||
|
/// @return The content of the dispatch for reuse in runtime code. Reuse is necessary because
|
||||||
|
/// pointers to functions can be passed from the creation code in storage variables.
|
||||||
|
InternalDispatchMap generateInternalDispatchFunctions();
|
||||||
/// Generates code for and returns the name of the function.
|
/// Generates code for and returns the name of the function.
|
||||||
std::string generateFunction(FunctionDefinition const& _function);
|
std::string generateFunction(FunctionDefinition const& _function);
|
||||||
/// Generates a getter for the given declaration and returns its name
|
/// Generates a getter for the given declaration and returns its name
|
||||||
|
@ -583,6 +583,20 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression()));
|
||||||
|
if (
|
||||||
|
functionType &&
|
||||||
|
functionType->kind() == FunctionType::Kind::Internal &&
|
||||||
|
!functionType->bound() &&
|
||||||
|
IRHelpers::referencedFunctionDeclaration(_functionCall.expression())
|
||||||
|
)
|
||||||
|
m_context.internalFunctionCalledDirectly(_functionCall.expression());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(
|
solUnimplementedAssert(
|
||||||
@ -688,9 +702,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
YulArity arity = YulArity::fromType(*functionType);
|
YulArity arity = YulArity::fromType(*functionType);
|
||||||
|
m_context.internalFunctionCalledThroughDispatch(arity);
|
||||||
|
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
// NOTE: generateInternalDispatchFunction() takes care of adding the function to function generation queue
|
IRNames::internalDispatch(arity) <<
|
||||||
m_context.generateInternalDispatchFunction(arity) <<
|
|
||||||
"(" <<
|
"(" <<
|
||||||
IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
|
IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
|
||||||
joinHumanReadablePrefixed(args) <<
|
joinHumanReadablePrefixed(args) <<
|
||||||
@ -1492,7 +1507,10 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
{
|
||||||
define(_memberAccess) << to_string(function->id()) << "\n";
|
define(_memberAccess) << to_string(function->id()) << "\n";
|
||||||
|
m_context.internalFunctionAccessed(_memberAccess, *function);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "Function not found in member access");
|
solAssert(false, "Function not found in member access");
|
||||||
break;
|
break;
|
||||||
@ -1756,7 +1774,14 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n";
|
{
|
||||||
|
FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract());
|
||||||
|
define(_identifier) << to_string(resolvedFunctionDef.id()) << "\n";
|
||||||
|
|
||||||
|
solAssert(resolvedFunctionDef.functionType(true), "");
|
||||||
|
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||||
|
m_context.internalFunctionAccessed(_identifier, resolvedFunctionDef);
|
||||||
|
}
|
||||||
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
handleVariableReference(*varDecl, _identifier);
|
handleVariableReference(*varDecl, _identifier);
|
||||||
else if (dynamic_cast<ContractDefinition const*>(declaration))
|
else if (dynamic_cast<ContractDefinition const*>(declaration))
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
void endVisit(Return const& _return) override;
|
void endVisit(Return const& _return) override;
|
||||||
void endVisit(UnaryOperation const& _unaryOperation) override;
|
void endVisit(UnaryOperation const& _unaryOperation) override;
|
||||||
bool visit(BinaryOperation const& _binOp) override;
|
bool visit(BinaryOperation const& _binOp) override;
|
||||||
|
bool visit(FunctionCall const& _funCall) override;
|
||||||
void endVisit(FunctionCall const& _funCall) override;
|
void endVisit(FunctionCall const& _funCall) override;
|
||||||
void endVisit(FunctionCallOptions const& _funCallOptions) override;
|
void endVisit(FunctionCallOptions const& _funCallOptions) override;
|
||||||
void endVisit(MemberAccess const& _memberAccess) override;
|
void endVisit(MemberAccess const& _memberAccess) override;
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
contract Test {
|
||||||
|
bytes6 name;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
function (bytes6 _name) internal setter = setName;
|
||||||
|
setter("abcdef");
|
||||||
|
|
||||||
|
applyShift(leftByteShift, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getName() public returns (bytes6 ret) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setName(bytes6 _name) private {
|
||||||
|
name = _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function leftByteShift(bytes6 _value, uint _shift) public returns (bytes6) {
|
||||||
|
return _value << _shift * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyShift(function (bytes6 _value, uint _shift) internal returns (bytes6) _shiftOperator, uint _bytes) internal {
|
||||||
|
name = _shiftOperator(name, _bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// getName() -> "def\x00\x00\x00"
|
@ -22,5 +22,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256[]): 0x20, 0x3, 0x1, 0x7, 0x3 -> 11
|
// f(uint256[]): 0x20, 0x3, 0x1, 0x7, 0x3 -> 11
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
contract A {
|
||||||
|
function f() internal virtual returns (uint256) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract B is A {
|
||||||
|
function f() internal override returns (uint256) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public returns (uint256) {
|
||||||
|
function() internal returns (uint256) ptr = A.f;
|
||||||
|
return ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// g() -> 1
|
@ -0,0 +1,17 @@
|
|||||||
|
library L {
|
||||||
|
function f() internal returns (uint) {
|
||||||
|
return 66;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function g() public returns (uint) {
|
||||||
|
function() internal returns(uint) ptr;
|
||||||
|
ptr = L.f;
|
||||||
|
return ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// g() -> 66
|
@ -0,0 +1,26 @@
|
|||||||
|
contract Base {
|
||||||
|
function f() internal returns (uint256 i) {
|
||||||
|
function() internal returns (uint256) ptr = g;
|
||||||
|
return ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() internal virtual returns (uint256 i) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract Derived is Base {
|
||||||
|
function g() internal override returns (uint256 i) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public returns (uint256 i) {
|
||||||
|
return f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// h() -> 2
|
Loading…
Reference in New Issue
Block a user