Call constructors of base classes.

This commit is contained in:
Christian 2015-01-19 23:08:48 +01:00
parent af92f98d86
commit ddf5e20d10
5 changed files with 108 additions and 24 deletions

View File

@ -31,13 +31,9 @@ namespace dev
namespace solidity
{
void CallGraph::addFunction(FunctionDefinition const& _function)
void CallGraph::addNode(ASTNode const& _node)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
_node.accept(*this);
}
set<FunctionDefinition const*> const& CallGraph::getCalls()
@ -63,5 +59,41 @@ bool CallGraph::visit(Identifier const& _identifier)
return true;
}
bool CallGraph::visit(FunctionDefinition const& _function)
{
addFunction(_function);
return true;
}
bool CallGraph::visit(MemberAccess const& _memberAccess)
{
// used for "BaseContract.baseContractFunction"
if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(_memberAccess.getMemberName()))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == _memberAccess.getMemberName())
{
addFunction(*function);
return true;
}
}
}
return true;
}
void CallGraph::addFunction(FunctionDefinition const& _function)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
}
}
}

View File

@ -38,14 +38,17 @@ namespace solidity
class CallGraph: private ASTConstVisitor
{
public:
void addFunction(FunctionDefinition const& _function);
void addNode(ASTNode const& _node);
void computeCallGraph();
std::set<FunctionDefinition const*> const& getCalls();
private:
void addFunctionToQueue(FunctionDefinition const& _function);
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
void addFunction(FunctionDefinition const& _function);
std::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue;

View File

@ -67,19 +67,52 @@ void Compiler::initializeContext(ContractDefinition const& _contract,
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
set<FunctionDefinition const*> neededFunctions;
// TODO constructors of base classes
FunctionDefinition const* constructor = _contract.getConstructor();
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor);
set<ASTNode const*> nodesUsedInConstructors;
// Determine the arguments that are used for the base constructors and also which functions
// are needed at compile time.
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->getConstructor())
nodesUsedInConstructors.insert(constructor);
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
{
baseArguments[baseContract] = &base->getArguments();
for (ASTPointer<Expression> const& arg: base->getArguments())
nodesUsedInConstructors.insert(arg.get());
}
}
}
//@TODO add virtual functions
neededFunctions = getFunctionsCalled(nodesUsedInConstructors);
// TODO we should add the overridden functions
for (FunctionDefinition const* fun: neededFunctions)
m_context.addFunction(*fun);
// we have many of them now
if (constructor)
appendConstructorCall(*constructor);
// Call constructors in base-to-derived order.
// The Constructor for the most derived contract is called later.
for (unsigned i = 1; i < bases.size(); i++)
{
ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, "");
FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor)
continue;
solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
}
if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor());
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size
@ -92,6 +125,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
fun->accept(*this);
}
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
vector<ASTPointer<Expression>> const& _arguments)
{
FunctionType constructorType(_constructor);
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < _arguments.size(); ++i)
{
compileExpression(*_arguments[i]);
ExpressionCompiler::appendTypeConversion(m_context, *_arguments[i]->getType(),
*constructorType.getParameterTypes()[i]);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnLabel;
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
@ -111,11 +159,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
m_context << returnTag;
}
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes)
{
// TODO this does not add virtual functions
CallGraph callgraph;
callgraph.addFunction(_constructor);
callgraph.computeCallGraph();
for (ASTNode const* node: _nodes)
callgraph.addNode(*node);
return callgraph.getCalls();
}

View File

@ -43,12 +43,13 @@ private:
void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context.
/// adds the constructor code.
/// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions referenced inside _nodes.
std::set<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.

View File

@ -31,7 +31,6 @@ namespace dev
namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{
for (Declaration const* declaration: _globals)