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/Compiler.h>
|
||||||
#include <libsolidity/ExpressionCompiler.h>
|
#include <libsolidity/ExpressionCompiler.h>
|
||||||
#include <libsolidity/CompilerUtils.h>
|
#include <libsolidity/CompilerUtils.h>
|
||||||
|
#include <libsolidity/CallGraph.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -37,49 +38,63 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
|
|||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||||
{
|
{
|
||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
m_context.setCompiledContracts(_contracts);
|
initializeContext(_contract, _magicGlobals, _contracts);
|
||||||
|
|
||||||
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
|
||||||
m_context.addMagicGlobal(*variable);
|
|
||||||
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||||
m_context.addFunction(*function);
|
m_context.addFunction(*function);
|
||||||
registerStateVariables(_contract);
|
|
||||||
|
|
||||||
appendFunctionSelector(_contract);
|
appendFunctionSelector(_contract);
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||||
function->accept(*this);
|
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)
|
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||||
{
|
{
|
||||||
CompilerContext runtimeContext;
|
m_context.setCompiledContracts(_contracts);
|
||||||
runtimeContext.setCompiledContracts(_contracts);
|
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
||||||
swap(m_context, runtimeContext);
|
m_context.addMagicGlobal(*variable);
|
||||||
|
|
||||||
registerStateVariables(_contract);
|
registerStateVariables(_contract);
|
||||||
|
}
|
||||||
|
|
||||||
FunctionDefinition* constructor = nullptr;
|
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
{
|
||||||
if (function->getName() == _contract.getName())
|
set<FunctionDefinition const*> neededFunctions;
|
||||||
{
|
FunctionDefinition const* constructor = _contract.getConstructor();
|
||||||
constructor = function.get();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
|
|
||||||
// stack contains sub size
|
|
||||||
if (constructor)
|
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();
|
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
|
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
||||||
unsigned argumentSize = 0;
|
unsigned argumentSize = 0;
|
||||||
for (ASTPointer<VariableDeclaration> const& var: constructor->getParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
||||||
argumentSize += var->getType()->getCalldataEncodedSize();
|
argumentSize += var->getType()->getCalldataEncodedSize();
|
||||||
if (argumentSize > 0)
|
if (argumentSize > 0)
|
||||||
{
|
{
|
||||||
@ -87,18 +102,18 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract,
|
|||||||
m_context.appendProgramSize();
|
m_context.appendProgramSize();
|
||||||
m_context << u256(1); // copy it to byte one as expected for ABI calls
|
m_context << u256(1); // copy it to byte one as expected for ABI calls
|
||||||
m_context << eth::Instruction::CODECOPY;
|
m_context << eth::Instruction::CODECOPY;
|
||||||
appendCalldataUnpacker(*constructor, true);
|
appendCalldataUnpacker(_constructor, true);
|
||||||
}
|
}
|
||||||
//@todo calling other functions inside the constructor should either trigger a parse error
|
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
|
||||||
//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 << returnTag;
|
m_context << returnTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
|
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
|
||||||
m_context << u256(0) << eth::Instruction::RETURN;
|
{
|
||||||
|
CallGraph callgraph;
|
||||||
|
callgraph.addFunction(_constructor);
|
||||||
|
callgraph.computeCallGraph();
|
||||||
|
return callgraph.getCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
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); }
|
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Creates a new compiler context / assembly, packs the current code into the data part and
|
/// Registers the global objects and the non-function objects inside the contract with the context.
|
||||||
/// adds the constructor code.
|
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||||
void packIntoContractCreator(ContractDefinition const& _contract,
|
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
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);
|
void appendFunctionSelector(ContractDefinition const& _contract);
|
||||||
/// Creates code that unpacks the arguments for the given function, from memory if
|
/// 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.
|
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
|
||||||
|
Loading…
Reference in New Issue
Block a user