mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Virtual function calls.
This commit is contained in:
committed by
Mathias Baumann
parent
18ab8aeb85
commit
565d9837e4
@@ -20,8 +20,11 @@
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <libdevcore/Whiskers.h>
|
||||
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
using namespace std;
|
||||
@@ -45,6 +48,35 @@ string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
|
||||
return m_localVariables[&_varDecl];
|
||||
}
|
||||
|
||||
string IRGenerationContext::functionName(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());
|
||||
}
|
||||
|
||||
FunctionDefinition const& IRGenerationContext::virtualFunction(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?
|
||||
string name = _function.name();
|
||||
FunctionType functionType(_function);
|
||||
for (auto const& contract: m_inheritanceHierarchy)
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
if (
|
||||
function->name() == name &&
|
||||
!function->isConstructor() &&
|
||||
FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType)
|
||||
)
|
||||
return *function;
|
||||
solAssert(false, "Super function " + name + " not found.");
|
||||
}
|
||||
|
||||
string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration)
|
||||
{
|
||||
return functionName(virtualFunction(_functionDeclaration));
|
||||
}
|
||||
|
||||
string IRGenerationContext::newYulVariable()
|
||||
{
|
||||
return "_" + to_string(++m_varCounter);
|
||||
@@ -56,3 +88,43 @@ string IRGenerationContext::variable(Expression const& _expression)
|
||||
solUnimplementedAssert(size == 1, "");
|
||||
return "expr_" + to_string(_expression.id());
|
||||
}
|
||||
|
||||
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
||||
{
|
||||
// TODO can we limit the generated functions to only those visited
|
||||
// in the expression context? What about creation / runtime context?
|
||||
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
||||
return m_functions->createFunction(funName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(fun <comma> <in>) -> <out> {
|
||||
switch fun
|
||||
<#cases>
|
||||
case <funID>
|
||||
{
|
||||
<out> := <name>(<in>)
|
||||
}
|
||||
</cases>
|
||||
default { invalid() }
|
||||
}
|
||||
)");
|
||||
templ("functionName", funName);
|
||||
templ("comma", _in > 0 ? "," : "");
|
||||
YulUtilFunctions utils(m_evmVersion, m_functions);
|
||||
templ("in", utils.suffixedVariableNameList("in_", 0, _in));
|
||||
templ("out", utils.suffixedVariableNameList("out_", 0, _out));
|
||||
vector<map<string, string>> functions;
|
||||
for (auto const& contract: m_inheritanceHierarchy)
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
if (
|
||||
!function->isConstructor() &&
|
||||
function->parameters().size() == _in &&
|
||||
function->returnParameters().size() == _out
|
||||
)
|
||||
functions.emplace_back(map<string, string> {
|
||||
{ "funID", to_string(function->id()) },
|
||||
{ "name", functionName(*function)}
|
||||
});
|
||||
templ("cases", move(functions));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,13 +28,16 @@
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class ContractDefinition;
|
||||
class VariableDeclaration;
|
||||
class FunctionDefinition;
|
||||
class Expression;
|
||||
|
||||
/**
|
||||
@@ -51,16 +54,30 @@ public:
|
||||
|
||||
std::shared_ptr<MultiUseYulFunctionCollector> functionCollector() const { return m_functions; }
|
||||
|
||||
/// Sets the current inheritance hierarchy from derived to base.
|
||||
void setInheritanceHierarchy(std::vector<ContractDefinition const*> _hierarchy)
|
||||
{
|
||||
m_inheritanceHierarchy = std::move(_hierarchy);
|
||||
}
|
||||
|
||||
|
||||
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
||||
std::string variableName(VariableDeclaration const& _varDecl);
|
||||
std::string functionName(FunctionDefinition const& _function);
|
||||
FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration);
|
||||
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
||||
|
||||
std::string newYulVariable();
|
||||
/// @returns the variable (or comma-separated list of variables) that contain
|
||||
/// the value of the given expression.
|
||||
std::string variable(Expression const& _expression);
|
||||
|
||||
std::string internalDispatch(size_t _in, size_t _out);
|
||||
|
||||
private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
||||
std::map<VariableDeclaration const*, std::string> m_localVariables;
|
||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||
size_t m_varCounter = 0;
|
||||
|
||||
@@ -93,6 +93,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
)");
|
||||
|
||||
resetContext();
|
||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||
t("CreationObject", creationObjectName(_contract));
|
||||
t("memoryInit", memoryInit());
|
||||
t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
|
||||
@@ -100,6 +101,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
t("functions", m_context.functionCollector()->requestedFunctions());
|
||||
|
||||
resetContext();
|
||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||
t("RuntimeObject", runtimeObjectName(_contract));
|
||||
t("dispatch", dispatchRoutine(_contract));
|
||||
t("runtimeFunctions", m_context.functionCollector()->requestedFunctions());
|
||||
@@ -115,7 +117,7 @@ string IRGenerator::generate(Block const& _block)
|
||||
|
||||
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
string functionName = "fun_" + to_string(_function.id()) + "_" + _function.name();
|
||||
string functionName = m_context.functionName(_function);
|
||||
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
||||
Whiskers t("\nfunction <functionName>(<params>) <returns> {\n<body>\n}\n");
|
||||
t("functionName", functionName);
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
@@ -64,10 +66,10 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
|
||||
solUnimplementedAssert(
|
||||
*_assignment.rightHandSide().annotation().type == *_assignment.leftHandSide().annotation().type,
|
||||
"Type conversion not yet implemented"
|
||||
);
|
||||
// solUnimplementedAssert(
|
||||
// *_assignment.rightHandSide().annotation().type == *_assignment.leftHandSide().annotation().type,
|
||||
// "Type conversion not yet implemented"
|
||||
// );
|
||||
// TODO proper lvalue handling
|
||||
auto const& identifier = dynamic_cast<Identifier const&>(_assignment.leftHandSide());
|
||||
string varName = m_context.variableName(dynamic_cast<VariableDeclaration const&>(*identifier.annotation().referencedDeclaration));
|
||||
@@ -98,11 +100,115 @@ void IRGeneratorForStatements::endVisit(BinaryOperation const& _binOp)
|
||||
solUnimplementedAssert(false, "");
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
auto const& decl = dynamic_cast<VariableDeclaration const&>(
|
||||
*_identifier.annotation().referencedDeclaration
|
||||
);
|
||||
m_code << "let " << m_context.variable(_identifier) << " := " << m_context.variableName(decl) << "\n";
|
||||
solUnimplementedAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
|
||||
FunctionTypePointer functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
|
||||
|
||||
TypePointers parameterTypes = functionType->parameterTypes();
|
||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
|
||||
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
|
||||
if (!functionType->takesArbitraryParameters())
|
||||
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||
|
||||
vector<ASTPointer<Expression const>> arguments;
|
||||
if (callArgumentNames.empty())
|
||||
// normal arguments
|
||||
arguments = callArguments;
|
||||
else
|
||||
// named arguments
|
||||
for (auto const& parameterName: functionType->parameterNames())
|
||||
{
|
||||
auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer<ASTString> const& _argName) {
|
||||
return *_argName == parameterName;
|
||||
});
|
||||
|
||||
solAssert(it != callArgumentNames.cend(), "");
|
||||
arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]);
|
||||
}
|
||||
|
||||
solUnimplementedAssert(!functionType->bound(), "");
|
||||
switch (functionType->kind())
|
||||
{
|
||||
case FunctionType::Kind::Internal:
|
||||
{
|
||||
vector<string> args;
|
||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
arguments[i]->accept(*this);
|
||||
// TODO convert
|
||||
//utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
|
||||
args.emplace_back(m_context.variable(*arguments[i]));
|
||||
}
|
||||
|
||||
if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||
{
|
||||
solAssert(!functionType->bound(), "");
|
||||
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
||||
{
|
||||
// @TODO The function can very well return multiple vars.
|
||||
m_code <<
|
||||
"let " <<
|
||||
m_context.variable(_functionCall) <<
|
||||
" := " <<
|
||||
m_context.virtualFunctionName(*functionDef) <<
|
||||
"(" <<
|
||||
joinHumanReadable(args) <<
|
||||
")\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_functionCall.expression().accept(*this);
|
||||
|
||||
// @TODO The function can very well return multiple vars.
|
||||
args = vector<string>{m_context.variable(_functionCall.expression())} + args;
|
||||
m_code <<
|
||||
"let " <<
|
||||
m_context.variable(_functionCall) <<
|
||||
" := " <<
|
||||
m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) <<
|
||||
"(" <<
|
||||
joinHumanReadable(args) <<
|
||||
")\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solUnimplemented("");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||
{
|
||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||
string value;
|
||||
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||
value = to_string(m_context.virtualFunction(*functionDef).id());
|
||||
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
value = m_context.variableName(*varDecl);
|
||||
else
|
||||
solUnimplemented("");
|
||||
m_code << "let " << m_context.variable(_identifier) << " := " << value << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||
{
|
||||
TypePointer type = _literal.annotation().type;
|
||||
|
||||
switch (type->category())
|
||||
{
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::Address:
|
||||
m_code << "let " << m_context.variable(_literal) << " := " << toCompactHexWithPrefix(type->literalValue(&_literal)) << "\n";
|
||||
break;
|
||||
case Type::Category::StringLiteral:
|
||||
solUnimplemented("");
|
||||
break; // will be done during conversion
|
||||
default:
|
||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,9 @@ public:
|
||||
bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
void endVisit(BinaryOperation const& _binOp) override;
|
||||
bool visit(FunctionCall const& _funCall) override;
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
bool visit(Literal const& _literal) override;
|
||||
|
||||
private:
|
||||
std::ostringstream m_code;
|
||||
|
||||
Reference in New Issue
Block a user