diff options
Diffstat (limited to 'libsolidity/codegen/ContractCompiler.cpp')
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 71 |
1 files changed, 68 insertions, 3 deletions
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a81ba518..f463db94 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -64,6 +64,12 @@ void ContractCompiler::compileContract( ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); + + if (_contract.isLibrary()) + // Check whether this is a call (true) or a delegatecall (false). + // This has to be the first code in the contract. + appendDelegatecallCheck(); + initializeContext(_contract, _contracts); appendFunctionSelector(_contract); appendMissingFunctions(); @@ -75,8 +81,13 @@ size_t ContractCompiler::compileConstructor( ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); - initializeContext(_contract, _contracts); - return packIntoContractCreator(_contract); + if (_contract.isLibrary()) + return deployLibrary(_contract); + else + { + initializeContext(_contract, _contracts); + return packIntoContractCreator(_contract); + } } size_t ContractCompiler::compileClone( @@ -122,6 +133,7 @@ void ContractCompiler::appendCallValueCheck() void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { + solAssert(!_contract.isLibrary(), "Tried to initialize library."); CompilerContext::LocationSetter locationSetter(m_context, _contract); // Determine the arguments that are used for the base constructors. std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; @@ -163,6 +175,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract) { solAssert(!!m_runtimeCompiler, ""); + solAssert(!_contract.isLibrary(), "Tried to use contract creator or library."); appendInitAndConstructorCode(_contract); @@ -188,6 +201,34 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont return m_context.runtimeSub(); } +size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract) +{ + solAssert(!!m_runtimeCompiler, ""); + solAssert(_contract.isLibrary(), "Tried to deploy contract as library."); + + CompilerContext::LocationSetter locationSetter(m_context, _contract); + + solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); + m_context.pushSubroutineSize(m_context.runtimeSub()); + m_context.pushSubroutineOffset(m_context.runtimeSub()); + m_context.appendInlineAssembly(R"( + { + // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data + // without the need for a shift. + let codepos := 11 + codecopy(codepos, subOffset, subSize) + // Check that the first opcode is a PUSH20 + switch eq(0x73, byte(0, mload(codepos))) + case 0 { invalid() } + mstore(0, address()) + mstore8(codepos, 0x73) + return(codepos, subSize) + } + )", {"subSize", "subOffset"}); + + return m_context.runtimeSub(); +} + void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor) { CompilerContext::LocationSetter locationSetter(m_context, _constructor); @@ -244,11 +285,26 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) _constructor.accept(*this); } +void ContractCompiler::appendDelegatecallCheck() +{ + // Special constant that will be replaced by the address at deploy time. + // At compilation time, this is just "PUSH20 00...000". + m_context.appendDeployTimeAddress(); + m_context << Instruction::ADDRESS << Instruction::EQ; + // The result on the stack is + // "We have not been called via DELEGATECALL". +} + void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) { map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; + if (_contract.isLibrary()) + { + solAssert(m_context.stackHeight() == 1, "CALL / DELEGATECALL flag expected."); + } + FunctionDefinition const* fallback = _contract.fallbackFunction(); eth::AssemblyItem notFound = m_context.newTag(); // directly jump to fallback if the data is too short to contain a function selector @@ -260,7 +316,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac if (!interfaceFunctions.empty()) CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); - // stack now is: 1 0 <funhash> + // stack now is: <can-call-non-view-functions>? <funhash> for (auto const& it: interfaceFunctions) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); @@ -272,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << notFound; if (fallback) { + solAssert(!_contract.isLibrary(), ""); if (!fallback->isPayable()) appendCallValueCheck(); @@ -291,6 +348,13 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); m_context << callDataUnpackerEntryPoints.at(it.first); + if (_contract.isLibrary() && functionType->stateMutability() > StateMutability::View) + { + // If the function is not a view function and is called without DELEGATECALL, + // we revert. + m_context << dupInstruction(2); + m_context.appendConditionalRevert(); + } m_context.setStackOffset(0); // We have to allow this for libraries, because value of the previous // call is still visible in the delegatecall. @@ -441,6 +505,7 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract) { + solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); for (VariableDeclaration const* variable: _contract.stateVariables()) if (variable->value() && !variable->isConstant()) ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable); |