mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Enabling creation function call
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
@@ -96,6 +97,15 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl)
|
||||
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||
}
|
||||
|
||||
string IRGenerationContext::creationObjectName(ContractDefinition const& _contract) const
|
||||
{
|
||||
return _contract.name() + "_" + toString(_contract.id());
|
||||
}
|
||||
string IRGenerationContext::runtimeObjectName(ContractDefinition const& _contract) const
|
||||
{
|
||||
return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
|
||||
}
|
||||
|
||||
string IRGenerationContext::newYulVariable()
|
||||
{
|
||||
return "_" + to_string(++m_varCounter);
|
||||
@@ -170,6 +180,11 @@ YulUtilFunctions IRGenerationContext::utils()
|
||||
return YulUtilFunctions(m_evmVersion, m_revertStrings, m_functions);
|
||||
}
|
||||
|
||||
ABIFunctions IRGenerationContext::abiFunctions()
|
||||
{
|
||||
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
|
||||
}
|
||||
|
||||
std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message)
|
||||
{
|
||||
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/interface/DebugSettings.h>
|
||||
@@ -38,11 +39,8 @@
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
class ContractDefinition;
|
||||
class VariableDeclaration;
|
||||
class FunctionDefinition;
|
||||
class Expression;
|
||||
class YulUtilFunctions;
|
||||
class ABIFunctions;
|
||||
|
||||
/**
|
||||
* Class that contains contextual information during IR generation.
|
||||
@@ -93,6 +91,9 @@ public:
|
||||
std::string functionName(FunctionDefinition const& _function);
|
||||
std::string functionName(VariableDeclaration const& _varDecl);
|
||||
|
||||
std::string creationObjectName(ContractDefinition const& _contract) const;
|
||||
std::string runtimeObjectName(ContractDefinition const& _contract) const;
|
||||
|
||||
std::string newYulVariable();
|
||||
|
||||
std::string internalDispatch(size_t _in, size_t _out);
|
||||
@@ -102,6 +103,8 @@ public:
|
||||
|
||||
langutil::EVMVersion evmVersion() const { return m_evmVersion; };
|
||||
|
||||
ABIFunctions abiFunctions();
|
||||
|
||||
/// @returns code that stores @param _message for revert reason
|
||||
/// if m_revertStrings is debug.
|
||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
||||
@@ -112,6 +115,8 @@ public:
|
||||
/// function call that was invoked as part of the try statement.
|
||||
std::string trySuccessConditionVariable(Expression const& _expression) const;
|
||||
|
||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||
|
||||
private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
RevertStrings m_revertStrings;
|
||||
@@ -131,6 +136,8 @@ private:
|
||||
/// long as the order of Yul functions in the generated code is deterministic and the same on
|
||||
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
|
||||
std::set<FunctionDefinition const*> m_functionGenerationQueue;
|
||||
|
||||
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -48,9 +48,12 @@ using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
pair<string, string> IRGenerator::run(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, string const> const& _otherYulSources
|
||||
)
|
||||
{
|
||||
string const ir = yul::reindent(generate(_contract));
|
||||
string const ir = yul::reindent(generate(_contract, _otherYulSources));
|
||||
|
||||
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
||||
if (!asmStack.parseAndAnalyze("", ir))
|
||||
@@ -73,8 +76,19 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
return {warning + ir, warning + asmStack.print()};
|
||||
}
|
||||
|
||||
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
string IRGenerator::generate(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, string const> const& _otherYulSources
|
||||
)
|
||||
{
|
||||
auto subObjectSources = [&_otherYulSources](std::set<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string
|
||||
{
|
||||
std::string subObjectsSources;
|
||||
for (ContractDefinition const* subObject: subObjects)
|
||||
subObjectsSources += _otherYulSources.at(subObject);
|
||||
return subObjectsSources;
|
||||
};
|
||||
|
||||
Whiskers t(R"(
|
||||
object "<CreationObject>" {
|
||||
code {
|
||||
@@ -93,13 +107,15 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
<dispatch>
|
||||
<runtimeFunctions>
|
||||
}
|
||||
<runtimeSubObjects>
|
||||
}
|
||||
<subObjects>
|
||||
}
|
||||
)");
|
||||
|
||||
resetContext(_contract);
|
||||
|
||||
t("CreationObject", creationObjectName(_contract));
|
||||
t("CreationObject", m_context.creationObjectName(_contract));
|
||||
t("memoryInit", memoryInit());
|
||||
t("notLibrary", !_contract.isLibrary());
|
||||
|
||||
@@ -112,7 +128,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
constructorParams.emplace_back(m_context.newYulVariable());
|
||||
t(
|
||||
"copyConstructorArguments",
|
||||
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, creationObjectName(_contract))
|
||||
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, m_context.creationObjectName(_contract))
|
||||
);
|
||||
}
|
||||
t("constructorParams", joinHumanReadable(constructorParams));
|
||||
@@ -123,12 +139,14 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
generateImplicitConstructors(_contract);
|
||||
generateQueuedFunctions();
|
||||
t("functions", m_context.functionCollector().requestedFunctions());
|
||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
|
||||
resetContext(_contract);
|
||||
t("RuntimeObject", runtimeObjectName(_contract));
|
||||
t("RuntimeObject", m_context.runtimeObjectName(_contract));
|
||||
t("dispatch", dispatchRoutine(_contract));
|
||||
generateQueuedFunctions();
|
||||
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
|
||||
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
return t.render();
|
||||
}
|
||||
|
||||
@@ -313,6 +331,7 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
|
||||
return generator.code();
|
||||
}
|
||||
|
||||
|
||||
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
|
||||
{
|
||||
auto listAllParams = [&](
|
||||
@@ -375,7 +394,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
|
||||
codecopy(0, dataoffset("<object>"), datasize("<object>"))
|
||||
return(0, datasize("<object>"))
|
||||
)X");
|
||||
t("object", runtimeObjectName(_contract));
|
||||
t("object", m_context.runtimeObjectName(_contract));
|
||||
return t.render();
|
||||
}
|
||||
|
||||
@@ -384,16 +403,6 @@ string IRGenerator::callValueCheck()
|
||||
return "if callvalue() { revert(0, 0) }";
|
||||
}
|
||||
|
||||
string IRGenerator::creationObjectName(ContractDefinition const& _contract)
|
||||
{
|
||||
return _contract.name() + "_" + to_string(_contract.id());
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
@@ -50,10 +50,16 @@ public:
|
||||
|
||||
/// Generates and returns the IR code, in unoptimized and optimized form
|
||||
/// (or just pretty-printed, depending on the optimizer settings).
|
||||
std::pair<std::string, std::string> run(ContractDefinition const& _contract);
|
||||
std::pair<std::string, std::string> run(
|
||||
ContractDefinition const& _contract,
|
||||
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
|
||||
);
|
||||
|
||||
private:
|
||||
std::string generate(ContractDefinition const& _contract);
|
||||
std::string generate(
|
||||
ContractDefinition const& _contract,
|
||||
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
|
||||
);
|
||||
std::string generate(Block const& _block);
|
||||
|
||||
/// Generates code for all the functions from the function generation queue.
|
||||
@@ -87,8 +93,6 @@ private:
|
||||
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);
|
||||
|
||||
@@ -849,11 +849,63 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("encode", abi.tupleEncoder({arguments.front()->annotation().type},{parameterTypes.front()}));
|
||||
templ("encode", abi.tupleEncoder({arguments.front()->annotation().type}, {parameterTypes.front()}));
|
||||
templ("nonIndexedArgs", IRVariable(*arguments.front()).commaSeparatedList());
|
||||
templ("log", "log" + to_string(logNumber));
|
||||
templ("indexedArgs", indexedArgs);
|
||||
m_code << templ.render();
|
||||
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Creation:
|
||||
{
|
||||
solAssert(!functionType->gasSet(), "Gas limit set for contract creation.");
|
||||
solAssert(
|
||||
functionType->returnParameterTypes().size() == 1,
|
||||
"Constructor should return only one type"
|
||||
);
|
||||
|
||||
TypePointers argumentTypes;
|
||||
string constructorParams;
|
||||
for (ASTPointer<Expression const> const& arg: arguments)
|
||||
{
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
|
||||
}
|
||||
|
||||
ContractDefinition const* contract =
|
||||
&dynamic_cast<ContractType const&>(*functionType->returnParameterTypes().front()).contractDefinition();
|
||||
m_context.subObjectsCreated().insert(contract);
|
||||
|
||||
Whiskers t(R"(
|
||||
let <memPos> := <allocateTemporaryMemory>()
|
||||
let <memEnd> := add(<memPos>, datasize("<object>"))
|
||||
if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { revert(0, 0) }
|
||||
datacopy(<memPos>, dataoffset("<object>"), datasize("<object>"))
|
||||
<memEnd> := <abiEncode>(<memEnd><constructorParams>)
|
||||
<?saltSet>
|
||||
let <retVars> := create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>)
|
||||
<!saltSet>
|
||||
let <retVars> := create(<value>, <memPos>, sub(<memEnd>, <memPos>))
|
||||
</saltSet>
|
||||
<releaseTemporaryMemory>()
|
||||
)");
|
||||
t("memPos", m_context.newYulVariable());
|
||||
t("memEnd", m_context.newYulVariable());
|
||||
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
|
||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||
t("object", m_context.creationObjectName(*contract));
|
||||
t("abiEncode",
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
|
||||
);
|
||||
t("constructorParams", constructorParams);
|
||||
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
t("saltSet", functionType->saltSet());
|
||||
if (functionType->saltSet())
|
||||
t("salt", IRVariable(_functionCall.expression()).part("salt").name());
|
||||
t("retVars", IRVariable(_functionCall).commaSeparatedList());
|
||||
m_code << t.render();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user