mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #623 from chriseth/sol_constructorChecks
Checks for the constructor and ability to call functions
This commit is contained in:
commit
3d98ec1323
67
CallGraph.cpp
Normal file
67
CallGraph.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
cpp-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2014
|
||||
* Callgraph of functions inside a contract.
|
||||
*/
|
||||
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/CallGraph.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
void CallGraph::addFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
if (!m_functionsSeen.count(&_function))
|
||||
{
|
||||
m_functionsSeen.insert(&_function);
|
||||
m_workQueue.push(&_function);
|
||||
}
|
||||
}
|
||||
|
||||
set<FunctionDefinition const*> const& CallGraph::getCalls()
|
||||
{
|
||||
return m_functionsSeen;
|
||||
}
|
||||
|
||||
void CallGraph::computeCallGraph()
|
||||
{
|
||||
while (!m_workQueue.empty())
|
||||
{
|
||||
FunctionDefinition const* fun = m_workQueue.front();
|
||||
fun->accept(*this);
|
||||
m_workQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool CallGraph::visit(Identifier const& _identifier)
|
||||
{
|
||||
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
|
||||
if (fun)
|
||||
addFunction(*fun);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
55
CallGraph.h
Normal file
55
CallGraph.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of cpp-ethereum.
|
||||
|
||||
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
cpp-ethereum is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2014
|
||||
* Callgraph of functions inside a contract.
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/**
|
||||
* Can be used to compute the graph of calls (or rather references) between functions of the same
|
||||
* contract. Current functionality is limited to computing all functions that are directly
|
||||
* or indirectly called by some functions.
|
||||
*/
|
||||
class CallGraph: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
void addFunction(FunctionDefinition const& _function);
|
||||
void computeCallGraph();
|
||||
|
||||
std::set<FunctionDefinition const*> const& getCalls();
|
||||
|
||||
private:
|
||||
void addFunctionToQueue(FunctionDefinition const& _function);
|
||||
virtual bool visit(Identifier const& _identifier) override;
|
||||
|
||||
std::set<FunctionDefinition const*> m_functionsSeen;
|
||||
std::queue<FunctionDefinition const*> m_workQueue;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
79
Compiler.cpp
79
Compiler.cpp
@ -27,6 +27,7 @@
|
||||
#include <libsolidity/Compiler.h>
|
||||
#include <libsolidity/ExpressionCompiler.h>
|
||||
#include <libsolidity/CompilerUtils.h>
|
||||
#include <libsolidity/CallGraph.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -37,49 +38,63 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
|
||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||
{
|
||||
m_context = CompilerContext(); // clear it just in case
|
||||
m_context.setCompiledContracts(_contracts);
|
||||
|
||||
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
||||
m_context.addMagicGlobal(*variable);
|
||||
initializeContext(_contract, _magicGlobals, _contracts);
|
||||
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||
m_context.addFunction(*function);
|
||||
registerStateVariables(_contract);
|
||||
|
||||
appendFunctionSelector(_contract);
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||
function->accept(*this);
|
||||
|
||||
packIntoContractCreator(_contract, _contracts);
|
||||
// Swap the runtime context with the creation-time context
|
||||
CompilerContext runtimeContext;
|
||||
swap(m_context, runtimeContext);
|
||||
initializeContext(_contract, _magicGlobals, _contracts);
|
||||
packIntoContractCreator(_contract, runtimeContext);
|
||||
}
|
||||
|
||||
void Compiler::packIntoContractCreator(ContractDefinition const& _contract,
|
||||
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||
{
|
||||
CompilerContext runtimeContext;
|
||||
runtimeContext.setCompiledContracts(_contracts);
|
||||
swap(m_context, runtimeContext);
|
||||
|
||||
m_context.setCompiledContracts(_contracts);
|
||||
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
||||
m_context.addMagicGlobal(*variable);
|
||||
registerStateVariables(_contract);
|
||||
}
|
||||
|
||||
FunctionDefinition* constructor = nullptr;
|
||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||
if (function->getName() == _contract.getName())
|
||||
{
|
||||
constructor = function.get();
|
||||
break;
|
||||
}
|
||||
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
|
||||
// stack contains sub size
|
||||
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
||||
{
|
||||
set<FunctionDefinition const*> neededFunctions;
|
||||
FunctionDefinition const* constructor = _contract.getConstructor();
|
||||
if (constructor)
|
||||
{
|
||||
neededFunctions = getFunctionsNeededByConstructor(*constructor);
|
||||
|
||||
for (FunctionDefinition const* fun: neededFunctions)
|
||||
m_context.addFunction(*fun);
|
||||
|
||||
if (constructor)
|
||||
appendConstructorCall(*constructor);
|
||||
|
||||
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
|
||||
// stack contains sub size
|
||||
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
|
||||
m_context << u256(0) << eth::Instruction::RETURN;
|
||||
|
||||
// note that we have to explicitly include all used functions because of absolute jump
|
||||
// labels
|
||||
for (FunctionDefinition const* fun: neededFunctions)
|
||||
fun->accept(*this);
|
||||
}
|
||||
|
||||
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
|
||||
{
|
||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||
m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
|
||||
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
||||
unsigned argumentSize = 0;
|
||||
for (ASTPointer<VariableDeclaration> const& var: constructor->getParameters())
|
||||
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
||||
argumentSize += var->getType()->getCalldataEncodedSize();
|
||||
if (argumentSize > 0)
|
||||
{
|
||||
@ -87,18 +102,18 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract,
|
||||
m_context.appendProgramSize();
|
||||
m_context << u256(1); // copy it to byte one as expected for ABI calls
|
||||
m_context << eth::Instruction::CODECOPY;
|
||||
appendCalldataUnpacker(*constructor, true);
|
||||
appendCalldataUnpacker(_constructor, true);
|
||||
}
|
||||
//@todo calling other functions inside the constructor should either trigger a parse error
|
||||
//or we should copy them here (register them above and call "accept") - detecting which
|
||||
// functions are referenced / called needs to be done in a recursive way.
|
||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
|
||||
constructor->accept(*this);
|
||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
|
||||
m_context << returnTag;
|
||||
}
|
||||
}
|
||||
|
||||
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
|
||||
m_context << u256(0) << eth::Instruction::RETURN;
|
||||
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
|
||||
{
|
||||
CallGraph callgraph;
|
||||
callgraph.addFunction(_constructor);
|
||||
callgraph.computeCallGraph();
|
||||
return callgraph.getCalls();
|
||||
}
|
||||
|
||||
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
||||
|
12
Compiler.h
12
Compiler.h
@ -38,10 +38,16 @@ public:
|
||||
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
||||
|
||||
private:
|
||||
/// Creates a new compiler context / assembly, packs the current code into the data part and
|
||||
/// adds the constructor code.
|
||||
void packIntoContractCreator(ContractDefinition const& _contract,
|
||||
/// Registers the global objects and the non-function objects inside the contract with the context.
|
||||
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||
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.
|
||||
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
||||
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);
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user