mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Create and output clone contracts.
This commit is contained in:
parent
f2f1e03007
commit
943fd623e1
88
Compiler.cpp
88
Compiler.cpp
@ -25,6 +25,7 @@
|
|||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmcore/Instruction.h>
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
|
#include <libevmcore/Params.h>
|
||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
#include <libsolidity/ExpressionCompiler.h>
|
#include <libsolidity/ExpressionCompiler.h>
|
||||||
#include <libsolidity/CompilerUtils.h>
|
#include <libsolidity/CompilerUtils.h>
|
||||||
@ -53,31 +54,45 @@ void Compiler::compileContract(ContractDefinition const& _contract,
|
|||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
|
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
|
||||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
|
||||||
initializeContext(_contract, _contracts);
|
initializeContext(_contract, _contracts);
|
||||||
appendFunctionSelector(_contract);
|
appendFunctionSelector(_contract);
|
||||||
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
appendFunctionsWithoutCode();
|
||||||
while (!functions.empty())
|
|
||||||
{
|
|
||||||
for (Declaration const* function: functions)
|
|
||||||
{
|
|
||||||
m_context.setStackOffset(0);
|
|
||||||
function->accept(*this);
|
|
||||||
}
|
|
||||||
functions = m_context.getFunctionsWithoutCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap the runtime context with the creation-time context
|
// Swap the runtime context with the creation-time context
|
||||||
swap(m_context, m_runtimeContext);
|
swap(m_context, m_runtimeContext);
|
||||||
CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
|
CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract);
|
||||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
|
||||||
initializeContext(_contract, _contracts);
|
initializeContext(_contract, _contracts);
|
||||||
packIntoContractCreator(_contract, m_runtimeContext);
|
packIntoContractCreator(_contract, m_runtimeContext);
|
||||||
if (m_optimize)
|
if (m_optimize)
|
||||||
m_context.optimise(m_optimizeRuns);
|
m_context.optimise(m_optimizeRuns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compiler::compileClone(
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
map<ContractDefinition const*, bytes const*> const& _contracts
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_context = CompilerContext(); // clear it just in case
|
||||||
|
initializeContext(_contract, _contracts);
|
||||||
|
|
||||||
|
appendInitAndConstructorCode(_contract);
|
||||||
|
|
||||||
|
//@todo determine largest return size of all runtime functions
|
||||||
|
eth::AssemblyItem runtimeSub = m_context.addSubroutine(getCloneRuntime());
|
||||||
|
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
|
||||||
|
m_runtimeSub = size_t(runtimeSub.data());
|
||||||
|
|
||||||
|
// stack contains sub size
|
||||||
|
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
|
||||||
|
m_context << u256(0) << eth::Instruction::RETURN;
|
||||||
|
|
||||||
|
appendFunctionsWithoutCode();
|
||||||
|
|
||||||
|
if (m_optimize)
|
||||||
|
m_context.optimise(m_optimizeRuns);
|
||||||
|
}
|
||||||
|
|
||||||
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
|
eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _function) const
|
||||||
{
|
{
|
||||||
return m_runtimeContext.getFunctionEntryLabelIfExists(_function);
|
return m_runtimeContext.getFunctionEntryLabelIfExists(_function);
|
||||||
@ -86,13 +101,14 @@ eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _fun
|
|||||||
void Compiler::initializeContext(ContractDefinition const& _contract,
|
void Compiler::initializeContext(ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, bytes const*> const& _contracts)
|
map<ContractDefinition const*, bytes const*> const& _contracts)
|
||||||
{
|
{
|
||||||
|
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
||||||
m_context.setCompiledContracts(_contracts);
|
m_context.setCompiledContracts(_contracts);
|
||||||
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
|
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
|
||||||
registerStateVariables(_contract);
|
registerStateVariables(_contract);
|
||||||
m_context.resetVisitedNodes(&_contract);
|
m_context.resetVisitedNodes(&_contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
// Determine the arguments that are used for the base constructors.
|
// Determine the arguments that are used for the base constructors.
|
||||||
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
|
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
|
||||||
@ -126,22 +142,22 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
|
|||||||
appendConstructor(*constructor);
|
appendConstructor(*constructor);
|
||||||
else if (auto c = m_context.getNextConstructor(_contract))
|
else if (auto c = m_context.getNextConstructor(_contract))
|
||||||
appendBaseConstructor(*c);
|
appendBaseConstructor(*c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
||||||
|
{
|
||||||
|
appendInitAndConstructorCode(_contract);
|
||||||
|
|
||||||
eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly());
|
eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly());
|
||||||
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
|
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
|
||||||
m_runtimeSub = size_t(runtimeSub.data());
|
m_runtimeSub = size_t(runtimeSub.data());
|
||||||
|
|
||||||
// stack contains sub size
|
// stack contains sub size
|
||||||
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
|
m_context << eth::Instruction::DUP1 << runtimeSub << u256(0) << eth::Instruction::CODECOPY;
|
||||||
m_context << u256(0) << eth::Instruction::RETURN;
|
m_context << u256(0) << eth::Instruction::RETURN;
|
||||||
|
|
||||||
// note that we have to include the functions again because of absolute jump labels
|
// note that we have to include the functions again because of absolute jump labels
|
||||||
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
appendFunctionsWithoutCode();
|
||||||
while (!functions.empty())
|
|
||||||
{
|
|
||||||
for (Declaration const* function: functions)
|
|
||||||
function->accept(*this);
|
|
||||||
functions = m_context.getFunctionsWithoutCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
||||||
@ -618,6 +634,20 @@ bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compiler::appendFunctionsWithoutCode()
|
||||||
|
{
|
||||||
|
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
|
||||||
|
while (!functions.empty())
|
||||||
|
{
|
||||||
|
for (Declaration const* function: functions)
|
||||||
|
{
|
||||||
|
m_context.setStackOffset(0);
|
||||||
|
function->accept(*this);
|
||||||
|
}
|
||||||
|
functions = m_context.getFunctionsWithoutCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::appendModifierOrFunctionCode()
|
void Compiler::appendModifierOrFunctionCode()
|
||||||
{
|
{
|
||||||
solAssert(m_currentFunction, "");
|
solAssert(m_currentFunction, "");
|
||||||
@ -674,3 +704,21 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons
|
|||||||
if (_targetType)
|
if (_targetType)
|
||||||
CompilerUtils(m_context).convertType(*_expression.getType(), *_targetType);
|
CompilerUtils(m_context).convertType(*_expression.getType(), *_targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eth::Assembly Compiler::getCloneRuntime()
|
||||||
|
{
|
||||||
|
eth::Assembly a;
|
||||||
|
a << eth::Instruction::CALLDATASIZE;
|
||||||
|
a << u256(0) << eth::Instruction::DUP1 << eth::Instruction::CALLDATACOPY;
|
||||||
|
//@todo adjust for larger return values, make this dynamic.
|
||||||
|
a << u256(0x20) << u256(0) << eth::Instruction::CALLDATASIZE;
|
||||||
|
a << u256(0) << eth::Instruction::DUP1;
|
||||||
|
// this is the address which has to be substituted by the linker.
|
||||||
|
//@todo implement as special "marker" AssemblyItem.
|
||||||
|
a << u256("0xcafecafecafecafecafecafecafecafecafecafe");
|
||||||
|
a << u256(eth::c_callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB;
|
||||||
|
a << eth::Instruction::CALLCODE;
|
||||||
|
//@todo adjust for larger return values, make this dynamic.
|
||||||
|
a << u256(0x20) << u256(0) << eth::Instruction::RETURN;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
14
Compiler.h
14
Compiler.h
@ -44,6 +44,12 @@ public:
|
|||||||
|
|
||||||
void compileContract(ContractDefinition const& _contract,
|
void compileContract(ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
||||||
|
/// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given
|
||||||
|
/// contract at runtime, but contains the full creation-time code.
|
||||||
|
void compileClone(
|
||||||
|
ContractDefinition const& _contract,
|
||||||
|
std::map<ContractDefinition const*, bytes const*> const& _contracts
|
||||||
|
);
|
||||||
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
|
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(); }
|
||||||
bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
|
bytes getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); }
|
||||||
/// @arg _sourceCodes is the map of input files to source code strings
|
/// @arg _sourceCodes is the map of input files to source code strings
|
||||||
@ -68,6 +74,8 @@ private:
|
|||||||
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
/// 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 packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
||||||
|
/// Appends state variable initialisation and constructor code.
|
||||||
|
void appendInitAndConstructorCode(ContractDefinition const& _contract);
|
||||||
void appendBaseConstructor(FunctionDefinition const& _constructor);
|
void appendBaseConstructor(FunctionDefinition const& _constructor);
|
||||||
void appendConstructor(FunctionDefinition const& _constructor);
|
void appendConstructor(FunctionDefinition const& _constructor);
|
||||||
void appendFunctionSelector(ContractDefinition const& _contract);
|
void appendFunctionSelector(ContractDefinition const& _contract);
|
||||||
@ -103,6 +111,9 @@ private:
|
|||||||
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||||
virtual bool visit(PlaceholderStatement const&) override;
|
virtual bool visit(PlaceholderStatement const&) override;
|
||||||
|
|
||||||
|
/// Repeatedly visits all function which are referenced but which are not compiled yet.
|
||||||
|
void appendFunctionsWithoutCode();
|
||||||
|
|
||||||
/// Appends one layer of function modifier code of the current function, or the function
|
/// Appends one layer of function modifier code of the current function, or the function
|
||||||
/// body itself if the last modifier was reached.
|
/// body itself if the last modifier was reached.
|
||||||
void appendModifierOrFunctionCode();
|
void appendModifierOrFunctionCode();
|
||||||
@ -110,6 +121,9 @@ private:
|
|||||||
void appendStackVariableInitialisation(VariableDeclaration const& _variable);
|
void appendStackVariableInitialisation(VariableDeclaration const& _variable);
|
||||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
||||||
|
|
||||||
|
/// @returns the runtime assembly for clone contracts.
|
||||||
|
static eth::Assembly getCloneRuntime();
|
||||||
|
|
||||||
bool const m_optimize;
|
bool const m_optimize;
|
||||||
unsigned const m_optimizeRuns;
|
unsigned const m_optimizeRuns;
|
||||||
CompilerContext m_context;
|
CompilerContext m_context;
|
||||||
|
@ -166,7 +166,13 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
|
|||||||
compiledContract.bytecode = compiler->getAssembledBytecode();
|
compiledContract.bytecode = compiler->getAssembledBytecode();
|
||||||
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
|
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
|
||||||
compiledContract.compiler = move(compiler);
|
compiledContract.compiler = move(compiler);
|
||||||
|
compiler = make_shared<Compiler>(_optimize, _runs);
|
||||||
|
compiler->compileContract(*contract, contractBytecode);
|
||||||
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
||||||
|
|
||||||
|
Compiler cloneCompiler(_optimize, _runs);
|
||||||
|
cloneCompiler.compileClone(*contract, contractBytecode);
|
||||||
|
compiledContract.cloneBytecode = cloneCompiler.getAssembledBytecode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,6 +205,11 @@ bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) cons
|
|||||||
return getContract(_contractName).runtimeBytecode;
|
return getContract(_contractName).runtimeBytecode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes const& CompilerStack::getCloneBytecode(string const& _contractName) const
|
||||||
|
{
|
||||||
|
return getContract(_contractName).cloneBytecode;
|
||||||
|
}
|
||||||
|
|
||||||
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
|
dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return dev::sha3(getRuntimeBytecode(_contractName));
|
return dev::sha3(getRuntimeBytecode(_contractName));
|
||||||
|
@ -99,6 +99,11 @@ public:
|
|||||||
bytes const& getBytecode(std::string const& _contractName = "") const;
|
bytes const& getBytecode(std::string const& _contractName = "") const;
|
||||||
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
/// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
|
||||||
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
|
bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
|
||||||
|
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
|
||||||
|
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
|
||||||
|
/// substituted by the actual address. Note that this sequence starts end ends in three X
|
||||||
|
/// characters but can contain anything in between.
|
||||||
|
bytes const& getCloneBytecode(std::string const& _contractName = "") const;
|
||||||
/// @returns normal contract assembly items
|
/// @returns normal contract assembly items
|
||||||
eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
|
eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const;
|
||||||
/// @returns runtime contract assembly items
|
/// @returns runtime contract assembly items
|
||||||
@ -167,6 +172,7 @@ private:
|
|||||||
std::shared_ptr<Compiler> compiler;
|
std::shared_ptr<Compiler> compiler;
|
||||||
bytes bytecode;
|
bytes bytecode;
|
||||||
bytes runtimeBytecode;
|
bytes runtimeBytecode;
|
||||||
|
bytes cloneBytecode;
|
||||||
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
||||||
mutable std::unique_ptr<std::string const> interface;
|
mutable std::unique_ptr<std::string const> interface;
|
||||||
mutable std::unique_ptr<std::string const> solidityInterface;
|
mutable std::unique_ptr<std::string const> solidityInterface;
|
||||||
|
Loading…
Reference in New Issue
Block a user