aboutsummaryrefslogtreecommitdiffstats
path: root/ExpressionCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ExpressionCompiler.cpp')
-rw-r--r--ExpressionCompiler.cpp207
1 files changed, 139 insertions, 68 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index ac972935..a0b0a54a 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -26,6 +26,7 @@
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
+#include <libsolidity/CompilerUtils.h>
using namespace std;
@@ -174,9 +175,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// explicit type conversion contract -> address, nothing to do.
}
else
- {
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
- }
}
else
{
@@ -185,7 +184,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
- if (function.getLocation() == Location::INTERNAL)
+ switch (function.getLocation())
+ {
+ case Location::INTERNAL:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@@ -201,68 +202,98 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context.appendJump();
m_context << returnLabel;
+ unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label
- m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1);
+ m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
- m_context << eth::Instruction::POP;
+ CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
+ break;
}
- else if (function.getLocation() == Location::EXTERNAL)
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
- else
+ case Location::EXTERNAL:
{
- switch (function.getLocation())
- {
- case Location::SEND:
- m_context << u256(0) << u256(0) << u256(0) << u256(0);
- arguments.front()->accept(*this);
- //@todo might not be necessary
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- _functionCall.getExpression().accept(*this);
- m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
- << eth::Instruction::CALL
- << eth::Instruction::POP;
- break;
- case Location::SUICIDE:
- arguments.front()->accept(*this);
- //@todo might not be necessary
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- m_context << eth::Instruction::SUICIDE;
- break;
- case Location::SHA3:
- arguments.front()->accept(*this);
- appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
- // @todo move this once we actually use memory
- m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
- break;
- case Location::ECRECOVER:
- case Location::SHA256:
- case Location::RIPEMD160:
+ unsigned dataOffset = 1; // reserve one byte for the function index
+ for (unsigned i = 0; i < arguments.size(); ++i)
{
- static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
- {Location::SHA256, 2},
- {Location::RIPEMD160, 3}};
- u256 contractAddress = contractAddresses.find(function.getLocation())->second;
- // @todo later, combine this code with external function call
- for (unsigned i = 0; i < arguments.size(); ++i)
- {
- arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
- // @todo move this once we actually use memory
- m_context << u256(i * 32) << eth::Instruction::MSTORE;
- }
- m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
- << contractAddress << u256(500) //@todo determine actual gas requirement
- << eth::Instruction::CALL
- << eth::Instruction::POP
- << u256(0) << eth::Instruction::MLOAD;
- break;
+ arguments[i]->accept(*this);
+ Type const& type = *function.getParameterTypes()[i];
+ appendTypeConversion(*arguments[i]->getType(), type);
+ unsigned const numBytes = type.getCalldataEncodedSize();
+ if (numBytes == 0 || numBytes > 32)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(arguments[i]->getLocation())
+ << errinfo_comment("Type " + type.toString() + " not yet supported."));
+ if (numBytes != 32)
+ m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
+ m_context << u256(dataOffset) << eth::Instruction::MSTORE;
+ dataOffset += numBytes;
}
- default:
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
+ //@todo only return the first return value for now
+ unsigned retSize = function.getReturnParameterTypes().empty() ? 0
+ : function.getReturnParameterTypes().front()->getCalldataEncodedSize();
+ // CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
+ m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
+ _functionCall.getExpression().accept(*this); // pushes addr and function index
+ m_context << u256(0) << eth::Instruction::MSTORE8
+ << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
+ << eth::Instruction::CALL
+ << eth::Instruction::POP; // @todo do not ignore failure indicator
+ if (retSize == 32)
+ m_context << u256(0) << eth::Instruction::MLOAD;
+ else if (retSize > 0)
+ m_context << (u256(1) << ((32 - retSize) * 8))
+ << u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
+ break;
+ }
+ case Location::SEND:
+ m_context << u256(0) << u256(0) << u256(0) << u256(0);
+ arguments.front()->accept(*this);
+ //@todo might not be necessary
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ _functionCall.getExpression().accept(*this);
+ m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
+ << eth::Instruction::CALL
+ << eth::Instruction::POP;
+ break;
+ case Location::SUICIDE:
+ arguments.front()->accept(*this);
+ //@todo might not be necessary
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ m_context << eth::Instruction::SUICIDE;
+ break;
+ case Location::SHA3:
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
+ // @todo move this once we actually use memory
+ m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
+ break;
+ case Location::ECRECOVER:
+ case Location::SHA256:
+ case Location::RIPEMD160:
+ {
+ static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
+ {Location::SHA256, 2},
+ {Location::RIPEMD160, 3}};
+ u256 contractAddress = contractAddresses.find(function.getLocation())->second;
+ // @todo later, combine this code with external function call
+ for (unsigned i = 0; i < arguments.size(); ++i)
+ {
+ arguments[i]->accept(*this);
+ appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
+ // @todo move this once we actually use memory
+ m_context << u256(i * 32) << eth::Instruction::MSTORE;
}
+ m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
+ << contractAddress << u256(500) //@todo determine actual gas requirement
+ << eth::Instruction::CALL
+ << eth::Instruction::POP
+ << u256(0) << eth::Instruction::MLOAD;
+ break;
+ }
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
}
}
return false;
@@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::CONTRACT:
- // call function
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
+ {
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
+ m_context << type.getFunctionIndex(member);
break;
+ }
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
@@ -323,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
- m_currentLValue = LValue(m_context, LValue::STORAGE);
+ m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
@@ -343,7 +376,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
- m_currentLValue = LValue(m_context, LValue::STORAGE);
+ m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false;
@@ -532,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
}
+ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
+ unsigned _baseStackOffset):
+ m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
+ m_stackSize(_dataType.getSizeOnStack())
+{
+}
+
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
{
switch (m_type)
@@ -542,7 +582,8 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
- *m_context << eth::dupInstruction(stackPos + 1);
+ for (unsigned i = 0; i < m_stackSize; ++i)
+ *m_context << eth::dupInstruction(stackPos + 1);
break;
}
case STORAGE:
@@ -550,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
- *m_context << eth::Instruction::SLOAD;
+ if (m_stackSize == 1)
+ *m_context << eth::Instruction::SLOAD;
+ else
+ for (unsigned i = 0; i < m_stackSize; ++i)
+ {
+ *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
+ if (i + 1 < m_stackSize)
+ *m_context << u256(1) << eth::Instruction::ADD;
+ else
+ *m_context << eth::Instruction::POP;
+ }
break;
case MEMORY:
if (!_expression.getType()->isValueType())
@@ -571,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{
case STACK:
{
- unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
- if (stackPos > 16)
+ unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
+ if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
- else if (stackPos > 0)
- *m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
+ else if (stackDiff > 0)
+ for (unsigned i = 0; i < m_stackSize; ++i)
+ *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
retrieveValue(_expression);
break;
@@ -584,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
case LValue::STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
- if (!_move)
- *m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
- *m_context << eth::Instruction::SSTORE;
+ // stack layout: value value ... value ref
+ if (!_move) // copy values
+ {
+ if (m_stackSize + 1 > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ << errinfo_comment("Stack too deep."));
+ for (unsigned i = 0; i < m_stackSize; ++i)
+ *m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
+ }
+ if (m_stackSize > 0) // store high index value first
+ *m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
+ for (unsigned i = 0; i < m_stackSize; ++i)
+ {
+ if (i + 1 >= m_stackSize)
+ *m_context << eth::Instruction::SSTORE;
+ else
+ // v v ... v v r+x
+ *m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
+ << eth::Instruction::SSTORE
+ << u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
+ }
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
@@ -612,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
+ m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration))
{
m_type = STACK;