@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case Location : : EXTERNAL :
case Location : : BARE :
_functionCall . getExpression ( ) . accept ( * this ) ;
appendExternalFunctionCall ( function , arguments , function . getLocation ( ) = = Location : : BARE ) ;
break ;
case Location : : CREATION :
{
FunctionCallOptions options ;
options . bare = function . getLocation ( ) = = Location : : BARE ;
options . obtainAddress = [ & ] ( ) { _functionCall . getExpression ( ) . accept ( * this ) ; } ;
appendExternalFunctionCall ( function , arguments , options ) ;
_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 ;
break ;
}
case Location : : SEND :
case Location : : SET_GAS :
{
FunctionCallOptions options ;
options . bare = true ;
options . obtainAddress = [ & ] ( ) { _functionCall . getExpression ( ) . accept ( * this ) ; } ;
options . obtainValue = [ & ] ( )
{
arguments . front ( ) - > accept ( * this ) ;
appendTypeConversion ( * arguments . front ( ) - > getType ( ) ,
* function . getParameterTypes ( ) . front ( ) , true ) ;
} ;
appendExternalFunctionCall ( FunctionType ( TypePointers { } , TypePointers { } , Location : : EXTERNAL ) , { } , options ) ;
// 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 ;
break ;
}
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 ;
case Location : : SEND :
_functionCall . getExpression ( ) . accept ( * this ) ;
m_context < < u256 ( 0 ) ; // 0 gas, we do not want to execute code
arguments . front ( ) - > accept ( * this ) ;
appendTypeConversion ( * arguments . front ( ) - > getType ( ) ,
* function . getParameterTypes ( ) . front ( ) , true ) ;
appendExternalFunctionCall ( FunctionType ( TypePointers { } , TypePointers { } ,
Location : : EXTERNAL , true , true ) , { } , true ) ;
break ;
case Location : : SUICIDE :
arguments . front ( ) - > accept ( * this ) ;
appendTypeConversion ( * arguments . front ( ) - > getType ( ) , * function . getParameterTypes ( ) . front ( ) , true ) ;
@ -289,11 +333,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
static const map < Location , u256 > contractAddresses { { Location : : ECRECOVER , 1 } ,
{ Location : : SHA256 , 2 } ,
{ Location : : RIPEMD160 , 3 } } ;
u256 contractAddress = contractAddresses . find ( function . getLocation ( ) ) - > second ;
FunctionCallOptions options ;
options . bare = true ;
options . obtainAddress = [ & ] ( ) { m_context < < contractAddress ; } ;
appendExternalFunctionCall ( function , arguments , options ) ;
m_context < < contractAddresses . find ( function . getLocation ( ) ) - > second ;
appendExternalFunctionCall ( function , arguments , true ) ;
break ;
}
default :
@ -305,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
bool ExpressionCompiler : : visit ( NewExpression const & _newExpression )
{
ContractType const * type = dynamic_cast < ContractType const * > ( _newExpression . getType ( ) . get ( ) ) ;
solAssert ( type , " " ) ;
TypePointers const & types = type - > getConstructorType ( ) - > getParameterTypes ( ) ;
vector < ASTPointer < Expression const > > arguments = _newExpression . getArguments ( ) ;
solAssert ( arguments . size ( ) = = types . size ( ) , " " ) ;
// copy the contracts code into memory
bytes const & bytecode = m_context . getCompiledContract ( * _newExpression . getContract ( ) ) ;
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 dataOffset = bytecode . size ( ) ;
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 > 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 ;
dataOffset + = CompilerUtils ( m_context ) . storeInMemory ( dataOffset , c_numBytes ,
c_leftAligned , c_padToWords ) ;
}
// size, offset, endowment
m_context < < u256 ( dataOffset ) < < u256 ( 0 ) < < u256 ( 0 ) < < eth : : Instruction : : CREATE ;
// code is created for the function call (CREATION) only
return false ;
}
@ -370,6 +380,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Invalid member access to integer. " ) ) ;
break ;
case Type : : Category : : FUNCTION :
solAssert ( ! ! _memberAccess . getExpression ( ) . getType ( ) - > getMemberType ( member ) ,
" Invalid member access to function. " ) ;
break ;
case Type : : Category : : MAGIC :
// we can ignore the kind of magic and only look at the name of the member
if ( member = = " coinbase " )
@ -646,46 +660,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler : : appendExternalFunctionCall ( FunctionType const & _functionType ,
vector < ASTPointer < Expression const > > const & _arguments ,
FunctionCallOptions const & _options )
bool bare )
{
solAssert ( _arguments . size ( ) = = _functionType . getParameterTypes ( ) . size ( ) , " " ) ;
_options . obtainAddress ( ) ;
if ( ! _options . bare )
CompilerUtils ( m_context ) . storeInMemory ( 0 , CompilerUtils : : dataStartOffset ) ;
// Assumed stack content here:
// <stack top>
// value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
// function identifier [unless bare]
// contract address
unsigned dataOffset = _options . bare ? 0 : CompilerUtils : : dataStartOffset ; // reserve 4 bytes for the function's hash identifier
for ( unsigned i = 0 ; i < _arguments . size ( ) ; + + i )
unsigned gasValueSize = ( _functionType . gasSet ( ) ? 1 : 0 ) + ( _functionType . valueSet ( ) ? 1 : 0 ) ;
unsigned contractStackPos = m_context . currentToBaseStackOffset ( 1 + gasValueSize + ( bare ? 0 : 1 ) ) ;
unsigned gasStackPos = m_context . currentToBaseStackOffset ( gasValueSize ) ;
unsigned valueStackPos = m_context . currentToBaseStackOffset ( 1 ) ;
if ( ! bare )
{
_arguments [ i ] - > accept ( * this ) ;
Type const & type = * _functionType . getParameterTypes ( ) [ i ] ;
appendTypeConversion ( * _arguments [ i ] - > getType ( ) , type , true ) ;
unsigned const c_numBytes = type . getCalldataEncodedSize ( ) ;
if ( c_numBytes = = 0 | | c_numBytes > 32 )
BOOST_THROW_EXCEPTION ( CompilerError ( )
< < errinfo_sourceLocation ( _arguments [ i ] - > getLocation ( ) )
< < errinfo_comment ( " Type " + type . toString ( ) + " not yet supported. " ) ) ;
bool const c_leftAligned = type . getCategory ( ) = = Type : : Category : : STRING ;
bool const c_padToWords = true ;
dataOffset + = CompilerUtils ( m_context ) . storeInMemory ( dataOffset , c_numBytes ,
c_leftAligned , c_padToWords ) ;
// copy function identifier
m_context < < eth : : dupInstruction ( gasValueSize + 1 ) ;
CompilerUtils ( m_context ) . storeInMemory ( 0 , CompilerUtils : : dataStartOffset ) ;
}
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils : : dataStartOffset ;
dataOffset + = appendArgumentCopyToMemory ( _functionType . getParameterTypes ( ) , _arguments , dataOffset ) ;
//@todo only return the first return value for now
Type const * firstType = _functionType . getReturnParameterTypes ( ) . empty ( ) ? nullptr :
_functionType . getReturnParameterTypes ( ) . front ( ) . get ( ) ;
unsigned retSize = firstType ? CompilerUtils : : getPaddedSize ( firstType - > getCalldataEncodedSize ( ) ) : 0 ;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context < < u256 ( retSize ) < < u256 ( 0 ) < < u256 ( dataOffset ) < < u256 ( 0 ) ;
if ( _options . obtainValue )
_options . obtainValue ( ) ;
if ( _functionType . valueSet ( ) )
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( valueStackPos ) ) ;
else
m_context < < u256 ( 0 ) ;
m_context < < eth : : dupInstruction ( 6 ) ; //copy contract address
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( contractStackPos ) ) ;
m_context < < u256 ( 25 ) < < eth : : Instruction : : GAS < < eth : : Instruction : : SUB
< < eth : : Instruction : : CALL
< < eth : : Instruction : : POP // @todo do not ignore failure indicator
< < eth : : Instruction : : POP ; // pop contract address
if ( _functionType . gasSet ( ) )
m_context < < eth : : dupInstruction ( m_context . baseToCurrentStackOffset ( gasStackPos ) ) ;
else
// send all gas except for the 21 needed to execute "SUB" and "CALL"
m_context < < u256 ( 21 ) < < eth : : Instruction : : GAS < < eth : : Instruction : : SUB ;
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
if ( retSize > 0 )
{
@ -694,6 +722,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
}
}
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 ;
}
ExpressionCompiler : : LValue : : LValue ( CompilerContext & _compilerContext , LValueType _type , Type const & _dataType ,
unsigned _baseStackOffset ) :
m_context ( & _compilerContext ) , m_type ( _type ) , m_baseStackOffset ( _baseStackOffset ) ,