2014-10-30 00:20:32 +00:00
/*
This file is part of cpp - ethereum .
cpp - ethereum is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
cpp - ethereum is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with cpp - ethereum . If not , see < http : //www.gnu.org/licenses/>.
*/
/**
* @ author Christian < c @ ethdev . com >
* @ date 2014
* Solidity AST to EVM bytecode compiler for expressions .
*/
# include <utility>
# include <numeric>
2014-11-10 16:31:09 +00:00
# include <libdevcore/Common.h>
2014-10-30 00:20:32 +00:00
# include <libsolidity/AST.h>
# include <libsolidity/ExpressionCompiler.h>
2014-10-30 11:42:04 +00:00
# include <libsolidity/CompilerContext.h>
2014-12-08 21:18:19 +00:00
# include <libsolidity/CompilerUtils.h>
2014-10-30 00:20:32 +00:00
using namespace std ;
2014-12-17 15:23:18 +00:00
namespace dev
{
namespace solidity
{
2014-10-30 00:20:32 +00:00
2014-12-11 16:35:23 +00:00
void ExpressionCompiler : : compileExpression ( CompilerContext & _context , Expression const & _expression , bool _optimize )
2014-10-30 00:20:32 +00:00
{
2014-12-11 16:35:23 +00:00
ExpressionCompiler compiler ( _context , _optimize ) ;
2014-10-30 00:20:32 +00:00
_expression . accept ( compiler ) ;
}
2015-01-08 23:58:32 +00:00
void ExpressionCompiler : : appendTypeConversion ( CompilerContext & _context , Type const & _typeOnStack ,
Type const & _targetType , bool _cleanupNeeded )
2014-11-04 18:13:03 +00:00
{
ExpressionCompiler compiler ( _context ) ;
2015-01-08 23:58:32 +00:00
compiler . appendTypeConversion ( _typeOnStack , _targetType , _cleanupNeeded ) ;
2014-11-04 18:13:03 +00:00
}
2014-12-06 00:06:24 +00:00
bool ExpressionCompiler : : visit ( Assignment const & _assignment )
2014-10-30 00:20:32 +00:00
{
2014-11-06 21:39:02 +00:00
_assignment . getRightHandSide ( ) . accept ( * this ) ;
appendTypeConversion ( * _assignment . getRightHandSide ( ) . getType ( ) , * _assignment . getType ( ) ) ;
2014-10-30 00:20:32 +00:00
_assignment . getLeftHandSide ( ) . accept ( * this ) ;
2014-12-17 15:23:18 +00:00
solAssert ( m_currentLValue . isValid ( ) , " LValue not retrieved. " ) ;
2014-10-30 00:20:32 +00:00
Token : : Value op = _assignment . getAssignmentOperator ( ) ;
2014-11-06 21:39:02 +00:00
if ( op ! = Token : : ASSIGN ) // compound assignment
2014-11-10 16:31:09 +00:00
{
if ( m_currentLValue . storesReferenceOnStack ( ) )
m_context < < eth : : Instruction : : SWAP1 < < eth : : Instruction : : DUP2 ;
m_currentLValue . retrieveValue ( _assignment , true ) ;
2014-11-06 21:39:02 +00:00
appendOrdinaryBinaryOperatorCode ( Token : : AssignmentToBinaryOp ( op ) , * _assignment . getType ( ) ) ;
2014-12-18 21:15:11 +00:00
if ( m_currentLValue . storesReferenceOnStack ( ) )
m_context < < eth : : Instruction : : SWAP1 ;
2014-11-10 16:31:09 +00:00
}
m_currentLValue . storeValue ( _assignment ) ;
2014-11-13 00:12:57 +00:00
m_currentLValue . reset ( ) ;
2014-10-30 00:20:32 +00:00
return false ;
}
2014-12-19 10:31:17 +00:00
bool ExpressionCompiler : : visit ( UnaryOperation const & _unaryOperation )
2014-10-30 00:20:32 +00:00
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
2014-12-19 10:31:17 +00:00
if ( _unaryOperation . getType ( ) - > getCategory ( ) = = Type : : Category : : INTEGER_CONSTANT )
{
m_context < < _unaryOperation . getType ( ) - > literalValue ( nullptr ) ;
return false ;
}
_unaryOperation . getSubExpression ( ) . accept ( * this ) ;
2014-10-30 00:20:32 +00:00
switch ( _unaryOperation . getOperator ( ) )
{
case Token : : NOT : // !
2014-10-31 16:20:27 +00:00
m_context < < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : BIT_NOT : // ~
2014-10-31 16:20:27 +00:00
m_context < < eth : : Instruction : : NOT ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : DELETE : // delete
2014-12-17 15:23:18 +00:00
solAssert ( m_currentLValue . isValid ( ) , " LValue not retrieved. " ) ;
2015-01-14 12:52:03 +00:00
m_currentLValue . setToZero ( _unaryOperation ) ;
2014-11-13 00:12:57 +00:00
m_currentLValue . reset ( ) ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : INC : // ++ (pre- or postfix)
case Token : : DEC : // -- (pre- or postfix)
2014-12-17 15:23:18 +00:00
solAssert ( m_currentLValue . isValid ( ) , " LValue not retrieved. " ) ;
2014-11-10 16:31:09 +00:00
m_currentLValue . retrieveValue ( _unaryOperation ) ;
2014-10-30 00:20:32 +00:00
if ( ! _unaryOperation . isPrefixOperation ( ) )
2014-11-10 16:31:09 +00:00
{
if ( m_currentLValue . storesReferenceOnStack ( ) )
m_context < < eth : : Instruction : : SWAP1 < < eth : : Instruction : : DUP2 ;
else
m_context < < eth : : Instruction : : DUP1 ;
}
2014-10-30 00:20:32 +00:00
m_context < < u256 ( 1 ) ;
if ( _unaryOperation . getOperator ( ) = = Token : : INC )
m_context < < eth : : Instruction : : ADD ;
else
m_context < < eth : : Instruction : : SWAP1 < < eth : : Instruction : : SUB ; // @todo avoid the swap
2014-11-10 16:31:09 +00:00
// Stack for prefix: [ref] (*ref)+-1
// Stack for postfix: *ref [ref] (*ref)+-1
if ( m_currentLValue . storesReferenceOnStack ( ) )
m_context < < eth : : Instruction : : SWAP1 ;
m_currentLValue . storeValue ( _unaryOperation , ! _unaryOperation . isPrefixOperation ( ) ) ;
2014-11-13 00:12:57 +00:00
m_currentLValue . reset ( ) ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : ADD : // +
// unary add, so basically no-op
break ;
case Token : : SUB : // -
m_context < < u256 ( 0 ) < < eth : : Instruction : : SUB ;
break ;
default :
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid unary operator: " +
string ( Token : : toString ( _unaryOperation . getOperator ( ) ) ) ) ) ;
2014-10-30 00:20:32 +00:00
}
2014-12-19 10:31:17 +00:00
return false ;
2014-10-30 00:20:32 +00:00
}
2014-12-06 00:06:24 +00:00
bool ExpressionCompiler : : visit ( BinaryOperation const & _binaryOperation )
2014-10-30 00:20:32 +00:00
{
2014-12-06 00:06:24 +00:00
Expression const & leftExpression = _binaryOperation . getLeftExpression ( ) ;
Expression const & rightExpression = _binaryOperation . getRightExpression ( ) ;
2014-11-04 14:01:07 +00:00
Type const & commonType = _binaryOperation . getCommonType ( ) ;
2015-01-09 20:36:25 +00:00
Token : : Value const c_op = _binaryOperation . getOperator ( ) ;
2014-10-30 00:20:32 +00:00
2015-01-09 20:36:25 +00:00
if ( c_op = = Token : : AND | | c_op = = Token : : OR ) // special case: short-circuiting
2014-10-30 00:20:32 +00:00
appendAndOrOperatorCode ( _binaryOperation ) ;
2014-12-19 10:31:17 +00:00
else if ( commonType . getCategory ( ) = = Type : : Category : : INTEGER_CONSTANT )
m_context < < commonType . literalValue ( nullptr ) ;
2014-10-30 00:20:32 +00:00
else
{
2014-12-19 10:31:17 +00:00
bool cleanupNeeded = commonType . getCategory ( ) = = Type : : Category : : INTEGER & &
2015-01-09 20:36:25 +00:00
( Token : : isCompareOp ( c_op ) | | c_op = = Token : : DIV | | c_op = = Token : : MOD ) ;
2014-11-04 18:13:03 +00:00
2014-12-11 16:35:23 +00:00
// for commutative operators, push the literal as late as possible to allow improved optimization
2014-12-19 10:31:17 +00:00
auto isLiteral = [ ] ( Expression const & _e )
{
return dynamic_cast < Literal const * > ( & _e ) | | _e . getType ( ) - > getCategory ( ) = = Type : : Category : : INTEGER_CONSTANT ;
} ;
2015-01-09 20:36:25 +00:00
bool swap = m_optimize & & Token : : isCommutativeOp ( c_op ) & & isLiteral ( rightExpression ) & & ! isLiteral ( leftExpression ) ;
2014-12-11 16:35:23 +00:00
if ( swap )
{
leftExpression . accept ( * this ) ;
appendTypeConversion ( * leftExpression . getType ( ) , commonType , cleanupNeeded ) ;
rightExpression . accept ( * this ) ;
appendTypeConversion ( * rightExpression . getType ( ) , commonType , cleanupNeeded ) ;
}
else
{
rightExpression . accept ( * this ) ;
appendTypeConversion ( * rightExpression . getType ( ) , commonType , cleanupNeeded ) ;
leftExpression . accept ( * this ) ;
appendTypeConversion ( * leftExpression . getType ( ) , commonType , cleanupNeeded ) ;
}
2015-01-09 20:36:25 +00:00
if ( Token : : isCompareOp ( c_op ) )
appendCompareOperatorCode ( c_op , commonType ) ;
2014-11-04 14:01:07 +00:00
else
2015-01-09 20:36:25 +00:00
appendOrdinaryBinaryOperatorCode ( c_op , commonType ) ;
2014-10-30 00:20:32 +00:00
}
// do not visit the child nodes, we already did that explicitly
return false ;
}
2014-12-06 00:06:24 +00:00
bool ExpressionCompiler : : visit ( FunctionCall const & _functionCall )
2014-10-30 00:20:32 +00:00
{
2014-11-26 12:19:17 +00:00
using Location = FunctionType : : Location ;
2014-10-30 00:20:32 +00:00
if ( _functionCall . isTypeConversion ( ) )
{
2014-11-21 18:14:56 +00:00
//@todo struct construction
2014-12-17 15:23:18 +00:00
solAssert ( _functionCall . getArguments ( ) . size ( ) = = 1 , " " ) ;
2014-12-06 00:06:24 +00:00
Expression const & firstArgument = * _functionCall . getArguments ( ) . front ( ) ;
2014-10-30 00:20:32 +00:00
firstArgument . accept ( * this ) ;
2015-01-07 22:45:26 +00:00
appendTypeConversion ( * firstArgument . getType ( ) , * _functionCall . getType ( ) ) ;
2014-10-30 00:20:32 +00:00
}
else
{
2014-11-25 13:43:23 +00:00
FunctionType const & function = dynamic_cast < FunctionType const & > ( * _functionCall . getExpression ( ) . getType ( ) ) ;
2014-12-10 22:01:40 +00:00
vector < ASTPointer < Expression const > > arguments = _functionCall . getArguments ( ) ;
2014-12-17 15:23:18 +00:00
solAssert ( arguments . size ( ) = = function . getParameterTypes ( ) . size ( ) , " " ) ;
2014-11-25 17:23:39 +00:00
2014-12-04 18:38:24 +00:00
switch ( function . getLocation ( ) )
{
case Location : : INTERNAL :
2014-10-30 00:20:32 +00:00
{
2014-11-25 17:23:39 +00:00
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
2014-10-30 00:20:32 +00:00
2014-11-25 17:23:39 +00:00
eth : : AssemblyItem returnLabel = m_context . pushNewTag ( ) ;
for ( unsigned i = 0 ; i < arguments . size ( ) ; + + i )
{
arguments [ i ] - > accept ( * this ) ;
appendTypeConversion ( * arguments [ i ] - > getType ( ) , * function . getParameterTypes ( ) [ i ] ) ;
}
_functionCall . getExpression ( ) . accept ( * this ) ;
2014-10-30 00:20:32 +00:00
2014-11-25 17:23:39 +00:00
m_context . appendJump ( ) ;
m_context < < returnLabel ;
2014-10-30 00:20:32 +00:00
2014-12-08 21:18:19 +00:00
unsigned returnParametersSize = CompilerUtils : : getSizeOnStack ( function . getReturnParameterTypes ( ) ) ;
2014-11-25 17:23:39 +00:00
// callee adds return parameters, but removes arguments and return label
2015-01-07 11:50:23 +00:00
m_context . adjustStackOffset ( returnParametersSize - CompilerUtils : : getSizeOnStack ( function . getParameterTypes ( ) ) - 1 ) ;
2014-11-25 17:23:39 +00:00
// @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 )
2014-12-08 21:18:19 +00:00
CompilerUtils ( m_context ) . popStackElement ( * function . getReturnParameterTypes ( ) [ i ] ) ;
2014-12-04 18:38:24 +00:00
break ;
2014-11-25 17:23:39 +00:00
}
2014-12-04 18:38:24 +00:00
case Location : : EXTERNAL :
2014-12-10 22:01:40 +00:00
case Location : : BARE :
2015-01-12 11:47:37 +00:00
_functionCall . getExpression ( ) . accept ( * this ) ;
appendExternalFunctionCall ( function , arguments , function . getLocation ( ) = = Location : : BARE ) ;
break ;
2015-01-13 17:12:19 +00:00
case Location : : CREATION :
2014-11-25 17:23:39 +00:00
{
2015-01-13 17:12:19 +00:00
_functionCall . getExpression ( ) . accept ( * this ) ;
solAssert ( ! function . gasSet ( ) , " Gas limit set for contract creation. " ) ;
solAssert ( function . getReturnParameterTypes ( ) . size ( ) = = 1 , " " ) ;
ContractDefinition const & contract = dynamic_cast < ContractType const & > (
* function . getReturnParameterTypes ( ) . front ( ) ) . getContractDefinition ( ) ;
// copy the contract's code into memory
bytes const & bytecode = m_context . getCompiledContract ( contract ) ;
m_context < < u256 ( bytecode . size ( ) ) ;
//@todo could be done by actually appending the Assembly, but then we probably need to compile
// multiple times. Will revisit once external fuctions are inlined.
m_context . appendData ( bytecode ) ;
//@todo copy to memory position 0, shift as soon as we use memory
m_context < < u256 ( 0 ) < < eth : : Instruction : : CODECOPY ;
unsigned length = bytecode . size ( ) ;
length + = appendArgumentCopyToMemory ( function . getParameterTypes ( ) , arguments , length ) ;
// size, offset, endowment
m_context < < u256 ( length ) < < u256 ( 0 ) ;
if ( function . valueSet ( ) )
m_context < < eth : : dupInstruction ( 3 ) ;
else
m_context < < u256 ( 0 ) ;
m_context < < eth : : Instruction : : CREATE ;
if ( function . valueSet ( ) )
m_context < < eth : : swapInstruction ( 1 ) < < eth : : Instruction : : POP ;
2014-12-04 18:38:24 +00:00
break ;
}
2015-01-12 11:47:37 +00:00
case Location : : SET_GAS :
2014-12-10 22:01:40 +00:00
{
2015-01-12 11:47:37 +00:00
// stack layout: contract_address function_id [gas] [value]
_functionCall . getExpression ( ) . accept ( * this ) ;
arguments . front ( ) - > accept ( * this ) ;
appendTypeConversion ( * arguments . front ( ) - > getType ( ) , IntegerType ( 256 ) , true ) ;
// Note that function is not the original function, but the ".gas" function.
// Its values of gasSet and valueSet is equal to the original function's though.
unsigned stackDepth = ( function . gasSet ( ) ? 1 : 0 ) + ( function . valueSet ( ) ? 1 : 0 ) ;
if ( stackDepth > 0 )
m_context < < eth : : swapInstruction ( stackDepth ) ;
if ( function . gasSet ( ) )
m_context < < eth : : Instruction : : POP ;
2014-12-04 18:38:24 +00:00
break ;
2014-12-10 22:01:40 +00:00
}
2015-01-12 11:47:37 +00:00
case Location : : SET_VALUE :
// stack layout: contract_address function_id [gas] [value]
_functionCall . getExpression ( ) . accept ( * this ) ;
// Note that function is not the original function, but the ".value" function.
// Its values of gasSet and valueSet is equal to the original function's though.
if ( function . valueSet ( ) )
m_context < < eth : : Instruction : : POP ;
arguments . front ( ) - > accept ( * this ) ;
break ;
2014-12-04 18:38:24 +00:00
case Location : : SEND :
2015-01-12 11:47:37 +00:00
_functionCall . getExpression ( ) . accept ( * this ) ;
2015-01-14 09:46:44 +00:00
m_context < < u256 ( 0 ) ; // 0 gas, we do not want to execute code
2015-01-12 11:47:37 +00:00
arguments . front ( ) - > accept ( * this ) ;
appendTypeConversion ( * arguments . front ( ) - > getType ( ) ,
* function . getParameterTypes ( ) . front ( ) , true ) ;
2015-01-14 09:46:44 +00:00
appendExternalFunctionCall ( FunctionType ( TypePointers { } , TypePointers { } ,
Location : : EXTERNAL , true , true ) , { } , true ) ;
2014-12-04 18:38:24 +00:00
break ;
case Location : : SUICIDE :
arguments . front ( ) - > accept ( * this ) ;
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
2014-12-10 16:15:17 +00:00
CompilerUtils ( m_context ) . storeInMemory ( 0 ) ;
m_context < < u256 ( 32 ) < < u256 ( 0 ) < < eth : : Instruction : : SHA3 ;
2014-12-04 18:38:24 +00:00
break ;
2015-01-08 23:22:06 +00:00
case Location : : LOG0 :
case Location : : LOG1 :
case Location : : LOG2 :
case Location : : LOG3 :
case Location : : LOG4 :
2015-01-09 14:00:47 +00:00
{
unsigned logNumber = int ( function . getLocation ( ) ) - int ( Location : : LOG0 ) ;
for ( int arg = logNumber ; arg > = 0 ; - - arg )
{
arguments [ arg ] - > accept ( * this ) ;
appendTypeConversion ( * arguments [ arg ] - > getType ( ) , * function . getParameterTypes ( ) [ arg ] , true ) ;
}
2015-01-08 23:22:06 +00:00
// @todo move this once we actually use memory
CompilerUtils ( m_context ) . storeInMemory ( 0 ) ;
2015-01-09 14:00:47 +00:00
m_context < < u256 ( 32 ) < < u256 ( 0 ) < < eth : : logInstruction ( logNumber ) ;
2015-01-08 23:22:06 +00:00
break ;
2015-01-09 14:00:47 +00:00
}
2015-01-15 18:59:35 +00:00
case Location : : BLOCKHASH :
{
arguments [ 0 ] - > accept ( * this ) ;
appendTypeConversion ( * arguments [ 0 ] - > getType ( ) , * function . getParameterTypes ( ) [ 0 ] , true ) ;
m_context < < eth : : Instruction : : BLOCKHASH ;
break ;
}
2014-12-04 18:38:24 +00:00
case Location : : ECRECOVER :
case Location : : SHA256 :
case Location : : RIPEMD160 :
{
static const map < Location , u256 > contractAddresses { { Location : : ECRECOVER , 1 } ,
{ Location : : SHA256 , 2 } ,
{ Location : : RIPEMD160 , 3 } } ;
2015-01-12 11:47:37 +00:00
m_context < < contractAddresses . find ( function . getLocation ( ) ) - > second ;
appendExternalFunctionCall ( function , arguments , true ) ;
2014-12-04 18:38:24 +00:00
break ;
}
default :
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid function type. " ) ) ;
2014-11-25 17:23:39 +00:00
}
2014-10-30 00:20:32 +00:00
}
return false ;
}
2015-01-15 12:33:58 +00:00
bool ExpressionCompiler : : visit ( NewExpression const & )
2014-12-12 15:49:26 +00:00
{
2015-01-13 17:12:19 +00:00
// code is created for the function call (CREATION) only
2014-12-12 15:49:26 +00:00
return false ;
}
2014-12-06 00:06:24 +00:00
void ExpressionCompiler : : endVisit ( MemberAccess const & _memberAccess )
2014-10-30 00:20:32 +00:00
{
2014-11-24 12:23:58 +00:00
ASTString const & member = _memberAccess . getMemberName ( ) ;
2014-11-21 18:14:56 +00:00
switch ( _memberAccess . getExpression ( ) . getType ( ) - > getCategory ( ) )
{
2015-01-07 21:54:56 +00:00
case Type : : Category : : CONTRACT :
{
ContractType const & type = dynamic_cast < ContractType const & > ( * _memberAccess . getExpression ( ) . getType ( ) ) ;
u256 identifier = type . getFunctionIdentifier ( member ) ;
if ( identifier ! = Invalid256 )
{
appendTypeConversion ( type , IntegerType ( 0 , IntegerType : : Modifier : : ADDRESS ) , true ) ;
m_context < < identifier ;
break ;
}
// fall-through to "integer" otherwise (address)
}
2014-11-21 18:14:56 +00:00
case Type : : Category : : INTEGER :
2014-11-25 17:23:39 +00:00
if ( member = = " balance " )
2014-11-26 12:19:17 +00:00
{
appendTypeConversion ( * _memberAccess . getExpression ( ) . getType ( ) ,
IntegerType ( 0 , IntegerType : : Modifier : : ADDRESS ) , true ) ;
2014-11-25 17:23:39 +00:00
m_context < < eth : : Instruction : : BALANCE ;
2014-11-26 12:19:17 +00:00
}
2014-12-15 00:02:33 +00:00
else if ( member = = " send " | | member . substr ( 0 , min < size_t > ( member . size ( ) , 4 ) ) = = " call " )
2014-11-26 12:19:17 +00:00
appendTypeConversion ( * _memberAccess . getExpression ( ) . getType ( ) ,
IntegerType ( 0 , IntegerType : : Modifier : : ADDRESS ) , true ) ;
2014-11-25 17:23:39 +00:00
else
2014-11-21 18:14:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid member access to integer. " ) ) ;
break ;
2015-01-12 11:47:37 +00:00
case Type : : Category : : FUNCTION :
solAssert ( ! ! _memberAccess . getExpression ( ) . getType ( ) - > getMemberType ( member ) ,
" Invalid member access to function. " ) ;
break ;
2014-11-21 18:14:56 +00:00
case Type : : Category : : MAGIC :
2014-11-24 12:23:58 +00:00
// we can ignore the kind of magic and only look at the name of the member
if ( member = = " coinbase " )
m_context < < eth : : Instruction : : COINBASE ;
else if ( member = = " timestamp " )
m_context < < eth : : Instruction : : TIMESTAMP ;
2015-01-19 14:34:15 +00:00
else if ( member = = " difficulty " )
2014-11-24 12:23:58 +00:00
m_context < < eth : : Instruction : : DIFFICULTY ;
else if ( member = = " number " )
m_context < < eth : : Instruction : : NUMBER ;
else if ( member = = " gaslimit " )
m_context < < eth : : Instruction : : GASLIMIT ;
else if ( member = = " sender " )
m_context < < eth : : Instruction : : CALLER ;
else if ( member = = " value " )
m_context < < eth : : Instruction : : CALLVALUE ;
else if ( member = = " origin " )
m_context < < eth : : Instruction : : ORIGIN ;
else if ( member = = " gas " )
m_context < < eth : : Instruction : : GAS ;
else if ( member = = " gasprice " )
m_context < < eth : : Instruction : : GASPRICE ;
else
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown magic member. " ) ) ;
2014-11-21 18:14:56 +00:00
break ;
case Type : : Category : : STRUCT :
{
StructType const & type = dynamic_cast < StructType const & > ( * _memberAccess . getExpression ( ) . getType ( ) ) ;
2014-11-24 12:23:58 +00:00
m_context < < type . getStorageOffsetOfMember ( member ) < < eth : : Instruction : : ADD ;
2014-12-08 21:18:19 +00:00
m_currentLValue = LValue ( m_context , LValue : : STORAGE , * _memberAccess . getType ( ) ) ;
2014-11-21 18:14:56 +00:00
m_currentLValue . retrieveValueIfLValueNotRequested ( _memberAccess ) ;
break ;
}
2015-01-19 18:18:34 +00:00
case Type : : Category : : TYPE :
{
TypeType const & type = dynamic_cast < TypeType const & > ( * _memberAccess . getExpression ( ) . getType ( ) ) ;
if ( type . getMembers ( ) . getMemberType ( member ) )
{
ContractDefinition const & contract = dynamic_cast < ContractType const & > ( * type . getActualType ( ) )
. getContractDefinition ( ) ;
for ( ASTPointer < FunctionDefinition > const & function : contract . getDefinedFunctions ( ) )
if ( function - > getName ( ) = = member )
{
m_context < < m_context . getFunctionEntryLabel ( * function ) . pushTag ( ) ;
return ;
}
}
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid member access to " + type . toString ( ) ) ) ;
}
2014-11-21 18:14:56 +00:00
default :
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Member access to unknown type. " ) ) ;
}
2014-10-30 00:20:32 +00:00
}
2014-12-06 00:06:24 +00:00
bool ExpressionCompiler : : visit ( IndexAccess const & _indexAccess )
2014-10-30 00:20:32 +00:00
{
2014-11-10 16:31:09 +00:00
_indexAccess . getBaseExpression ( ) . accept ( * this ) ;
_indexAccess . getIndexExpression ( ) . accept ( * this ) ;
appendTypeConversion ( * _indexAccess . getIndexExpression ( ) . getType ( ) ,
* dynamic_cast < MappingType const & > ( * _indexAccess . getBaseExpression ( ) . getType ( ) ) . getKeyType ( ) ,
true ) ;
// @todo move this once we actually use memory
2014-12-10 16:15:17 +00:00
CompilerUtils ( m_context ) . storeInMemory ( 0 ) ;
CompilerUtils ( m_context ) . storeInMemory ( 32 ) ;
2014-11-10 16:31:09 +00:00
m_context < < u256 ( 64 ) < < u256 ( 0 ) < < eth : : Instruction : : SHA3 ;
2014-12-08 21:18:19 +00:00
m_currentLValue = LValue ( m_context , LValue : : STORAGE , * _indexAccess . getType ( ) ) ;
2014-11-10 16:31:09 +00:00
m_currentLValue . retrieveValueIfLValueNotRequested ( _indexAccess ) ;
2014-10-30 00:20:32 +00:00
2014-11-10 16:31:09 +00:00
return false ;
2014-10-30 00:20:32 +00:00
}
2014-12-06 00:06:24 +00:00
void ExpressionCompiler : : endVisit ( Identifier const & _identifier )
2014-10-30 00:20:32 +00:00
{
2014-12-06 00:06:24 +00:00
Declaration const * declaration = _identifier . getReferencedDeclaration ( ) ;
if ( MagicVariableDeclaration const * magicVar = dynamic_cast < MagicVariableDeclaration const * > ( declaration ) )
2014-11-21 18:14:56 +00:00
{
2014-11-26 12:19:17 +00:00
if ( magicVar - > getType ( ) - > getCategory ( ) = = Type : : Category : : CONTRACT ) // must be "this"
2014-11-21 18:14:56 +00:00
m_context < < eth : : Instruction : : ADDRESS ;
}
2015-01-19 18:18:34 +00:00
else if ( FunctionDefinition const * functionDef = dynamic_cast < FunctionDefinition const * > ( declaration ) )
2015-01-15 19:04:24 +00:00
m_context < < m_context . getVirtualFunctionEntryLabel ( * functionDef ) . pushTag ( ) ;
2015-01-19 18:18:34 +00:00
else if ( dynamic_cast < VariableDeclaration const * > ( declaration ) )
2014-11-21 18:14:56 +00:00
{
2014-12-06 00:06:24 +00:00
m_currentLValue . fromIdentifier ( _identifier , * declaration ) ;
2014-11-21 18:14:56 +00:00
m_currentLValue . retrieveValueIfLValueNotRequested ( _identifier ) ;
}
2015-01-19 18:18:34 +00:00
else if ( dynamic_cast < ContractDefinition const * > ( declaration ) )
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Identifier type not expected in expression context. " ) ) ;
}
2014-10-30 00:20:32 +00:00
}
2014-12-06 00:06:24 +00:00
void ExpressionCompiler : : endVisit ( Literal const & _literal )
2014-10-30 00:20:32 +00:00
{
switch ( _literal . getType ( ) - > getCategory ( ) )
{
2014-12-19 10:31:17 +00:00
case Type : : Category : : INTEGER_CONSTANT :
2014-10-30 00:20:32 +00:00
case Type : : Category : : BOOL :
2014-12-09 17:46:18 +00:00
case Type : : Category : : STRING :
2014-12-19 10:31:17 +00:00
m_context < < _literal . getType ( ) - > literalValue ( & _literal ) ;
2014-10-30 00:20:32 +00:00
break ;
default :
2014-12-09 17:46:18 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Only integer, boolean and string literals implemented for now. " ) ) ;
2014-10-30 00:20:32 +00:00
}
}
2014-12-06 00:06:24 +00:00
void ExpressionCompiler : : appendAndOrOperatorCode ( BinaryOperation const & _binaryOperation )
2014-10-30 00:20:32 +00:00
{
2015-01-09 20:36:25 +00:00
Token : : Value const c_op = _binaryOperation . getOperator ( ) ;
solAssert ( c_op = = Token : : OR | | c_op = = Token : : AND , " " ) ;
2014-10-30 00:20:32 +00:00
_binaryOperation . getLeftExpression ( ) . accept ( * this ) ;
m_context < < eth : : Instruction : : DUP1 ;
2015-01-09 20:36:25 +00:00
if ( c_op = = Token : : AND )
2014-10-31 16:20:27 +00:00
m_context < < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
eth : : AssemblyItem endLabel = m_context . appendConditionalJump ( ) ;
2014-10-31 16:47:43 +00:00
m_context < < eth : : Instruction : : POP ;
2014-10-30 00:20:32 +00:00
_binaryOperation . getRightExpression ( ) . accept ( * this ) ;
m_context < < endLabel ;
}
void ExpressionCompiler : : appendCompareOperatorCode ( Token : : Value _operator , Type const & _type )
{
if ( _operator = = Token : : EQ | | _operator = = Token : : NE )
{
m_context < < eth : : Instruction : : EQ ;
if ( _operator = = Token : : NE )
2014-10-31 16:20:27 +00:00
m_context < < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
}
else
{
2014-11-05 13:20:56 +00:00
IntegerType const & type = dynamic_cast < IntegerType const & > ( _type ) ;
2015-01-09 20:36:25 +00:00
bool const c_isSigned = type . isSigned ( ) ;
2014-10-30 00:20:32 +00:00
switch ( _operator )
{
case Token : : GTE :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SLT : eth : : Instruction : : LT )
2014-10-31 16:20:27 +00:00
< < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : LTE :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SGT : eth : : Instruction : : GT )
2014-10-31 16:20:27 +00:00
< < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : GT :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SGT : eth : : Instruction : : GT ) ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : LT :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SLT : eth : : Instruction : : LT ) ;
2014-10-30 00:20:32 +00:00
break ;
default :
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown comparison operator. " ) ) ;
2014-10-30 00:20:32 +00:00
}
}
}
void ExpressionCompiler : : appendOrdinaryBinaryOperatorCode ( Token : : Value _operator , Type const & _type )
{
if ( Token : : isArithmeticOp ( _operator ) )
appendArithmeticOperatorCode ( _operator , _type ) ;
else if ( Token : : isBitOp ( _operator ) )
appendBitOperatorCode ( _operator ) ;
else if ( Token : : isShiftOp ( _operator ) )
appendShiftOperatorCode ( _operator ) ;
else
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown binary operator. " ) ) ;
2014-10-30 00:20:32 +00:00
}
void ExpressionCompiler : : appendArithmeticOperatorCode ( Token : : Value _operator , Type const & _type )
{
2014-11-05 13:20:56 +00:00
IntegerType const & type = dynamic_cast < IntegerType const & > ( _type ) ;
2015-01-09 20:36:25 +00:00
bool const c_isSigned = type . isSigned ( ) ;
2014-10-30 00:20:32 +00:00
switch ( _operator )
{
case Token : : ADD :
m_context < < eth : : Instruction : : ADD ;
break ;
case Token : : SUB :
2014-11-06 21:39:02 +00:00
m_context < < eth : : Instruction : : SUB ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : MUL :
m_context < < eth : : Instruction : : MUL ;
break ;
case Token : : DIV :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SDIV : eth : : Instruction : : DIV ) ;
2014-10-30 00:20:32 +00:00
break ;
case Token : : MOD :
2015-01-09 20:36:25 +00:00
m_context < < ( c_isSigned ? eth : : Instruction : : SMOD : eth : : Instruction : : MOD ) ;
2014-10-30 00:20:32 +00:00
break ;
default :
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown arithmetic operator. " ) ) ;
2014-10-30 00:20:32 +00:00
}
}
void ExpressionCompiler : : appendBitOperatorCode ( Token : : Value _operator )
{
switch ( _operator )
{
case Token : : BIT_OR :
m_context < < eth : : Instruction : : OR ;
break ;
case Token : : BIT_AND :
m_context < < eth : : Instruction : : AND ;
break ;
case Token : : BIT_XOR :
m_context < < eth : : Instruction : : XOR ;
break ;
default :
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown bit operator. " ) ) ;
2014-10-30 00:20:32 +00:00
}
}
void ExpressionCompiler : : appendShiftOperatorCode ( Token : : Value _operator )
{
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Shift operators not yet implemented. " ) ) ;
2014-10-30 00:20:32 +00:00
switch ( _operator )
{
case Token : : SHL :
break ;
case Token : : SAR :
break ;
default :
2014-11-05 13:20:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Unknown shift operator. " ) ) ;
2014-10-30 00:20:32 +00:00
}
}
2014-11-04 20:29:36 +00:00
void ExpressionCompiler : : appendTypeConversion ( Type const & _typeOnStack , Type const & _targetType , bool _cleanupNeeded )
2014-11-04 18:13:03 +00:00
{
2014-11-06 21:39:02 +00:00
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher order bits
2014-11-04 18:13:03 +00:00
2014-11-04 20:29:36 +00:00
if ( _typeOnStack = = _targetType & & ! _cleanupNeeded )
2014-11-04 18:13:03 +00:00
return ;
2014-12-19 10:31:17 +00:00
Type : : Category stackTypeCategory = _typeOnStack . getCategory ( ) ;
Type : : Category targetTypeCategory = _targetType . getCategory ( ) ;
2015-01-23 16:36:12 +00:00
2015-01-26 12:24:16 +00:00
if ( stackTypeCategory = = Type : : Category : : STRING )
2015-01-23 16:36:12 +00:00
{
2015-01-26 12:24:16 +00:00
if ( targetTypeCategory = = Type : : Category : : INTEGER )
2015-01-23 16:36:12 +00:00
{
2015-01-26 12:24:16 +00:00
// conversion from string to hash. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const & targetIntegerType = dynamic_cast < IntegerType const & > ( _targetType ) ;
StaticStringType const & sourceStringType = dynamic_cast < StaticStringType const & > ( _typeOnStack ) ;
if ( targetIntegerType . isHash ( ) )
{
solAssert ( targetIntegerType . getNumBits ( ) = = sourceStringType . getNumBytes ( ) * 8 , " The size should be the same. " ) ;
m_context < < ( u256 ( 1 ) < < 256 - sourceStringType . getNumBytes ( ) * 8 ) < <
eth : : Instruction : : SWAP1 < < eth : : Instruction : : DIV ;
}
}
else {
solAssert ( targetTypeCategory = = Type : : Category : : STRING , " Invalid type conversion requested. " ) ;
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
2015-01-23 16:36:12 +00:00
}
}
else if ( targetTypeCategory = = Type : : Category : : STRING & & stackTypeCategory = = Type : : Category : : INTEGER )
{
// conversion from hash to string. no need to clean the high bit
// only to shift left because of opposite alignment
StaticStringType const & targetStringType = dynamic_cast < StaticStringType const & > ( _targetType ) ;
IntegerType const & sourceIntegerType = dynamic_cast < IntegerType const & > ( _typeOnStack ) ;
if ( sourceIntegerType . isHash ( ) )
{
solAssert ( sourceIntegerType . getNumBits ( ) = = targetStringType . getNumBytes ( ) * 8 , " The size should be the same. " ) ;
2015-01-26 12:24:16 +00:00
m_context < < ( u256 ( 1 ) < < 256 - sourceIntegerType . getNumBits ( ) ) < < eth : : Instruction : : MUL ;
2015-01-23 16:36:12 +00:00
}
}
else if ( stackTypeCategory = = Type : : Category : : INTEGER | | stackTypeCategory = = Type : : Category : : CONTRACT | |
2015-01-07 22:45:26 +00:00
stackTypeCategory = = Type : : Category : : INTEGER_CONSTANT )
2014-12-19 10:31:17 +00:00
{
solAssert ( targetTypeCategory = = Type : : Category : : INTEGER | | targetTypeCategory = = Type : : Category : : CONTRACT , " " ) ;
2015-01-07 22:45:26 +00:00
IntegerType addressType ( 0 , IntegerType : : Modifier : : ADDRESS ) ;
IntegerType const & targetType = targetTypeCategory = = Type : : Category : : INTEGER
? dynamic_cast < IntegerType const & > ( _targetType ) : addressType ;
if ( stackTypeCategory = = Type : : Category : : INTEGER_CONSTANT )
{
IntegerConstantType const & constType = dynamic_cast < IntegerConstantType const & > ( _typeOnStack ) ;
// We know that the stack is clean, we only have to clean for a narrowing conversion
// where cleanup is forced.
if ( targetType . getNumBits ( ) < constType . getIntegerType ( ) - > getNumBits ( ) & & _cleanupNeeded )
appendHighBitsCleanup ( targetType ) ;
}
else
{
IntegerType const & typeOnStack = stackTypeCategory = = Type : : Category : : INTEGER
? dynamic_cast < IntegerType const & > ( _typeOnStack ) : addressType ;
// Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits
if ( targetType . getNumBits ( ) > typeOnStack . getNumBits ( ) )
appendHighBitsCleanup ( typeOnStack ) ;
else if ( _cleanupNeeded )
appendHighBitsCleanup ( targetType ) ;
}
2014-12-19 10:31:17 +00:00
}
2014-11-04 20:29:36 +00:00
else if ( _typeOnStack ! = _targetType )
2014-11-04 18:13:03 +00:00
// All other types should not be convertible to non-equal types.
2014-11-06 21:04:10 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid type conversion requested. " ) ) ;
2014-11-04 18:13:03 +00:00
}
void ExpressionCompiler : : appendHighBitsCleanup ( IntegerType const & _typeOnStack )
{
if ( _typeOnStack . getNumBits ( ) = = 256 )
return ;
else if ( _typeOnStack . isSigned ( ) )
m_context < < u256 ( _typeOnStack . getNumBits ( ) / 8 - 1 ) < < eth : : Instruction : : SIGNEXTEND ;
else
m_context < < ( ( u256 ( 1 ) < < _typeOnStack . getNumBits ( ) ) - 1 ) < < eth : : Instruction : : AND ;
}
2014-12-10 22:01:40 +00:00
void ExpressionCompiler : : appendExternalFunctionCall ( FunctionType const & _functionType ,
vector < ASTPointer < Expression const > > const & _arguments ,
2015-01-12 11:47:37 +00:00
bool bare )
2014-12-10 22:01:40 +00:00
{
2014-12-17 15:23:18 +00:00
solAssert ( _arguments . size ( ) = = _functionType . getParameterTypes ( ) . size ( ) , " " ) ;
2014-12-10 22:01:40 +00:00
2015-01-12 11:47:37 +00:00
// Assumed stack content here:
// <stack top>
// value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
2015-01-14 10:57:22 +00:00
// function identifier [unless bare]
2015-01-12 11:47:37 +00:00
// contract address
2015-01-08 16:18:31 +00:00
2015-01-12 11:47:37 +00:00
unsigned gasValueSize = ( _functionType . gasSet ( ) ? 1 : 0 ) + ( _functionType . valueSet ( ) ? 1 : 0 ) ;
2015-01-14 10:57:22 +00:00
unsigned contractStackPos = m_context . currentToBaseStackOffset ( 1 + gasValueSize + ( bare ? 0 : 1 ) ) ;
unsigned gasStackPos = m_context . currentToBaseStackOffset ( gasValueSize ) ;
unsigned valueStackPos = m_context . currentToBaseStackOffset ( 1 ) ;
2015-01-12 11:47:37 +00:00
if ( ! bare )
2014-12-10 22:01:40 +00:00
{
2015-01-14 10:57:22 +00:00
// copy function identifier
2015-01-12 11:47:37 +00:00
m_context < < eth : : dupInstruction ( gasValueSize + 1 ) ;
2015-01-08 23:27:26 +00:00
CompilerUtils ( m_context ) . storeInMemory ( 0 , CompilerUtils : : dataStartOffset ) ;
2014-12-10 22:01:40 +00:00
}
2015-01-08 16:18:31 +00:00
2015-01-14 10:57:22 +00:00
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils : : dataStartOffset ;
2015-01-13 17:12:19 +00:00
dataOffset + = appendArgumentCopyToMemory ( _functionType . getParameterTypes ( ) , _arguments , dataOffset ) ;
2014-12-10 22:01:40 +00:00
//@todo only return the first return value for now
Type const * firstType = _functionType . getReturnParameterTypes ( ) . empty ( ) ? nullptr :
_functionType . getReturnParameterTypes ( ) . front ( ) . get ( ) ;
2015-01-08 23:58:32 +00:00
unsigned retSize = firstType ? CompilerUtils : : getPaddedSize ( firstType - > getCalldataEncodedSize ( ) ) : 0 ;
2014-12-10 22:01:40 +00:00
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context < < u256 ( retSize ) < < u256 ( 0 ) < < u256 ( dataOffset ) < < u256 ( 0 ) ;
2015-01-12 11:47:37 +00:00
if ( _functionType . valueSet ( ) )
2015-01-14 10:57:22 +00:00
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( valueStackPos ) ) ;
2014-12-10 22:01:40 +00:00
else
m_context < < u256 ( 0 ) ;
2015-01-14 10:57:22 +00:00
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( contractStackPos ) ) ;
2015-01-08 16:18:31 +00:00
2015-01-12 11:47:37 +00:00
if ( _functionType . gasSet ( ) )
2015-01-14 10:57:22 +00:00
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( gasStackPos ) ) ;
2015-01-12 11:47:37 +00:00
else
2015-01-14 09:46:44 +00:00
// send all gas except for the 21 needed to execute "SUB" and "CALL"
m_context < < u256 ( 21 ) < < eth : : Instruction : : GAS < < eth : : Instruction : : SUB ;
2015-01-12 11:47:37 +00:00
m_context < < eth : : Instruction : : CALL
< < eth : : Instruction : : POP ; // @todo do not ignore failure indicator
if ( _functionType . valueSet ( ) )
m_context < < eth : : Instruction : : POP ;
if ( _functionType . gasSet ( ) )
m_context < < eth : : Instruction : : POP ;
if ( ! bare )
m_context < < eth : : Instruction : : POP ;
m_context < < eth : : Instruction : : POP ; // pop contract address
2015-01-08 16:18:31 +00:00
2014-12-10 22:01:40 +00:00
if ( retSize > 0 )
{
2015-01-09 20:36:25 +00:00
bool const c_leftAligned = firstType - > getCategory ( ) = = Type : : Category : : STRING ;
CompilerUtils ( m_context ) . loadFromMemory ( 0 , retSize , c_leftAligned , false , true ) ;
2014-12-10 22:01:40 +00:00
}
}
2015-01-13 17:12:19 +00:00
unsigned ExpressionCompiler : : appendArgumentCopyToMemory ( TypePointers const & _types ,
vector < ASTPointer < Expression const > > const & _arguments ,
unsigned _memoryOffset )
{
unsigned length = 0 ;
for ( unsigned i = 0 ; i < _arguments . size ( ) ; + + i )
{
_arguments [ i ] - > accept ( * this ) ;
appendTypeConversion ( * _arguments [ i ] - > getType ( ) , * _types [ i ] , true ) ;
unsigned const c_numBytes = _types [ i ] - > getCalldataEncodedSize ( ) ;
if ( c_numBytes = = 0 | | c_numBytes > 32 )
BOOST_THROW_EXCEPTION ( CompilerError ( )
< < errinfo_sourceLocation ( _arguments [ i ] - > getLocation ( ) )
< < errinfo_comment ( " Type " + _types [ i ] - > toString ( ) + " not yet supported. " ) ) ;
bool const c_leftAligned = _types [ i ] - > getCategory ( ) = = Type : : Category : : STRING ;
bool const c_padToWords = true ;
length + = CompilerUtils ( m_context ) . storeInMemory ( _memoryOffset + length , c_numBytes ,
c_leftAligned , c_padToWords ) ;
}
return length ;
}
2014-12-10 13:37:37 +00:00
ExpressionCompiler : : LValue : : LValue ( CompilerContext & _compilerContext , LValueType _type , Type const & _dataType ,
2014-12-08 21:18:19 +00:00
unsigned _baseStackOffset ) :
2015-01-14 12:52:03 +00:00
m_context ( & _compilerContext ) , m_type ( _type ) , m_baseStackOffset ( _baseStackOffset )
2014-12-08 21:18:19 +00:00
{
2015-01-14 12:52:03 +00:00
//@todo change the type cast for arrays
solAssert ( _dataType . getStorageSize ( ) < = numeric_limits < unsigned > : : max ( ) , " The storage size of " + _dataType . toString ( ) + " should fit in unsigned " ) ;
if ( m_type = = STORAGE )
m_size = unsigned ( _dataType . getStorageSize ( ) ) ;
else
m_size = unsigned ( _dataType . getSizeOnStack ( ) ) ;
2014-12-08 21:18:19 +00:00
}
2014-11-10 16:31:09 +00:00
void ExpressionCompiler : : LValue : : retrieveValue ( Expression const & _expression , bool _remove ) const
2014-10-30 00:20:32 +00:00
{
2014-11-10 16:31:09 +00:00
switch ( m_type )
2014-11-07 01:06:37 +00:00
{
2014-11-10 16:31:09 +00:00
case STACK :
2014-11-07 01:06:37 +00:00
{
2014-11-10 16:31:09 +00:00
unsigned stackPos = m_context - > baseToCurrentStackOffset ( unsigned ( m_baseStackOffset ) ) ;
2014-11-07 01:06:37 +00:00
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. " ) ) ;
2015-01-14 12:52:03 +00:00
for ( unsigned i = 0 ; i < m_size ; + + i )
2014-12-08 21:18:19 +00:00
* m_context < < eth : : dupInstruction ( stackPos + 1 ) ;
2014-11-07 01:06:37 +00:00
break ;
}
2014-11-10 16:31:09 +00:00
case STORAGE :
2014-11-21 18:14:56 +00:00
if ( ! _expression . getType ( ) - > isValueType ( ) )
break ; // no distinction between value and reference for non-value types
2014-11-10 16:31:09 +00:00
if ( ! _remove )
* m_context < < eth : : Instruction : : DUP1 ;
2015-01-14 12:52:03 +00:00
if ( m_size = = 1 )
2014-12-08 21:18:19 +00:00
* m_context < < eth : : Instruction : : SLOAD ;
else
2015-01-14 12:52:03 +00:00
for ( unsigned i = 0 ; i < m_size ; + + i )
2014-12-08 21:18:19 +00:00
{
* m_context < < eth : : Instruction : : DUP1 < < eth : : Instruction : : SLOAD < < eth : : Instruction : : SWAP1 ;
2015-01-14 12:52:03 +00:00
if ( i + 1 < m_size )
* m_context < < u256 ( 1 ) < < eth : : Instruction : : ADD ;
2014-12-08 21:18:19 +00:00
else
* m_context < < eth : : Instruction : : POP ;
}
2014-11-07 01:06:37 +00:00
break ;
2014-11-10 16:31:09 +00:00
case MEMORY :
2014-11-21 18:14:56 +00:00
if ( ! _expression . getType ( ) - > isValueType ( ) )
break ; // no distinction between value and reference for non-value types
2014-11-10 16:31:09 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Location type not yet implemented. " ) ) ;
2014-11-07 01:06:37 +00:00
break ;
default :
2014-11-10 16:31:09 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Unsupported location type. " ) ) ;
2014-11-07 01:06:37 +00:00
break ;
}
2014-10-30 00:20:32 +00:00
}
2014-11-10 16:31:09 +00:00
void ExpressionCompiler : : LValue : : storeValue ( Expression const & _expression , bool _move ) const
2014-10-30 00:20:32 +00:00
{
2014-11-10 16:31:09 +00:00
switch ( m_type )
2014-11-07 01:06:37 +00:00
{
2014-11-10 16:31:09 +00:00
case STACK :
2014-11-07 01:06:37 +00:00
{
2015-01-14 12:52:03 +00:00
unsigned stackDiff = m_context - > baseToCurrentStackOffset ( unsigned ( m_baseStackOffset ) ) - m_size + 1 ;
2014-12-08 21:18:19 +00:00
if ( stackDiff > 16 )
2014-11-07 01:06:37 +00:00
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Stack too deep. " ) ) ;
2014-12-08 21:18:19 +00:00
else if ( stackDiff > 0 )
2015-01-14 12:52:03 +00:00
for ( unsigned i = 0 ; i < m_size ; + + i )
2014-12-08 21:18:19 +00:00
* m_context < < eth : : swapInstruction ( stackDiff ) < < eth : : Instruction : : POP ;
2014-11-07 01:06:37 +00:00
if ( ! _move )
2014-11-10 16:31:09 +00:00
retrieveValue ( _expression ) ;
2014-11-07 01:06:37 +00:00
break ;
}
2014-11-10 16:31:09 +00:00
case LValue : : STORAGE :
2014-11-21 18:14:56 +00:00
if ( ! _expression . getType ( ) - > isValueType ( ) )
break ; // no distinction between value and reference for non-value types
2014-12-08 21:18:19 +00:00
// stack layout: value value ... value ref
if ( ! _move ) // copy values
{
2015-01-14 12:52:03 +00:00
if ( m_size + 1 > 16 )
2014-12-08 21:18:19 +00:00
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Stack too deep. " ) ) ;
2015-01-14 12:52:03 +00:00
for ( unsigned i = 0 ; i < m_size ; + + i )
* m_context < < eth : : dupInstruction ( m_size + 1 ) < < eth : : Instruction : : SWAP1 ;
2014-12-08 21:18:19 +00:00
}
2015-01-14 12:52:03 +00:00
if ( m_size > 0 ) // store high index value first
* m_context < < u256 ( m_size - 1 ) < < eth : : Instruction : : ADD ;
for ( unsigned i = 0 ; i < m_size ; + + i )
2014-12-08 21:18:19 +00:00
{
2015-01-14 12:52:03 +00:00
if ( i + 1 > = m_size )
2014-12-08 21:18:19 +00:00
* 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 ;
}
2014-11-07 01:06:37 +00:00
break ;
2014-11-10 16:31:09 +00:00
case LValue : : MEMORY :
2014-11-21 18:14:56 +00:00
if ( ! _expression . getType ( ) - > isValueType ( ) )
break ; // no distinction between value and reference for non-value types
2014-11-10 16:31:09 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Location type not yet implemented. " ) ) ;
2014-11-07 01:06:37 +00:00
break ;
default :
2014-11-10 16:31:09 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Unsupported location type. " ) ) ;
2014-11-07 01:06:37 +00:00
break ;
}
2014-10-30 00:20:32 +00:00
}
2015-01-14 12:52:03 +00:00
void ExpressionCompiler : : LValue : : setToZero ( Expression const & _expression ) const
{
switch ( m_type )
{
case STACK :
{
unsigned stackDiff = m_context - > baseToCurrentStackOffset ( unsigned ( m_baseStackOffset ) ) ;
if ( stackDiff > 16 )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Stack too deep. " ) ) ;
2015-01-16 15:26:57 +00:00
solAssert ( stackDiff > = m_size - 1 , " " ) ;
2015-01-16 11:55:49 +00:00
for ( unsigned i = 0 ; i < m_size ; + + i )
2015-01-16 15:26:57 +00:00
* m_context < < u256 ( 0 ) < < eth : : swapInstruction ( stackDiff + 1 - i )
2015-01-16 12:07:16 +00:00
< < eth : : Instruction : : POP ;
2015-01-14 12:52:03 +00:00
break ;
}
case LValue : : STORAGE :
if ( m_size = = 0 )
* m_context < < eth : : Instruction : : POP ;
for ( unsigned i = 0 ; i < m_size ; + + i )
{
if ( i + 1 > = m_size )
* m_context < < u256 ( 0 ) < < eth : : Instruction : : SWAP1 < < eth : : Instruction : : SSTORE ;
else
* m_context < < u256 ( 0 ) < < eth : : Instruction : : DUP2 < < eth : : Instruction : : SSTORE
< < u256 ( 1 ) < < eth : : Instruction : : ADD ;
}
break ;
case LValue : : MEMORY :
if ( ! _expression . getType ( ) - > isValueType ( ) )
break ; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Location type not yet implemented. " ) ) ;
break ;
default :
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _expression . getLocation ( ) )
< < errinfo_comment ( " Unsupported location type. " ) ) ;
break ;
}
}
2014-11-20 17:33:23 +00:00
void ExpressionCompiler : : LValue : : retrieveValueIfLValueNotRequested ( Expression const & _expression )
2014-11-10 16:31:09 +00:00
{
if ( ! _expression . lvalueRequested ( ) )
{
retrieveValue ( _expression , true ) ;
reset ( ) ;
}
}
2014-11-21 18:14:56 +00:00
void ExpressionCompiler : : LValue : : fromIdentifier ( Identifier const & _identifier , Declaration const & _declaration )
2014-11-10 16:31:09 +00:00
{
if ( m_context - > isLocalVariable ( & _declaration ) )
{
m_type = STACK ;
2015-01-14 12:52:03 +00:00
m_size = _identifier . getType ( ) - > getSizeOnStack ( ) ;
2014-11-10 16:31:09 +00:00
m_baseStackOffset = m_context - > getBaseStackOffsetOfVariable ( _declaration ) ;
}
else if ( m_context - > isStateVariable ( & _declaration ) )
{
m_type = STORAGE ;
2015-01-14 12:52:03 +00:00
solAssert ( _identifier . getType ( ) - > getStorageSize ( ) < = numeric_limits < unsigned > : : max ( ) , " The storage size of " + _identifier . getType ( ) - > toString ( ) + " should fit in unsigned " ) ;
m_size = unsigned ( _identifier . getType ( ) - > getStorageSize ( ) ) ;
2014-11-10 16:31:09 +00:00
* m_context < < m_context - > getStorageLocationOfVariable ( _declaration ) ;
}
else
2014-11-21 18:14:56 +00:00
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_sourceLocation ( _identifier . getLocation ( ) )
2014-11-10 16:31:09 +00:00
< < errinfo_comment ( " Identifier type not supported or identifier not found. " ) ) ;
}
2014-10-30 00:20:32 +00:00
}
}