aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen/ExpressionCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen/ExpressionCompiler.cpp')
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp111
1 files changed, 90 insertions, 21 deletions
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index b973a117..3d05edd3 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolidity/ast/AST.h>
@@ -297,9 +298,6 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::BitNot: // ~
m_context << Instruction::NOT;
break;
- case Token::After: // after
- m_context << Instruction::TIMESTAMP << Instruction::ADD;
- break;
case Token::Delete: // delete
solAssert(!!m_currentLValue, "LValue not retrieved.");
m_currentLValue->setToZero(_unaryOperation.location());
@@ -536,6 +534,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
m_context << u256(0);
m_context << Instruction::CREATE;
+ // Check if zero (out of stack or not enough balance).
+ m_context << Instruction::DUP1 << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
break;
@@ -567,12 +568,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::Send:
_functionCall.expression().accept(*this);
- m_context << u256(0); // do not send gas (there still is the stipend)
+ // Provide the gas stipend manually at first because we may send zero ether.
+ // Will be zeroed if we send more than zero ether.
+ m_context << u256(eth::GasCosts::callStipend);
arguments.front()->accept(*this);
utils().convertType(
*arguments.front()->annotation().type,
*function.parameterTypes().front(), true
);
+ // gas <- gas * !value
+ m_context << Instruction::SWAP1 << Instruction::DUP2;
+ m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1;
appendExternalFunctionCall(
FunctionType(
TypePointers{},
@@ -582,6 +588,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
Location::Bare,
false,
nullptr,
+ false,
+ false,
true,
true
),
@@ -662,7 +670,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
if (!event.isAnonymous())
{
- m_context << u256(h256::Arith(dev::sha3(function.externalSignature())));
+ m_context << u256(h256::Arith(dev::keccak256(function.externalSignature())));
++numIndexed;
}
solAssert(numIndexed <= 4, "Too many indexed arguments.");
@@ -792,15 +800,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().storeFreeMemoryPointer();
// Stack: memptr requested_length
+ // Check if length is zero
+ m_context << Instruction::DUP1 << Instruction::ISZERO;
+ auto skipInit = m_context.appendConditionalJump();
+
// We only have to initialise if the base type is a not a value type.
if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
{
m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
utils().zeroInitialiseMemoryArray(arrayType);
- m_context << Instruction::POP;
}
- else
- m_context << Instruction::POP;
+ m_context << skipInit;
+ m_context << Instruction::POP;
break;
}
default:
@@ -1320,11 +1331,18 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
m_context << Instruction::MUL;
break;
case Token::Div:
- m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
- break;
case Token::Mod:
- m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
+ {
+ // Test for division by zero
+ m_context << Instruction::DUP2 << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+
+ if (_operator == Token::Div)
+ m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
+ else
+ m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
break;
+ }
case Token::Exp:
m_context << Instruction::EXP;
break;
@@ -1445,6 +1463,31 @@ void ExpressionCompiler::appendExternalFunctionCall(
argumentTypes.push_back(_arguments[i]->annotation().type);
}
+ if (funKind == FunctionKind::ECRecover)
+ {
+ // Clears 32 bytes of currently free memory and advances free memory pointer.
+ // Output area will be "start of input area" - 32.
+ // The reason is that a failing ECRecover cannot be detected, it will just return
+ // zero bytes (which we cannot detect).
+ solAssert(0 < retSize && retSize <= 32, "");
+ utils().fetchFreeMemoryPointer();
+ m_context << Instruction::DUP1 << u256(0) << Instruction::MSTORE;
+ m_context << u256(32) << Instruction::ADD;
+ utils().storeFreeMemoryPointer();
+ }
+
+ // Touch the end of the output area so that we do not pay for memory resize during the call
+ // (which we would have to subtract from the gas left)
+ // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
+ // would remove that, so we use MSTORE here.
+ if (!_functionType.gasSet() && retSize > 0)
+ {
+ m_context << u256(0);
+ utils().fetchFreeMemoryPointer();
+ // This touches too much, but that way we save some rounding arithmetics
+ m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
+ }
+
// Copy function identifier to memory.
utils().fetchFreeMemoryPointer();
if (!_functionType.isBareCall() || manualFunctionId)
@@ -1453,7 +1496,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
}
// If the function takes arbitrary parameters, copy dynamic length data in place.
- // Move argumenst to memory, will not update the free memory pointer (but will update the memory
+ // Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
utils().encodeToMemory(
argumentTypes,
@@ -1471,12 +1514,24 @@ void ExpressionCompiler::appendExternalFunctionCall(
// function identifier [unless bare]
// contract address
- // Output data will replace input data.
+ // Output data will replace input data, unless we have ECRecover (then, output
+ // area will be 32 bytes just before input area).
// put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input>
m_context << u256(retSize);
- utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB;
- m_context << Instruction::DUP2;
+ utils().fetchFreeMemoryPointer(); // This is the start of input
+ if (funKind == FunctionKind::ECRecover)
+ {
+ // In this case, output is 32 bytes before input and has already been cleared.
+ m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1;
+ // Here: <input end> <output size> <outpos> <input pos>
+ m_context << Instruction::DUP1 << Instruction::DUP5 << Instruction::SUB;
+ m_context << Instruction::SWAP1;
+ }
+ else
+ {
+ m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB;
+ m_context << Instruction::DUP2;
+ }
// CALL arguments: outSize, outOff, inSize, inOff (already present up to here)
// [value,] addr, gas (stack top)
@@ -1488,6 +1543,15 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
+ bool existenceChecked = false;
+ // Check the the target contract exists (has code) for non-low-level calls.
+ if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall)
+ {
+ m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ existenceChecked = true;
+ }
+
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
@@ -1497,12 +1561,9 @@ void ExpressionCompiler::appendExternalFunctionCall(
u256 gasNeededByCaller = eth::GasCosts::callGas + 10;
if (_functionType.valueSet())
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
- if (!isCallCode && !isDelegateCall)
+ if (!isCallCode && !isDelegateCall && !existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
- m_context <<
- gasNeededByCaller <<
- Instruction::GAS <<
- Instruction::SUB;
+ m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
}
if (isDelegateCall)
m_context << Instruction::DELEGATECALL;
@@ -1539,6 +1600,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().loadFromMemoryDynamic(IntegerType(160), false, true, false);
utils().convertType(IntegerType(160), FixedBytesType(20));
}
+ else if (funKind == FunctionKind::ECRecover)
+ {
+ // Output is 32 bytes before input / free mem pointer.
+ // Failing ecrecover cannot be detected, so we clear output before the call.
+ m_context << u256(32);
+ utils().fetchFreeMemoryPointer();
+ m_context << Instruction::SUB << Instruction::MLOAD;
+ }
else if (!_functionType.returnParameterTypes().empty())
{
utils().fetchFreeMemoryPointer();