Implement function modifiers.

This commit is contained in:
chriseth 2020-11-30 18:59:49 +01:00
parent 9328503265
commit ccaa81fbe7
22 changed files with 220 additions and 8 deletions

View File

@ -32,10 +32,9 @@ YulArity YulArity::fromType(FunctionType const& _functionType)
TupleType(_functionType.returnParameterTypes()).sizeOnStack()
};
}
string IRNames::function(FunctionDefinition const& _function)
{
// @TODO previously, we had to distinguish creation context and runtime context,
// but since we do not work with jump positions anymore, this should not be a problem, right?
return "fun_" + _function.name() + "_" + to_string(_function.id());
}
@ -44,6 +43,21 @@ string IRNames::function(VariableDeclaration const& _varDecl)
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
}
string IRNames::modifierInvocation(ModifierInvocation const& _modifierInvocation)
{
// This uses the ID of the modifier invocation because it has to be unique
// for each invocation.
solAssert(!_modifierInvocation.name().path().empty(), "");
string const& modifierName = _modifierInvocation.name().path().back();
solAssert(!modifierName.empty(), "");
return "modifier_" + modifierName + "_" + to_string(_modifierInvocation.id());
}
string IRNames::functionWithModifierInner(FunctionDefinition const& _function)
{
return "fun_" + _function.name() + "_" + to_string(_function.id()) + "_inner";
}
string IRNames::creationObject(ContractDefinition const& _contract)
{
return _contract.name() + "_" + toString(_contract.id());

View File

@ -49,6 +49,8 @@ struct IRNames
{
static std::string function(FunctionDefinition const& _function);
static std::string function(VariableDeclaration const& _varDecl);
static std::string modifierInvocation(ModifierInvocation const& _modifierInvocation);
static std::string functionWithModifierInner(FunctionDefinition const& _function);
static std::string creationObject(ContractDefinition const& _contract);
static std::string runtimeObject(ContractDefinition const& _contract);
static std::string internalDispatch(YulArity const& _arity);

View File

@ -80,6 +80,11 @@ IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const&
return m_localVariables.at(&_varDecl);
}
void IRGenerationContext::resetLocalVariables()
{
m_localVariables.clear();
}
void IRGenerationContext::registerImmutableVariable(VariableDeclaration const& _variable)
{
solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable.");

View File

@ -97,6 +97,7 @@ public:
IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl);
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
IRVariable const& localVariable(VariableDeclaration const& _varDecl);
void resetLocalVariables();
/// Registers an immutable variable of the contract.
/// Should only be called at construction time.

View File

@ -249,10 +249,10 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
{
string functionName = IRNames::function(_function);
return m_context.functionCollector().createFunction(functionName, [&]() {
solUnimplementedAssert(_function.modifiers().empty(), "Modifiers not implemented yet.");
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<initReturnVariables>
<retInit>
<body>
}
)");
@ -268,8 +268,137 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
retInit += generateInitialAssignment(*varDecl);
}
t("retParams", joinHumanReadable(retParams));
t("initReturnVariables", retInit);
t("retInit", retInit);
if (_function.modifiers().empty())
t("body", generate(_function.body()));
else
{
for (size_t i = 0; i < _function.modifiers().size(); ++i)
{
ModifierInvocation const& modifier = *_function.modifiers().at(i);
string next =
i + 1 < _function.modifiers().size() ?
IRNames::modifierInvocation(*_function.modifiers().at(i + 1)) :
IRNames::functionWithModifierInner(_function);
generateModifier(modifier, _function, next);
}
t("body",
(retParams.empty() ? string{} : joinHumanReadable(retParams) + " := ") +
IRNames::modifierInvocation(*_function.modifiers().at(0)) +
"(" +
joinHumanReadable(retParams + params) +
")"
);
// Now generate the actual inner function.
generateFunctionWithModifierInner(_function);
}
return t.render();
});
}
string IRGenerator::generateModifier(
ModifierInvocation const& _modifierInvocation,
FunctionDefinition const& _function,
string const& _nextFunction
)
{
string functionName = IRNames::modifierInvocation(_modifierInvocation);
return m_context.functionCollector().createFunction(functionName, [&]() {
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<assignRetParams>
<evalArgs>
<body>
}
)");
t("functionName", functionName);
vector<string> retParamsIn;
for (auto const& varDecl: _function.returnParameters())
retParamsIn += IRVariable(*varDecl).stackSlots();
vector<string> params = retParamsIn;
for (auto const& varDecl: _function.parameters())
params += m_context.addLocalVariable(*varDecl).stackSlots();
t("params", joinHumanReadable(params));
vector<string> retParams;
string assignRetParams;
for (size_t i = 0; i < retParamsIn.size(); ++i)
{
retParams.emplace_back(m_context.newYulVariable());
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
}
t("retParams", joinHumanReadable(retParams));
t("assignRetParams", assignRetParams);
solAssert(*_modifierInvocation.name().annotation().requiredLookup == VirtualLookup::Virtual, "");
ModifierDefinition const& modifier = dynamic_cast<ModifierDefinition const&>(
*_modifierInvocation.name().annotation().referencedDeclaration
).resolveVirtual(m_context.mostDerivedContract());
solAssert(
modifier.parameters().empty() ==
(!_modifierInvocation.arguments() || _modifierInvocation.arguments()->empty()),
""
);
IRGeneratorForStatements expressionEvaluator(m_context, m_utils);
if (_modifierInvocation.arguments())
for (size_t i = 0; i < _modifierInvocation.arguments()->size(); i++)
{
IRVariable argument = expressionEvaluator.evaluateExpression(
*_modifierInvocation.arguments()->at(i),
*modifier.parameters()[i]->annotation().type
);
expressionEvaluator.define(
m_context.addLocalVariable(*modifier.parameters()[i]),
argument
);
}
t("evalArgs", expressionEvaluator.code());
IRGeneratorForStatements generator(m_context, m_utils, [&]() {
string ret = joinHumanReadable(retParams);
return
(ret.empty() ? "" : ret + " := ") +
_nextFunction + "(" + joinHumanReadable(params) + ")\n";
});
generator.generate(modifier.body());
t("body", generator.code());
return t.render();
});
}
string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& _function)
{
string functionName = IRNames::functionWithModifierInner(_function);
return m_context.functionCollector().createFunction(functionName, [&]() {
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<assignRetParams>
<body>
}
)");
t("functionName", functionName);
vector<string> retParams;
vector<string> retParamsIn;
for (auto const& varDecl: _function.returnParameters())
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
string assignRetParams;
for (size_t i = 0; i < retParams.size(); ++i)
{
retParamsIn.emplace_back(m_context.newYulVariable());
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
}
vector<string> params = retParamsIn;
for (auto const& varDecl: _function.parameters())
params += m_context.addLocalVariable(*varDecl).stackSlots();
t("params", joinHumanReadable(params));
t("retParams", joinHumanReadable(retParams));
t("assignRetParams", assignRetParams);
t("body", generate(_function.body()));
return t.render();
});
@ -510,6 +639,7 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
{
// TODO reset local variables somewhere here.
auto listAllParams = [&](
map<ContractDefinition const*, vector<string>> const& baseParams) -> vector<string>
{

View File

@ -73,6 +73,12 @@ private:
InternalDispatchMap generateInternalDispatchFunctions();
/// Generates code for and returns the name of the function.
std::string generateFunction(FunctionDefinition const& _function);
std::string generateModifier(
ModifierInvocation const& _modifierInvocation,
FunctionDefinition const& _function,
std::string const& _nextFunction
);
std::string generateFunctionWithModifierInner(FunctionDefinition const& _function);
/// Generates a getter for the given declaration and returns its name
std::string generateGetter(VariableDeclaration const& _varDecl);

View File

@ -581,6 +581,12 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
return false;
}
void IRGeneratorForStatements::endVisit(PlaceholderStatement const&)
{
solAssert(m_placeholderCallback, "");
m_code << m_placeholderCallback();
}
bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
{
setLocation(_forStatement);

View File

@ -40,8 +40,13 @@ class YulUtilFunctions;
class IRGeneratorForStatements: public ASTConstVisitor
{
public:
IRGeneratorForStatements(IRGenerationContext& _context, YulUtilFunctions& _utils):
IRGeneratorForStatements(
IRGenerationContext& _context,
YulUtilFunctions& _utils,
std::function<std::string()> _placeholderCallback = {}
):
m_context(_context),
m_placeholderCallback(std::move(_placeholderCallback)),
m_utils(_utils)
{}
@ -58,6 +63,9 @@ public:
/// Calculates expression's value and returns variable where it was stored
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); }
/// @returns the name of a function that computes the value of the given constant
/// and also generates the function.
std::string constantValueFunction(VariableDeclaration const& _constant);
@ -66,6 +74,7 @@ public:
bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override;
bool visit(TupleExpression const& _tuple) override;
void endVisit(PlaceholderStatement const& _placeholder) override;
bool visit(Block const& _block) override;
void endVisit(Block const& _block) override;
bool visit(IfStatement const& _ifStatement) override;
@ -135,8 +144,7 @@ private:
/// @returns an output stream that can be used to define @a _var using a function call or
/// single stack slot expression.
std::ostream& define(IRVariable const& _var);
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); }
/// Assigns @a _var to the value of @a _value while performing type conversions, if required.
void assign(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, false); }
/// Declares variable @a _var.
@ -189,6 +197,7 @@ private:
std::ostringstream m_code;
IRGenerationContext& m_context;
std::function<std::string()> m_placeholderCallback;
YulUtilFunctions& m_utils;
std::optional<IRLValue> m_currentLValue;
langutil::SourceLocation m_currentLocation;

View File

@ -34,5 +34,7 @@ contract C is B {
return (x, y);
}
}
// ====
// compileViaYul: also
// ----
// test() -> 5, 10

View File

@ -28,6 +28,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f() -> true
// g() -> FAILURE

View File

@ -14,6 +14,8 @@ contract C {
x = t;
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// f() ->

View File

@ -14,6 +14,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// f() ->

View File

@ -8,6 +8,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// getOne() -> 0
// getOne(), 1 wei -> 1

View File

@ -13,5 +13,7 @@ contract C is A {
}
}
// ====
// compileViaYul: also
// ----
// f() -> false

View File

@ -24,5 +24,7 @@ contract Test {
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x202

View File

@ -30,5 +30,7 @@ contract Test {
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x202

View File

@ -15,5 +15,7 @@ contract C is A {
}
}
// ====
// compileViaYul: also
// ----
// f() -> false

View File

@ -0,0 +1,13 @@
contract C {
modifier m(bool condition) {
if (condition) _;
}
function f(uint x) public m(x >= 10) returns (uint[5] memory r) {
r[2] = 3;
}
}
// ----
// f(uint256): 9 -> 0x00, 0x00, 0x00, 0x00, 0x00
// f(uint256): 10 -> 0x00, 0x00, 3, 0x00, 0x00

View File

@ -10,6 +10,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// f() -> 2

View File

@ -14,6 +14,8 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// f() ->

View File

@ -13,6 +13,8 @@ contract C {
return address(this).balance;
}
}
// ====
// compileViaYul: also
// ----
// f(), 27 wei -> FAILURE
// balance() -> 0

View File

@ -16,6 +16,8 @@ contract C {
x += 3;
}
}
// ====
// compileViaYul: also
// ----
// f1() ->
// x() -> 0x08