mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol2Yul] Adding support for constructors with parameters in case of inheritance
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user