From e5514becb89c945f59fd440696d0bb3122edbe99 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 7 Mar 2016 16:55:53 +0100 Subject: BREAKING: Implement delegatecall and make default for library calls. --- libsolidity/codegen/Compiler.cpp | 10 ++++------ libsolidity/codegen/Compiler.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 29 ++++++++++++++++++----------- 3 files changed, 23 insertions(+), 18 deletions(-) (limited to 'libsolidity/codegen') 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 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{"send", "call", "callcode"}).count(member)) + else if ((set{"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; -- cgit