diff options
author | chriseth <c@ethdev.com> | 2016-03-07 23:55:53 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-03-12 00:49:32 +0800 |
commit | e5514becb89c945f59fd440696d0bb3122edbe99 (patch) | |
tree | 6358ae151c50802b3cb0c158f7b8b06c53669fd4 /libsolidity | |
parent | 60a21c6487743578af6fd4e1540a36a2b80fcac7 (diff) | |
download | dexon-solidity-e5514becb89c945f59fd440696d0bb3122edbe99.tar.gz dexon-solidity-e5514becb89c945f59fd440696d0bb3122edbe99.tar.zst dexon-solidity-e5514becb89c945f59fd440696d0bb3122edbe99.zip |
BREAKING: Implement delegatecall and make default for library calls.
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/ast/Types.cpp | 47 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 4 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.cpp | 10 | ||||
-rw-r--r-- | libsolidity/codegen/Compiler.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 29 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 2 |
6 files changed, 51 insertions, 43 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index bca83d59..0696b908 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -333,6 +333,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons {"balance", make_shared<IntegerType >(256)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} }; else @@ -1561,9 +1562,9 @@ unsigned FunctionType::sizeOnStack() const } unsigned size = 0; - if (location == Location::External || location == Location::CallCode) + if (location == Location::External || location == Location::CallCode || location == Location::DelegateCall) size = 2; - else if (location == Location::Bare || location == Location::BareCallCode) + else if (location == Location::Bare || location == Location::BareCallCode || location == Location::BareDelegateCall) size = 1; else if (location == Location::Internal) size = 1; @@ -1619,9 +1620,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Location::RIPEMD160: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: { - MemberList::MemberMap members{ - { + MemberList::MemberMap members; + if (m_location != Location::BareDelegateCall) + members.push_back(MemberList::Member( "value", make_shared<FunctionType>( parseElementaryTypeVector({"uint"}), @@ -1634,25 +1637,22 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con m_gasSet, m_valueSet ) - } - }; + )); if (m_location != Location::Creation) - members.push_back( - MemberList::Member( - "gas", - make_shared<FunctionType>( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(true, false)}, - strings(), - strings(), - Location::SetGas, - false, - nullptr, - m_gasSet, - m_valueSet - ) + members.push_back(MemberList::Member( + "gas", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(true, false)}, + strings(), + strings(), + Location::SetGas, + false, + nullptr, + m_gasSet, + m_valueSet ) - ); + )); return members; } default: @@ -1700,6 +1700,7 @@ bool FunctionType::isBareCall() const { case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: case Location::ECRecover: case Location::SHA256: case Location::RIPEMD160: @@ -1785,7 +1786,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) returnParameterTypes, m_parameterNames, returnParameterNames, - _inLibrary ? Location::CallCode : m_location, + _inLibrary ? Location::DelegateCall : m_location, m_arbitraryParameters, m_declaration, m_gasSet, @@ -1884,7 +1885,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current for (auto const& it: contract.interfaceFunctions()) members.push_back(MemberList::Member( it.second->declaration().name(), - it.second->asMemberFunction(true), // use callcode + it.second->asMemberFunction(true), &it.second->declaration() )); if (isBase) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b4a2d573..189dd10a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -732,8 +732,10 @@ public: Internal, ///< stack-call using plain JUMP External, ///< external call using CALL CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage + DelegateCall, ///< extercnal call using DELEGATECALL, i.e. not exchanging the storage Bare, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash + BareDelegateCall, ///< DELEGATECALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas SHA3, ///< SHA3 @@ -869,7 +871,7 @@ public: /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. - /// @param _inLibrary if true, uses CallCode as location. + /// @param _inLibrary if true, uses DelegateCall as location. /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 18803b71..c7eb71a8 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -773,15 +773,13 @@ eth::Assembly Compiler::cloneRuntime() 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; - // unfortunately, we have to send the value again, so that CALLVALUE returns the correct value - // in the callcoded contract. - a << u256(0) << eth::Instruction::CALLVALUE; + a << u256(0); // this is the address which has to be substituted by the linker. //@todo implement as special "marker" AssemblyItem. a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(schedule.callGas + schedule.callValueTransferGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; - a << eth::Instruction::CALLCODE; - //Propagate error condition (if CALLCODE pushes 0 on stack). + a << u256(schedule.callGas + 10) << eth::Instruction::GAS << eth::Instruction::SUB; + a << eth::Instruction::DELEGATECALL; + //Propagate error condition (if DELEGATECALL pushes 0 on stack). a << eth::Instruction::ISZERO; a.appendJumpI(a.errorTag()); //@todo adjust for larger return values, make this dynamic. diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 9d069f7c..fa33bd30 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -44,7 +44,7 @@ public: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts ); - /// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given + /// Compiles a contract that uses DELEGATECALL 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, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58db07b1..e0b2b5f6 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -465,8 +465,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { FunctionType const& function = *functionType; if (function.bound()) - // Only callcode functions can be bound, this might be lifted later. - solAssert(function.location() == Location::CallCode, ""); + // Only delegatecall functions can be bound, this might be lifted later. + solAssert(function.location() == Location::DelegateCall, ""); switch (function.location()) { case Location::Internal: @@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case Location::External: case Location::CallCode: + case Location::DelegateCall: case Location::Bare: case Location::BareCallCode: + case Location::BareDelegateCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; @@ -875,7 +877,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ); m_context << eth::Instruction::BALANCE; } - else if ((set<string>{"send", "call", "callcode"}).count(member)) + else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), @@ -1356,6 +1358,7 @@ void ExpressionCompiler::appendExternalFunctionCall( FunctionKind funKind = _functionType.location(); bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode; bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode; + bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall; unsigned retSize = 0; if (returnSuccessCondition) @@ -1371,13 +1374,13 @@ void ExpressionCompiler::appendExternalFunctionCall( TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); bool manualFunctionId = - (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && + (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && !_arguments.empty() && _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { - // If we have a BareCall or BareCallCode and the first type has exactly 4 bytes, use it as + // If we have a Bare* and the first type has exactly 4 bytes, use it as // function identifier. _arguments.front()->accept(*this); utils().convertType( @@ -1416,7 +1419,7 @@ void ExpressionCompiler::appendExternalFunctionCall( parameterTypes, _functionType.padArguments(), _functionType.takesArbitraryParameters(), - isCallCode + isCallCode || isDelegateCall ); // Stack now: @@ -1435,8 +1438,10 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::Instruction::DUP2; // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) - // value, addr, gas (stack top) - if (_functionType.valueSet()) + // [value,] addr, gas (stack top) + if (isDelegateCall) + solAssert(!_functionType.valueSet(), "Value set for delegatecall"); + else if (_functionType.valueSet()) m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else m_context << u256(0); @@ -1446,20 +1451,22 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); else { - eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context. + eth::EVMSchedule schedule; // send all gas except the amount needed to execute "SUB" and "CALL" // @todo this retains too much gas for now, needs to be fine-tuned. u256 gasNeededByCaller = schedule.callGas + 10; if (_functionType.valueSet()) gasNeededByCaller += schedule.callValueTransferGas; - if (!isCallCode) + if (!isCallCode && !isDelegateCall) gasNeededByCaller += schedule.callNewAccountGas; // we never know m_context << gasNeededByCaller << eth::Instruction::GAS << eth::Instruction::SUB; } - if (isCallCode) + if (isDelegateCall) + m_context << eth::Instruction::DELEGATECALL; + else if (isCallCode) m_context << eth::Instruction::CALLCODE; else m_context << eth::Instruction::CALL; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 517d0055..1d13a23c 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -124,7 +124,7 @@ public: eth::LinkerObject const& object(std::string const& _contractName = "") const; /// @returns the runtime object for the contract. eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; - /// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE. + /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. /// 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. |