diff options
-rw-r--r-- | Compiler.cpp | 88 | ||||
-rw-r--r-- | Compiler.h | 14 | ||||
-rw-r--r-- | CompilerStack.cpp | 11 | ||||
-rw-r--r-- | CompilerStack.h | 6 |
4 files changed, 99 insertions, 20 deletions
diff --git a/Compiler.cpp b/Compiler.cpp index 6ed6480f..eadfc204 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -25,6 +25,7 @@ #include <boost/range/adaptor/reversed.hpp> #include <libevmcore/Instruction.h> #include <libevmasm/Assembly.h> +#include <libevmcore/Params.h> #include <libsolidity/AST.h> #include <libsolidity/ExpressionCompiler.h> #include <libsolidity/CompilerUtils.h> @@ -53,31 +54,45 @@ void Compiler::compileContract(ContractDefinition const& _contract, m_context = CompilerContext(); // clear it just in case { CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract); - CompilerUtils(m_context).initialiseFreeMemoryPointer(); initializeContext(_contract, _contracts); appendFunctionSelector(_contract); - 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(); - } + appendFunctionsWithoutCode(); } // Swap the runtime context with the creation-time context swap(m_context, m_runtimeContext); CompilerContext::LocationSetter locationSetterCreationTime(m_context, _contract); - CompilerUtils(m_context).initialiseFreeMemoryPointer(); initializeContext(_contract, _contracts); packIntoContractCreator(_contract, m_runtimeContext); if (m_optimize) 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 { return m_runtimeContext.getFunctionEntryLabelIfExists(_function); @@ -86,13 +101,14 @@ eth::AssemblyItem Compiler::getFunctionEntryLabel(FunctionDefinition const& _fun void Compiler::initializeContext(ContractDefinition const& _contract, map<ContractDefinition const*, bytes const*> const& _contracts) { + CompilerUtils(m_context).initialiseFreeMemoryPointer(); m_context.setCompiledContracts(_contracts); m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts()); registerStateVariables(_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. std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); @@ -126,22 +142,22 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp appendConstructor(*constructor); else if (auto c = m_context.getNextConstructor(_contract)) appendBaseConstructor(*c); +} + +void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) +{ + appendInitAndConstructorCode(_contract); eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.getAssembly()); 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; // note that we have to include the functions again because of absolute jump labels - set<Declaration const*> functions = m_context.getFunctionsWithoutCode(); - while (!functions.empty()) - { - for (Declaration const* function: functions) - function->accept(*this); - functions = m_context.getFunctionsWithoutCode(); - } + appendFunctionsWithoutCode(); } void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) @@ -618,6 +634,20 @@ bool Compiler::visit(PlaceholderStatement const& _placeholderStatement) 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() { solAssert(m_currentFunction, ""); @@ -674,3 +704,21 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons if (_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; +} @@ -44,6 +44,12 @@ public: void compileContract(ContractDefinition const& _contract, 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 getRuntimeBytecode() { return m_context.getAssembledRuntimeBytecode(m_runtimeSub); } /// @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 /// with a new and initialized context. Adds the constructor code. 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 appendConstructor(FunctionDefinition const& _constructor); void appendFunctionSelector(ContractDefinition const& _contract); @@ -103,6 +111,9 @@ private: virtual bool visit(ExpressionStatement const& _expressionStatement) 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 /// body itself if the last modifier was reached. void appendModifierOrFunctionCode(); @@ -110,6 +121,9 @@ private: void appendStackVariableInitialisation(VariableDeclaration const& _variable); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); + /// @returns the runtime assembly for clone contracts. + static eth::Assembly getCloneRuntime(); + bool const m_optimize; unsigned const m_optimizeRuns; CompilerContext m_context; diff --git a/CompilerStack.cpp b/CompilerStack.cpp index f056bb9b..a85738eb 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -166,7 +166,13 @@ void CompilerStack::compile(bool _optimize, unsigned _runs) compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); compiledContract.compiler = move(compiler); + compiler = make_shared<Compiler>(_optimize, _runs); + compiler->compileContract(*contract, contractBytecode); 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; } +bytes const& CompilerStack::getCloneBytecode(string const& _contractName) const +{ + return getContract(_contractName).cloneBytecode; +} + dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const { return dev::sha3(getRuntimeBytecode(_contractName)); diff --git a/CompilerStack.h b/CompilerStack.h index a7c6ea3b..735c4d15 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -99,6 +99,11 @@ public: 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. 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 eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const; /// @returns runtime contract assembly items @@ -167,6 +172,7 @@ private: std::shared_ptr<Compiler> compiler; bytes bytecode; bytes runtimeBytecode; + bytes cloneBytecode; std::shared_ptr<InterfaceHandler> interfaceHandler; mutable std::unique_ptr<std::string const> interface; mutable std::unique_ptr<std::string const> solidityInterface; |