[Sol2Yul] Adding support for constructors with parameters in case of inheritance

This commit is contained in:
Djordje Mijovic
2020-04-22 14:47:08 +02:00
parent d0fcd468f6
commit 5329da93fb
22 changed files with 476 additions and 82 deletions
+123 -48
View File
@@ -81,7 +81,9 @@ string IRGenerator::generate(ContractDefinition const& _contract)
object "<CreationObject>" {
code {
<memoryInit>
<constructor>
<callValueCheck>
<?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
<implicitConstructor>(<constructorParams>)
<deploy>
<functions>
}
@@ -99,8 +101,25 @@ string IRGenerator::generate(ContractDefinition const& _contract)
t("CreationObject", creationObjectName(_contract));
t("memoryInit", memoryInit());
t("constructor", constructorCode(_contract));
FunctionDefinition const* constructor = _contract.constructor();
t("callValueCheck", !constructor || !constructor->isPayable() ? callValueCheck() : "");
vector<string> constructorParams;
if (constructor && !constructor->parameters().empty())
{
for (size_t i = 0; i < constructor->parameters().size(); ++i)
constructorParams.emplace_back(m_context.newYulVariable());
t(
"copyConstructorArguments",
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, creationObjectName(_contract))
);
}
t("constructorParams", joinHumanReadable(constructorParams));
t("constructorHasParams", !constructorParams.empty());
t("implicitConstructor", implicitConstructorName(_contract));
t("deploy", deployCode(_contract));
generateImplicitConstructors(_contract);
generateQueuedFunctions();
t("functions", m_context.functionCollector().requestedFunctions());
@@ -238,64 +257,115 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec
return generator.code();
}
string IRGenerator::constructorCode(ContractDefinition const& _contract)
pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstructorArguments(
ContractDefinition const& _contract
)
{
// Initialization of state variables in base-to-derived order.
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
map<ContractDefinition const*, string> constructorParams;
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
using boost::adaptors::reverse;
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
base->name().annotation().referencedDeclaration
)->constructor(); baseConstructor && base->arguments())
baseConstructorArguments.emplace_back(
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
base->arguments()
);
ostringstream out;
if (FunctionDefinition const* constructor = _contract.constructor())
for (auto const& modifier: constructor->modifiers())
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
modifier->name()->annotation().referencedDeclaration
)->constructor(); baseConstructor && modifier->arguments())
baseConstructorArguments.emplace_back(
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
modifier->arguments()
);
FunctionDefinition const* constructor = _contract.constructor();
if (constructor && !constructor->isPayable())
out << callValueCheck();
for (ContractDefinition const* contract: reverse(_contract.annotation().linearizedBaseContracts))
IRGeneratorForStatements generator{m_context, m_utils};
for (auto&& [baseContract, arguments]: baseConstructorArguments)
{
out <<
"\n// Begin state variable initialization for contract \"" <<
contract->name() <<
"\" (" <<
contract->stateVariables().size() <<
" variables)\n";
IRGeneratorForStatements generator{m_context, m_utils};
for (VariableDeclaration const* variable: contract->stateVariables())
if (!variable->isConstant() && !variable->immutable())
generator.initializeStateVar(*variable);
out << generator.code();
out << "// End state variable initialization for contract \"" << contract->name() << "\".\n";
solAssert(baseContract && arguments, "");
if (baseContract->constructor() && !arguments->empty())
{
vector<string> params;
for (size_t i = 0; i < arguments->size(); ++i)
params.emplace_back(generator.evaluateExpression(
*(arguments->at(i)),
*(baseContract->constructor()->parameters()[i]->type())
).commaSeparatedList());
constructorParams[baseContract] = joinHumanReadable(params);
}
}
if (constructor)
return {generator.code(), constructorParams};
}
string IRGenerator::initStateVariables(ContractDefinition const& _contract)
{
IRGeneratorForStatements generator{m_context, m_utils};
for (VariableDeclaration const* variable: _contract.stateVariables())
if (!variable->isConstant() && !variable->immutable())
generator.initializeStateVar(*variable);
return generator.code();
}
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
{
auto listAllParams = [&](
map<ContractDefinition const*, string> const& baseParams) -> string
{
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
unsigned paramVars = make_shared<TupleType>(constructor->functionType(false)->parameterTypes())->sizeOnStack();
vector<string> params;
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
if (baseParams.count(contract))
params.emplace_back(baseParams.at(contract));
return joinHumanReadable(params);
};
Whiskers t(R"X(
let programSize := datasize("<object>")
let argSize := sub(codesize(), programSize)
map<ContractDefinition const*, string> baseConstructorParams;
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
{
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
baseConstructorParams.erase(contract);
let memoryDataOffset := <allocate>(argSize)
codecopy(memoryDataOffset, programSize, argSize)
m_context.functionCollector().createFunction(implicitConstructorName(*contract), [&]() {
Whiskers t(R"(
function <functionName>(<params><comma><baseParams>) {
<evalBaseArguments>
<?hasNextConstructor> <nextConstructor>(<nextParams>) </hasNextConstructor>
<initStateVariables>
<userDefinedConstructorBody>
}
)");
string params;
if (contract->constructor())
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
t("params", params);
string baseParamsString = listAllParams(baseConstructorParams);
t("baseParams", baseParamsString);
t("comma", !params.empty() && !baseParamsString.empty() ? ", " : "");
t("functionName", implicitConstructorName(*contract));
pair<string, map<ContractDefinition const*, string>> evaluatedArgs = evaluateConstructorArguments(*contract);
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
t("evalBaseArguments", evaluatedArgs.first);
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
{
t("hasNextConstructor", true);
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
t("nextConstructor", implicitConstructorName(*nextContract));
t("nextParams", listAllParams(baseConstructorParams));
}
else
t("hasNextConstructor", false);
t("initStateVariables", initStateVariables(*contract));
t("userDefinedConstructorBody", contract->constructor() ? generate(contract->constructor()->body()) : "");
<assignToParams> <abiDecode>(memoryDataOffset, add(memoryDataOffset, argSize))
<constructorName>(<params>)
)X");
t("object", creationObjectName(_contract));
t("allocate", m_utils.allocationFunction());
t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ");
t("params", suffixedVariableNameList("param_", 0, paramVars));
t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true));
t("constructorName", m_context.enqueueFunctionForCodeGeneration(*constructor));
out << t.render();
return t.render();
});
}
return out.str();
}
string IRGenerator::deployCode(ContractDefinition const& _contract)
@@ -323,6 +393,11 @@ string IRGenerator::runtimeObjectName(ContractDefinition const& _contract)
return _contract.name() + "_" + to_string(_contract.id()) + "_deployed";
}
string IRGenerator::implicitConstructorName(ContractDefinition const& _contract)
{
return "constructor_" + _contract.name() + "_" + to_string(_contract.id());
}
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{
Whiskers t(R"X(
+18 -1
View File
@@ -67,12 +67,29 @@ private:
/// Generates code that assigns the initial value of the respective type.
std::string generateInitialAssignment(VariableDeclaration const& _varDecl);
std::string constructorCode(ContractDefinition const& _contract);
/// Generates implicit constructors for all contracts in the inheritance hierarchy of
/// @a _contract
/// If there are user defined constructors, their body will be included in implicit constructors body.
void generateImplicitConstructors(ContractDefinition const& _contract);
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
/// @a _contract
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
std::pair<std::string, std::map<ContractDefinition const*, std::string>> evaluateConstructorArguments(
ContractDefinition const& _contract
);
/// Initializes state variables of
/// @a _contract
/// @returns Source code to initialize state variables
std::string initStateVariables(ContractDefinition const& _contract);
std::string deployCode(ContractDefinition const& _contract);
std::string callValueCheck();
std::string creationObjectName(ContractDefinition const& _contract);
std::string runtimeObjectName(ContractDefinition const& _contract);
std::string implicitConstructorName(ContractDefinition const& _contract);
std::string dispatchRoutine(ContractDefinition const& _contract);
@@ -169,6 +169,14 @@ void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _va
assign(m_context.localVariable(_varDecl), zero);
}
IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType)
{
_expression.accept(*this);
IRVariable variable{m_context.newYulVariable(), _targetType};
define(variable, _expression);
return variable;
}
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
{
if (Expression const* expression = _varDeclStatement.initialValue())
@@ -51,6 +51,9 @@ public:
/// Generates code to initialize the given local variable.
void initializeLocalVar(VariableDeclaration const& _varDecl);
/// Calculates expression's value and returns variable where it was stored
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override;