2016-05-19 17:32:14 +00:00
/*
2016-11-18 23:13:20 +00:00
This file is part of solidity .
2016-05-19 17:32:14 +00:00
2016-11-18 23:13:20 +00:00
solidity is free software : you can redistribute it and / or modify
2016-05-19 17:32:14 +00:00
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 .
2016-11-18 23:13:20 +00:00
solidity is distributed in the hope that it will be useful ,
2016-05-19 17:32:14 +00:00
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
2016-11-18 23:13:20 +00:00
along with solidity . If not , see < http : //www.gnu.org/licenses/>.
2016-05-19 17:32:14 +00:00
*/
/**
* @ author Christian < c @ ethdev . com >
* @ date 2014
* Solidity compiler .
*/
2018-12-17 16:06:11 +00:00
# include <libsolidity/ast/AST.h>
2019-04-15 13:33:39 +00:00
# include <libsolidity/ast/TypeProvider.h>
2018-12-17 16:06:11 +00:00
# include <libsolidity/codegen/CompilerUtils.h>
2016-05-19 17:32:14 +00:00
# include <libsolidity/codegen/ContractCompiler.h>
# include <libsolidity/codegen/ExpressionCompiler.h>
2017-05-24 16:34:19 +00:00
2019-02-13 10:35:49 +00:00
# include <libyul/backends/evm/AsmCodeGen.h>
2017-05-24 16:34:19 +00:00
# include <libevmasm/Instruction.h>
# include <libevmasm/Assembly.h>
# include <libevmasm/GasMeter.h>
2019-02-13 10:35:49 +00:00
2018-12-17 16:06:11 +00:00
# include <liblangutil/ErrorReporter.h>
2017-05-24 16:34:19 +00:00
# include <boost/range/adaptor/reversed.hpp>
# include <algorithm>
2016-05-19 17:32:14 +00:00
using namespace std ;
using namespace dev ;
2018-11-14 16:11:55 +00:00
using namespace langutil ;
2019-03-28 11:47:21 +00:00
using namespace dev : : eth ;
2016-05-19 17:32:14 +00:00
using namespace dev : : solidity ;
2017-08-29 00:01:47 +00:00
namespace
{
2016-05-19 17:32:14 +00:00
/**
* Simple helper class to ensure that the stack height is the same at certain places in the code .
*/
class StackHeightChecker
{
public :
2017-08-21 14:42:17 +00:00
explicit StackHeightChecker ( CompilerContext const & _context ) :
2016-05-19 17:32:14 +00:00
m_context ( _context ) , stackHeight ( m_context . stackHeight ( ) ) { }
2018-08-08 14:26:30 +00:00
void check ( ) { solAssert ( m_context . stackHeight ( ) = = stackHeight , std : : string ( " I sense a disturbance in the stack: " ) + to_string ( m_context . stackHeight ( ) ) + " vs " + to_string ( stackHeight ) ) ; }
2016-05-19 17:32:14 +00:00
private :
CompilerContext const & m_context ;
unsigned stackHeight ;
} ;
2017-08-29 00:01:47 +00:00
}
2016-05-19 17:32:14 +00:00
void ContractCompiler : : compileContract (
ContractDefinition const & _contract ,
2019-01-10 15:44:31 +00:00
map < ContractDefinition const * , shared_ptr < Compiler const > > const & _otherCompilers
2016-05-19 17:32:14 +00:00
)
{
CompilerContext : : LocationSetter locationSetter ( m_context , _contract ) ;
2017-11-14 11:58:04 +00:00
if ( _contract . isLibrary ( ) )
// Check whether this is a call (true) or a delegatecall (false).
// This has to be the first code in the contract.
appendDelegatecallCheck ( ) ;
2019-01-10 15:44:31 +00:00
initializeContext ( _contract , _otherCompilers ) ;
2018-06-19 17:30:38 +00:00
// This generates the dispatch function for externally visible functions
// and adds the function to the compilation queue. Additionally internal functions,
// which are referenced directly or indirectly will be added.
2016-05-19 17:32:14 +00:00
appendFunctionSelector ( _contract ) ;
2018-06-19 17:30:38 +00:00
// This processes the above populated queue until it is empty.
2016-05-19 17:32:14 +00:00
appendMissingFunctions ( ) ;
}
size_t ContractCompiler : : compileConstructor (
ContractDefinition const & _contract ,
2019-01-10 15:44:31 +00:00
std : : map < ContractDefinition const * , shared_ptr < Compiler const > > const & _otherCompilers
2016-05-19 17:32:14 +00:00
)
{
CompilerContext : : LocationSetter locationSetter ( m_context , _contract ) ;
2017-11-14 11:58:04 +00:00
if ( _contract . isLibrary ( ) )
return deployLibrary ( _contract ) ;
else
{
2019-01-10 15:44:31 +00:00
initializeContext ( _contract , _otherCompilers ) ;
2017-11-14 11:58:04 +00:00
return packIntoContractCreator ( _contract ) ;
}
2016-05-19 17:32:14 +00:00
}
void ContractCompiler : : initializeContext (
ContractDefinition const & _contract ,
2019-01-10 15:44:31 +00:00
map < ContractDefinition const * , shared_ptr < Compiler const > > const & _otherCompilers
2016-05-19 17:32:14 +00:00
)
{
2017-08-10 15:56:04 +00:00
m_context . setExperimentalFeatures ( _contract . sourceUnit ( ) . annotation ( ) . experimentalFeatures ) ;
2019-01-10 15:44:31 +00:00
m_context . setOtherCompilers ( _otherCompilers ) ;
2016-05-19 17:32:14 +00:00
m_context . setInheritanceHierarchy ( _contract . annotation ( ) . linearizedBaseContracts ) ;
CompilerUtils ( m_context ) . initialiseFreeMemoryPointer ( ) ;
registerStateVariables ( _contract ) ;
m_context . resetVisitedNodes ( & _contract ) ;
}
2016-11-17 17:23:31 +00:00
void ContractCompiler : : appendCallValueCheck ( )
{
// Throw if function is not payable but call contained ether.
m_context < < Instruction : : CALLVALUE ;
2017-12-30 19:13:41 +00:00
// TODO: error message?
2017-05-24 09:47:43 +00:00
m_context . appendConditionalRevert ( ) ;
2016-11-17 17:23:31 +00:00
}
2016-05-19 17:32:14 +00:00
void ContractCompiler : : appendInitAndConstructorCode ( ContractDefinition const & _contract )
{
2017-11-14 11:58:04 +00:00
solAssert ( ! _contract . isLibrary ( ) , " Tried to initialize library. " ) ;
2017-08-25 16:58:12 +00:00
CompilerContext : : LocationSetter locationSetter ( m_context , _contract ) ;
2018-04-05 14:25:20 +00:00
m_baseArguments = & _contract . annotation ( ) . baseConstructorArguments ;
2016-05-19 17:32:14 +00:00
// Initialization of state variables in base-to-derived order.
2018-04-05 14:25:20 +00:00
for ( ContractDefinition const * contract : boost : : adaptors : : reverse (
_contract . annotation ( ) . linearizedBaseContracts
) )
2016-05-19 17:32:14 +00:00
initializeStateVariables ( * contract ) ;
if ( FunctionDefinition const * constructor = _contract . constructor ( ) )
appendConstructor ( * constructor ) ;
else if ( auto c = m_context . nextConstructor ( _contract ) )
appendBaseConstructor ( * c ) ;
2016-11-15 10:24:53 +00:00
else
2016-11-17 17:23:31 +00:00
appendCallValueCheck ( ) ;
2016-05-19 17:32:14 +00:00
}
2016-11-10 17:16:21 +00:00
size_t ContractCompiler : : packIntoContractCreator ( ContractDefinition const & _contract )
2016-05-19 17:32:14 +00:00
{
2016-11-10 17:16:21 +00:00
solAssert ( ! ! m_runtimeCompiler , " " ) ;
2017-11-14 11:58:04 +00:00
solAssert ( ! _contract . isLibrary ( ) , " Tried to use contract creator or library. " ) ;
2016-11-10 17:16:21 +00:00
2016-05-19 17:32:14 +00:00
appendInitAndConstructorCode ( _contract ) ;
2016-11-10 17:16:21 +00:00
// We jump to the deploy routine because we first have to append all missing functions,
// which can cause further functions to be added to the runtime context.
eth : : AssemblyItem deployRoutine = m_context . appendJumpToNew ( ) ;
// We have to include copies of functions in the construction time and runtime context
// because of absolute jumps.
appendMissingFunctions ( ) ;
m_runtimeCompiler - > appendMissingFunctions ( ) ;
2017-08-25 16:58:12 +00:00
CompilerContext : : LocationSetter locationSetter ( m_context , _contract ) ;
2016-11-10 17:16:21 +00:00
m_context < < deployRoutine ;
solAssert ( m_context . runtimeSub ( ) ! = size_t ( - 1 ) , " Runtime sub not registered " ) ;
2016-11-11 10:41:50 +00:00
m_context . pushSubroutineSize ( m_context . runtimeSub ( ) ) ;
m_context < < Instruction : : DUP1 ;
m_context . pushSubroutineOffset ( m_context . runtimeSub ( ) ) ;
m_context < < u256 ( 0 ) < < Instruction : : CODECOPY ;
2016-05-19 17:32:14 +00:00
m_context < < u256 ( 0 ) < < Instruction : : RETURN ;
2016-11-10 17:16:21 +00:00
return m_context . runtimeSub ( ) ;
2016-05-19 17:32:14 +00:00
}
2017-11-14 11:58:04 +00:00
size_t ContractCompiler : : deployLibrary ( ContractDefinition const & _contract )
{
solAssert ( ! ! m_runtimeCompiler , " " ) ;
solAssert ( _contract . isLibrary ( ) , " Tried to deploy contract as library. " ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _contract ) ;
solAssert ( m_context . runtimeSub ( ) ! = size_t ( - 1 ) , " Runtime sub not registered " ) ;
m_context . pushSubroutineSize ( m_context . runtimeSub ( ) ) ;
m_context . pushSubroutineOffset ( m_context . runtimeSub ( ) ) ;
2019-02-25 23:59:13 +00:00
// This code replaces the address added by appendDeployTimeAddress().
2017-11-14 11:58:04 +00:00
m_context . appendInlineAssembly ( R " (
{
// If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
// without the need for a shift.
let codepos : = 11
codecopy ( codepos , subOffset , subSize )
// Check that the first opcode is a PUSH20
2019-02-25 23:59:13 +00:00
if iszero ( eq ( 0x73 , byte ( 0 , mload ( codepos ) ) ) ) { invalid ( ) }
2017-11-14 11:58:04 +00:00
mstore ( 0 , address ( ) )
mstore8 ( codepos , 0x73 )
return ( codepos , subSize )
}
) " , { " subSize " , " subOffset " });
return m_context . runtimeSub ( ) ;
}
2016-05-19 17:32:14 +00:00
void ContractCompiler : : appendBaseConstructor ( FunctionDefinition const & _constructor )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _constructor ) ;
FunctionType constructorType ( _constructor ) ;
if ( ! constructorType . parameterTypes ( ) . empty ( ) )
{
2018-04-05 14:25:20 +00:00
solAssert ( m_baseArguments , " " ) ;
solAssert ( m_baseArguments - > count ( & _constructor ) , " " ) ;
std : : vector < ASTPointer < Expression > > const * arguments = nullptr ;
ASTNode const * baseArgumentNode = m_baseArguments - > at ( & _constructor ) ;
if ( auto inheritanceSpecifier = dynamic_cast < InheritanceSpecifier const * > ( baseArgumentNode ) )
arguments = inheritanceSpecifier - > arguments ( ) ;
else if ( auto modifierInvocation = dynamic_cast < ModifierInvocation const * > ( baseArgumentNode ) )
2018-04-10 09:22:26 +00:00
arguments = modifierInvocation - > arguments ( ) ;
2016-05-19 17:32:14 +00:00
solAssert ( arguments , " " ) ;
2018-03-16 18:02:15 +00:00
solAssert ( arguments - > size ( ) = = constructorType . parameterTypes ( ) . size ( ) , " " ) ;
2016-05-19 17:32:14 +00:00
for ( unsigned i = 0 ; i < arguments - > size ( ) ; + + i )
compileExpression ( * ( arguments - > at ( i ) ) , constructorType . parameterTypes ( ) [ i ] ) ;
}
_constructor . accept ( * this ) ;
}
void ContractCompiler : : appendConstructor ( FunctionDefinition const & _constructor )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _constructor ) ;
2016-11-15 10:24:53 +00:00
if ( ! _constructor . isPayable ( ) )
2016-11-17 17:23:31 +00:00
appendCallValueCheck ( ) ;
2016-05-19 17:32:14 +00:00
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
if ( ! _constructor . parameters ( ) . empty ( ) )
{
CompilerUtils ( m_context ) . fetchFreeMemoryPointer ( ) ;
2019-05-15 16:31:07 +00:00
// CODESIZE returns the actual size of the code,
// which is the size of the generated code (``programSize``)
// plus the constructor arguments added to the transaction payload.
m_context . appendProgramSize ( ) ;
m_context < < Instruction : : CODESIZE < < Instruction : : SUB ;
2016-05-19 17:32:14 +00:00
// stack: <memptr> <argument size>
m_context < < Instruction : : DUP1 ;
m_context . appendProgramSize ( ) ;
m_context < < Instruction : : DUP4 < < Instruction : : CODECOPY ;
2019-05-15 16:31:07 +00:00
// stack: <memptr> <argument size>
m_context < < Instruction : : DUP2 < < Instruction : : DUP2 < < Instruction : : ADD ;
// stack: <memptr> <argument size> <mem end>
2016-05-19 17:32:14 +00:00
CompilerUtils ( m_context ) . storeFreeMemoryPointer ( ) ;
2019-05-15 16:31:07 +00:00
// stack: <memptr> <argument size>
2018-02-20 09:13:58 +00:00
CompilerUtils ( m_context ) . abiDecode ( FunctionType ( _constructor ) . parameterTypes ( ) , true ) ;
2016-05-19 17:32:14 +00:00
}
_constructor . accept ( * this ) ;
}
2017-11-14 11:58:04 +00:00
void ContractCompiler : : appendDelegatecallCheck ( )
{
// Special constant that will be replaced by the address at deploy time.
// At compilation time, this is just "PUSH20 00...000".
m_context . appendDeployTimeAddress ( ) ;
m_context < < Instruction : : ADDRESS < < Instruction : : EQ ;
// The result on the stack is
// "We have not been called via DELEGATECALL".
}
2018-09-10 10:09:42 +00:00
void ContractCompiler : : appendInternalSelector (
map < FixedHash < 4 > , eth : : AssemblyItem const > const & _entryPoints ,
vector < FixedHash < 4 > > const & _ids ,
eth : : AssemblyItem const & _notFoundTag ,
size_t _runs
)
{
// Code for selecting from n functions without split:
// n times: dup1, push4 <id_i>, eq, push2/3 <tag_i>, jumpi
// push2/3 <notfound> jump
// (called SELECT[n])
// Code for selecting from n functions with split:
// dup1, push4 <pivot>, gt, push2/3<tag_less>, jumpi
// SELECT[n/2]
// tag_less:
// SELECT[n/2]
//
// This means each split adds 16-18 bytes of additional code (note the additional jump out!)
// The average execution cost if we do not split at all are:
// (3 + 3 + 3 + 3 + 10) * n/2 = 24 * n/2 = 12 * n
// If we split once:
// (3 + 3 + 3 + 3 + 10) + 24 * n/4 = 24 * (n/4 + 1) = 6 * n + 24;
//
// We should split if
// _runs * 12 * n > _runs * (6 * n + 24) + 17 * createDataGas
// <=> _runs * 6 * (n - 4) > 17 * createDataGas
//
// Which also means that the execution itself is not profitable
// unless we have at least 5 functions.
// Start with some comparisons to avoid overflow, then do the actual comparison.
bool split = false ;
if ( _ids . size ( ) < = 4 )
split = false ;
else if ( _runs > ( 17 * eth : : GasCosts : : createDataGas ) / 6 )
split = true ;
else
split = ( _runs * 6 * ( _ids . size ( ) - 4 ) > 17 * eth : : GasCosts : : createDataGas ) ;
if ( split )
{
size_t pivotIndex = _ids . size ( ) / 2 ;
FixedHash < 4 > pivot { _ids . at ( pivotIndex ) } ;
m_context < < dupInstruction ( 1 ) < < u256 ( FixedHash < 4 > : : Arith ( pivot ) ) < < Instruction : : GT ;
eth : : AssemblyItem lessTag { m_context . appendConditionalJump ( ) } ;
// Here, we have funid >= pivot
vector < FixedHash < 4 > > larger { _ids . begin ( ) + pivotIndex , _ids . end ( ) } ;
appendInternalSelector ( _entryPoints , larger , _notFoundTag , _runs ) ;
m_context < < lessTag ;
// Here, we have funid < pivot
vector < FixedHash < 4 > > smaller { _ids . begin ( ) , _ids . begin ( ) + pivotIndex } ;
appendInternalSelector ( _entryPoints , smaller , _notFoundTag , _runs ) ;
}
else
{
for ( auto const & id : _ids )
{
m_context < < dupInstruction ( 1 ) < < u256 ( FixedHash < 4 > : : Arith ( id ) ) < < Instruction : : EQ ;
m_context . appendConditionalJumpTo ( _entryPoints . at ( id ) ) ;
}
m_context . appendJumpTo ( _notFoundTag ) ;
}
}
2018-12-11 15:42:09 +00:00
namespace
{
// Helper function to check if any function is payable
bool hasPayableFunctions ( ContractDefinition const & _contract )
{
FunctionDefinition const * fallback = _contract . fallbackFunction ( ) ;
if ( fallback & & fallback - > isPayable ( ) )
return true ;
for ( auto const & it : _contract . interfaceFunctions ( ) )
if ( it . second - > isPayable ( ) )
return true ;
return false ;
}
}
2016-05-19 17:32:14 +00:00
void ContractCompiler : : appendFunctionSelector ( ContractDefinition const & _contract )
{
map < FixedHash < 4 > , FunctionTypePointer > interfaceFunctions = _contract . interfaceFunctions ( ) ;
2019-02-14 10:37:24 +00:00
map < FixedHash < 4 > , eth : : AssemblyItem const > callDataUnpackerEntryPoints ;
2016-05-19 17:32:14 +00:00
2017-11-14 11:58:04 +00:00
if ( _contract . isLibrary ( ) )
{
solAssert ( m_context . stackHeight ( ) = = 1 , " CALL / DELEGATECALL flag expected. " ) ;
}
2016-05-19 17:32:14 +00:00
FunctionDefinition const * fallback = _contract . fallbackFunction ( ) ;
2018-12-11 15:42:09 +00:00
solAssert ( ! _contract . isLibrary ( ) | | ! fallback , " Libraries can't have fallback functions " ) ;
bool needToAddCallvalueCheck = true ;
if ( ! hasPayableFunctions ( _contract ) & & ! interfaceFunctions . empty ( ) & & ! _contract . isLibrary ( ) )
{
appendCallValueCheck ( ) ;
needToAddCallvalueCheck = false ;
}
2016-05-19 17:32:14 +00:00
eth : : AssemblyItem notFound = m_context . newTag ( ) ;
2017-10-11 08:45:24 +00:00
// directly jump to fallback if the data is too short to contain a function selector
// also guards against short data
m_context < < u256 ( 4 ) < < Instruction : : CALLDATASIZE < < Instruction : : LT ;
m_context . appendConditionalJumpTo ( notFound ) ;
2016-05-19 17:32:14 +00:00
// retrieve the function signature hash from the calldata
if ( ! interfaceFunctions . empty ( ) )
2018-12-11 15:42:09 +00:00
{
2016-05-19 17:32:14 +00:00
CompilerUtils ( m_context ) . loadFromMemory ( 0 , IntegerType ( CompilerUtils : : dataStartOffset * 8 ) , true ) ;
2018-12-11 15:42:09 +00:00
// stack now is: <can-call-non-view-functions>? <funhash>
vector < FixedHash < 4 > > sortedIDs ;
for ( auto const & it : interfaceFunctions )
{
callDataUnpackerEntryPoints . emplace ( it . first , m_context . newTag ( ) ) ;
sortedIDs . emplace_back ( it . first ) ;
}
std : : sort ( sortedIDs . begin ( ) , sortedIDs . end ( ) ) ;
2019-02-21 17:13:52 +00:00
appendInternalSelector ( callDataUnpackerEntryPoints , sortedIDs , notFound , m_optimiserSettings . expectedExecutionsPerDeployment ) ;
2016-05-19 17:32:14 +00:00
}
m_context < < notFound ;
2018-12-11 15:42:09 +00:00
2016-05-19 17:32:14 +00:00
if ( fallback )
{
2017-11-14 11:58:04 +00:00
solAssert ( ! _contract . isLibrary ( ) , " " ) ;
2018-12-11 15:42:09 +00:00
if ( ! fallback - > isPayable ( ) & & needToAddCallvalueCheck )
2016-11-17 17:23:31 +00:00
appendCallValueCheck ( ) ;
2017-06-29 08:48:53 +00:00
solAssert ( fallback - > isFallback ( ) , " " ) ;
2017-06-27 12:29:04 +00:00
solAssert ( FunctionType ( * fallback ) . parameterTypes ( ) . empty ( ) , " " ) ;
solAssert ( FunctionType ( * fallback ) . returnParameterTypes ( ) . empty ( ) , " " ) ;
2017-06-29 08:48:53 +00:00
fallback - > accept ( * this ) ;
2017-06-27 12:29:04 +00:00
m_context < < Instruction : : STOP ;
2016-05-19 17:32:14 +00:00
}
else
2017-12-30 19:13:41 +00:00
// TODO: error message here?
2017-05-24 09:47:43 +00:00
m_context . appendRevert ( ) ;
2016-05-19 17:32:14 +00:00
for ( auto const & it : interfaceFunctions )
{
FunctionTypePointer const & functionType = it . second ;
solAssert ( functionType - > hasDeclaration ( ) , " " ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , functionType - > declaration ( ) ) ;
2016-06-18 10:11:55 +00:00
2016-05-19 17:32:14 +00:00
m_context < < callDataUnpackerEntryPoints . at ( it . first ) ;
2017-11-14 11:58:04 +00:00
if ( _contract . isLibrary ( ) & & functionType - > stateMutability ( ) > StateMutability : : View )
{
// If the function is not a view function and is called without DELEGATECALL,
// we revert.
m_context < < dupInstruction ( 2 ) ;
m_context . appendConditionalRevert ( ) ;
}
2017-06-23 15:20:07 +00:00
m_context . setStackOffset ( 0 ) ;
2016-09-15 15:44:32 +00:00
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
2018-12-11 15:42:09 +00:00
if ( ! functionType - > isPayable ( ) & & ! _contract . isLibrary ( ) & & needToAddCallvalueCheck )
2016-11-17 17:23:31 +00:00
appendCallValueCheck ( ) ;
2016-06-18 10:11:55 +00:00
2017-06-28 15:55:20 +00:00
// Return tag is used to jump out of the function.
2016-08-26 18:37:10 +00:00
eth : : AssemblyItem returnTag = m_context . pushNewTag ( ) ;
2017-06-30 12:09:35 +00:00
if ( ! functionType - > parameterTypes ( ) . empty ( ) )
{
// Parameter for calldataUnpacker
m_context < < CompilerUtils : : dataStartOffset ;
2017-12-13 16:23:37 +00:00
m_context < < Instruction : : DUP1 < < Instruction : : CALLDATASIZE < < Instruction : : SUB ;
2018-02-20 09:13:58 +00:00
CompilerUtils ( m_context ) . abiDecode ( functionType - > parameterTypes ( ) ) ;
2017-06-30 12:09:35 +00:00
}
2018-11-13 09:53:40 +00:00
m_context . appendJumpTo (
m_context . functionEntryLabel ( functionType - > declaration ( ) ) ,
eth : : AssemblyItem : : JumpType : : IntoFunction
) ;
2016-05-19 17:32:14 +00:00
m_context < < returnTag ;
2017-06-28 15:55:20 +00:00
// Return tag and input parameters get consumed.
2017-06-23 15:20:07 +00:00
m_context . adjustStackOffset (
CompilerUtils ( m_context ) . sizeOnStack ( functionType - > returnParameterTypes ( ) ) -
CompilerUtils ( m_context ) . sizeOnStack ( functionType - > parameterTypes ( ) ) -
1
) ;
2017-06-28 15:55:20 +00:00
// Consumes the return parameters.
2016-05-19 17:32:14 +00:00
appendReturnValuePacker ( functionType - > returnParameterTypes ( ) , _contract . isLibrary ( ) ) ;
}
}
void ContractCompiler : : appendReturnValuePacker ( TypePointers const & _typeParameters , bool _isLibrary )
{
CompilerUtils utils ( m_context ) ;
if ( _typeParameters . empty ( ) )
m_context < < Instruction : : STOP ;
else
{
utils . fetchFreeMemoryPointer ( ) ;
//@todo optimization: if we return a single memory array, there should be enough space before
// its data to add the needed parts and we avoid a memory copy.
2017-09-26 21:10:30 +00:00
utils . abiEncode ( _typeParameters , _typeParameters , _isLibrary ) ;
2016-05-19 17:32:14 +00:00
utils . toSizeAfterFreeMemoryPointer ( ) ;
m_context < < Instruction : : RETURN ;
}
}
void ContractCompiler : : registerStateVariables ( ContractDefinition const & _contract )
{
for ( auto const & var : ContractType ( _contract ) . stateVariables ( ) )
m_context . addStateVariable ( * get < 0 > ( var ) , get < 1 > ( var ) , get < 2 > ( var ) ) ;
}
void ContractCompiler : : initializeStateVariables ( ContractDefinition const & _contract )
{
2017-11-14 11:58:04 +00:00
solAssert ( ! _contract . isLibrary ( ) , " Tried to initialize state variables of library. " ) ;
2016-05-19 17:32:14 +00:00
for ( VariableDeclaration const * variable : _contract . stateVariables ( ) )
if ( variable - > value ( ) & & ! variable - > isConstant ( ) )
2019-02-21 17:13:52 +00:00
ExpressionCompiler ( m_context , m_optimiserSettings . runOrderLiterals ) . appendStateVariableInitialization ( * variable ) ;
2016-05-19 17:32:14 +00:00
}
bool ContractCompiler : : visit ( VariableDeclaration const & _variableDeclaration )
{
solAssert ( _variableDeclaration . isStateVariable ( ) , " Compiler visit to non-state variable declaration. " ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _variableDeclaration ) ;
m_context . startFunction ( _variableDeclaration ) ;
m_breakTags . clear ( ) ;
m_continueTags . clear ( ) ;
if ( _variableDeclaration . isConstant ( ) )
2019-02-21 17:13:52 +00:00
ExpressionCompiler ( m_context , m_optimiserSettings . runOrderLiterals ) . appendConstStateVariableAccessor ( _variableDeclaration ) ;
2016-05-19 17:32:14 +00:00
else
2019-02-21 17:13:52 +00:00
ExpressionCompiler ( m_context , m_optimiserSettings . runOrderLiterals ) . appendStateVariableAccessor ( _variableDeclaration ) ;
2016-05-19 17:32:14 +00:00
return false ;
}
bool ContractCompiler : : visit ( FunctionDefinition const & _function )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _function ) ;
m_context . startFunction ( _function ) ;
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
2018-05-03 14:40:33 +00:00
// reserve additional slots: [retarg0] ... [retargm]
2016-05-19 17:32:14 +00:00
unsigned parametersSize = CompilerUtils : : sizeOnStack ( _function . parameters ( ) ) ;
if ( ! _function . isConstructor ( ) )
// adding 1 for return address.
m_context . adjustStackOffset ( parametersSize + 1 ) ;
for ( ASTPointer < VariableDeclaration const > const & variable : _function . parameters ( ) )
{
m_context . addVariable ( * variable , parametersSize ) ;
parametersSize - = variable - > annotation ( ) . type - > sizeOnStack ( ) ;
}
for ( ASTPointer < VariableDeclaration const > const & variable : _function . returnParameters ( ) )
appendStackVariableInitialisation ( * variable ) ;
if ( _function . isConstructor ( ) )
if ( auto c = m_context . nextConstructor ( dynamic_cast < ContractDefinition const & > ( * _function . scope ( ) ) ) )
appendBaseConstructor ( * c ) ;
2018-07-04 12:14:41 +00:00
solAssert ( m_returnTags . empty ( ) , " " ) ;
2016-05-19 17:32:14 +00:00
m_breakTags . clear ( ) ;
m_continueTags . clear ( ) ;
m_currentFunction = & _function ;
2016-08-06 12:48:59 +00:00
m_modifierDepth = - 1 ;
2018-05-03 14:40:33 +00:00
m_scopeStackHeight . clear ( ) ;
2016-05-19 17:32:14 +00:00
appendModifierOrFunctionCode ( ) ;
2016-08-06 12:48:59 +00:00
solAssert ( m_returnTags . empty ( ) , " " ) ;
2016-05-19 17:32:14 +00:00
// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
// that shows the target positions of the elements, where "-1" denotes that this element needs
// to be removed from the stack.
// Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work.
unsigned const c_argumentsSize = CompilerUtils : : sizeOnStack ( _function . parameters ( ) ) ;
unsigned const c_returnValuesSize = CompilerUtils : : sizeOnStack ( _function . returnParameters ( ) ) ;
vector < int > stackLayout ;
stackLayout . push_back ( c_returnValuesSize ) ; // target of return address
stackLayout + = vector < int > ( c_argumentsSize , - 1 ) ; // discard all arguments
for ( unsigned i = 0 ; i < c_returnValuesSize ; + + i )
stackLayout . push_back ( i ) ;
2017-01-20 18:01:19 +00:00
if ( stackLayout . size ( ) > 17 )
BOOST_THROW_EXCEPTION (
CompilerError ( ) < <
errinfo_sourceLocation ( _function . location ( ) ) < <
errinfo_comment ( " Stack too deep, try removing local variables. " )
) ;
2016-05-19 17:32:14 +00:00
while ( stackLayout . back ( ) ! = int ( stackLayout . size ( ) - 1 ) )
if ( stackLayout . back ( ) < 0 )
{
m_context < < Instruction : : POP ;
stackLayout . pop_back ( ) ;
}
else
{
m_context < < swapInstruction ( stackLayout . size ( ) - stackLayout . back ( ) - 1 ) ;
swap ( stackLayout [ stackLayout . back ( ) ] , stackLayout . back ( ) ) ;
}
2018-05-03 14:40:33 +00:00
for ( int i = 0 ; i < int ( stackLayout . size ( ) ) ; + + i )
if ( stackLayout [ i ] ! = i )
solAssert ( false , " Invalid stack layout on cleanup. " ) ;
2016-05-19 17:32:14 +00:00
for ( ASTPointer < VariableDeclaration const > const & variable : _function . parameters ( ) + _function . returnParameters ( ) )
m_context . removeVariable ( * variable ) ;
m_context . adjustStackOffset ( - ( int ) c_returnValuesSize ) ;
2017-06-29 08:48:53 +00:00
/// The constructor and the fallback function doesn't to jump out.
2018-07-10 16:39:26 +00:00
if ( ! _function . isConstructor ( ) )
{
solAssert ( m_context . numberOfLocalVariables ( ) = = 0 , " " ) ;
if ( ! _function . isFallback ( ) )
m_context . appendJump ( eth : : AssemblyItem : : JumpType : : OutOfFunction ) ;
}
2018-05-03 14:40:33 +00:00
2016-05-19 17:32:14 +00:00
return false ;
}
bool ContractCompiler : : visit ( InlineAssembly const & _inlineAssembly )
{
unsigned startStackHeight = m_context . stackHeight ( ) ;
2018-10-15 09:58:51 +00:00
yul : : ExternalIdentifierAccess identifierAccess ;
2018-11-21 11:42:34 +00:00
identifierAccess . resolve = [ & ] ( yul : : Identifier const & _identifier , yul : : IdentifierContext , bool )
2017-03-14 14:41:23 +00:00
{
auto ref = _inlineAssembly . annotation ( ) . externalReferences . find ( & _identifier ) ;
if ( ref = = _inlineAssembly . annotation ( ) . externalReferences . end ( ) )
return size_t ( - 1 ) ;
return ref - > second . valueSize ;
} ;
2018-11-21 11:42:34 +00:00
identifierAccess . generateCode = [ & ] ( yul : : Identifier const & _identifier , yul : : IdentifierContext _context , yul : : AbstractAssembly & _assembly )
2017-03-14 14:41:23 +00:00
{
auto ref = _inlineAssembly . annotation ( ) . externalReferences . find ( & _identifier ) ;
solAssert ( ref ! = _inlineAssembly . annotation ( ) . externalReferences . end ( ) , " " ) ;
Declaration const * decl = ref - > second . declaration ;
solAssert ( ! ! decl , " " ) ;
2018-10-15 09:58:51 +00:00
if ( _context = = yul : : IdentifierContext : : RValue )
2017-03-14 14:41:23 +00:00
{
2017-04-28 16:15:18 +00:00
int const depositBefore = _assembly . stackHeight ( ) ;
2017-03-14 14:41:23 +00:00
solAssert ( ! ! decl - > type ( ) , " Type of declaration required but not yet determined. " ) ;
if ( FunctionDefinition const * functionDef = dynamic_cast < FunctionDefinition const * > ( decl ) )
2016-05-19 17:32:14 +00:00
{
2017-04-21 17:13:46 +00:00
solAssert ( ! ref - > second . isOffset & & ! ref - > second . isSlot , " " ) ;
2017-03-14 14:41:23 +00:00
functionDef = & m_context . resolveVirtualFunction ( * functionDef ) ;
2017-04-28 16:15:18 +00:00
auto functionEntryLabel = m_context . functionEntryLabel ( * functionDef ) . pushTag ( ) ;
solAssert ( functionEntryLabel . data ( ) < = std : : numeric_limits < size_t > : : max ( ) , " " ) ;
_assembly . appendLabelReference ( size_t ( functionEntryLabel . data ( ) ) ) ;
2017-03-14 14:41:23 +00:00
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if ( CompilerContext * rtc = m_context . runtimeContext ( ) )
2016-11-10 17:16:21 +00:00
{
2017-04-28 16:15:18 +00:00
_assembly . appendConstant ( u256 ( 1 ) < < 32 ) ;
_assembly . appendInstruction ( Instruction : : MUL ) ;
auto runtimeEntryLabel = rtc - > functionEntryLabel ( * functionDef ) . toSubAssemblyTag ( m_context . runtimeSub ( ) ) ;
solAssert ( runtimeEntryLabel . data ( ) < = std : : numeric_limits < size_t > : : max ( ) , " " ) ;
_assembly . appendLabelReference ( size_t ( runtimeEntryLabel . data ( ) ) ) ;
_assembly . appendInstruction ( Instruction : : OR ) ;
2017-03-14 14:41:23 +00:00
}
}
else if ( auto variable = dynamic_cast < VariableDeclaration const * > ( decl ) )
{
solAssert ( ! variable - > isConstant ( ) , " " ) ;
2017-04-21 17:13:46 +00:00
if ( m_context . isStateVariable ( decl ) )
{
auto const & location = m_context . storageLocationOfVariable ( * decl ) ;
if ( ref - > second . isSlot )
m_context < < location . first ;
else if ( ref - > second . isOffset )
m_context < < u256 ( location . second ) ;
else
solAssert ( false , " " ) ;
}
else if ( m_context . isLocalVariable ( decl ) )
{
2017-04-28 16:15:18 +00:00
int stackDiff = _assembly . stackHeight ( ) - m_context . baseStackOffsetOfVariable ( * variable ) ;
2017-04-21 17:13:46 +00:00
if ( ref - > second . isSlot | | ref - > second . isOffset )
{
2017-04-25 10:56:34 +00:00
solAssert ( variable - > type ( ) - > dataStoredIn ( DataLocation : : Storage ) , " " ) ;
unsigned size = variable - > type ( ) - > sizeOnStack ( ) ;
if ( size = = 2 )
{
// slot plus offset
if ( ref - > second . isOffset )
stackDiff - - ;
}
else
{
solAssert ( size = = 1 , " " ) ;
// only slot, offset is zero
if ( ref - > second . isOffset )
{
2017-04-28 16:15:18 +00:00
_assembly . appendConstant ( u256 ( 0 ) ) ;
2017-04-25 10:56:34 +00:00
return ;
}
}
2017-04-21 17:13:46 +00:00
}
else
solAssert ( variable - > type ( ) - > sizeOnStack ( ) = = 1 , " " ) ;
if ( stackDiff < 1 | | stackDiff > 16 )
BOOST_THROW_EXCEPTION (
CompilerError ( ) < <
errinfo_sourceLocation ( _inlineAssembly . location ( ) ) < <
errinfo_comment ( " Stack too deep, try removing local variables. " )
) ;
solAssert ( variable - > type ( ) - > sizeOnStack ( ) = = 1 , " " ) ;
2017-04-28 16:15:18 +00:00
_assembly . appendInstruction ( dupInstruction ( stackDiff ) ) ;
2017-04-21 17:13:46 +00:00
}
else
solAssert ( false , " " ) ;
2017-03-14 14:41:23 +00:00
}
else if ( auto contract = dynamic_cast < ContractDefinition const * > ( decl ) )
{
2017-04-21 17:13:46 +00:00
solAssert ( ! ref - > second . isOffset & & ! ref - > second . isSlot , " " ) ;
2017-03-14 14:41:23 +00:00
solAssert ( contract - > isLibrary ( ) , " " ) ;
2017-04-28 16:15:18 +00:00
_assembly . appendLinkerSymbol ( contract - > fullyQualifiedName ( ) ) ;
2017-03-14 14:41:23 +00:00
}
else
solAssert ( false , " Invalid declaration type. " ) ;
2017-04-28 16:15:18 +00:00
solAssert ( _assembly . stackHeight ( ) - depositBefore = = int ( ref - > second . valueSize ) , " " ) ;
2017-03-22 18:02:27 +00:00
}
else
{
2017-03-14 14:41:23 +00:00
// lvalue context
2017-04-21 17:13:46 +00:00
solAssert ( ! ref - > second . isOffset & & ! ref - > second . isSlot , " " ) ;
2017-03-14 14:41:23 +00:00
auto variable = dynamic_cast < VariableDeclaration const * > ( decl ) ;
solAssert (
! ! variable & & m_context . isLocalVariable ( variable ) ,
" Can only assign to stack variables in inline assembly. "
) ;
2017-04-11 19:12:17 +00:00
solAssert ( variable - > type ( ) - > sizeOnStack ( ) = = 1 , " " ) ;
2017-04-28 16:15:18 +00:00
int stackDiff = _assembly . stackHeight ( ) - m_context . baseStackOffsetOfVariable ( * variable ) - 1 ;
2017-03-14 14:41:23 +00:00
if ( stackDiff > 16 | | stackDiff < 1 )
BOOST_THROW_EXCEPTION (
CompilerError ( ) < <
errinfo_sourceLocation ( _inlineAssembly . location ( ) ) < <
2017-04-28 16:15:18 +00:00
errinfo_comment ( " Stack too deep( " + to_string ( stackDiff ) + " ), try removing local variables. " )
2016-05-19 17:32:14 +00:00
) ;
2017-04-28 16:15:18 +00:00
_assembly . appendInstruction ( swapInstruction ( stackDiff ) ) ;
_assembly . appendInstruction ( Instruction : : POP ) ;
2016-05-19 17:32:14 +00:00
}
2017-03-14 14:41:23 +00:00
} ;
2017-04-26 13:41:08 +00:00
solAssert ( _inlineAssembly . annotation ( ) . analysisInfo , " " ) ;
2019-02-13 10:35:49 +00:00
yul : : CodeGenerator : : assemble (
2017-04-12 16:32:25 +00:00
_inlineAssembly . operations ( ) ,
2017-04-26 13:41:08 +00:00
* _inlineAssembly . annotation ( ) . analysisInfo ,
2019-01-16 10:44:11 +00:00
* m_context . assemblyPtr ( ) ,
2019-02-21 20:56:30 +00:00
m_context . evmVersion ( ) ,
2019-02-26 18:55:13 +00:00
identifierAccess ,
false ,
m_optimiserSettings . optimizeStackAllocation
2016-05-19 17:32:14 +00:00
) ;
m_context . setStackOffset ( startStackHeight ) ;
return false ;
}
bool ContractCompiler : : visit ( IfStatement const & _ifStatement )
{
StackHeightChecker checker ( m_context ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _ifStatement ) ;
compileExpression ( _ifStatement . condition ( ) ) ;
m_context < < Instruction : : ISZERO ;
eth : : AssemblyItem falseTag = m_context . appendConditionalJump ( ) ;
eth : : AssemblyItem endTag = falseTag ;
_ifStatement . trueStatement ( ) . accept ( * this ) ;
if ( _ifStatement . falseStatement ( ) )
{
endTag = m_context . appendJumpToNew ( ) ;
m_context < < falseTag ;
_ifStatement . falseStatement ( ) - > accept ( * this ) ;
}
m_context < < endTag ;
checker . check ( ) ;
return false ;
}
bool ContractCompiler : : visit ( WhileStatement const & _whileStatement )
{
StackHeightChecker checker ( m_context ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _whileStatement ) ;
2018-05-14 13:26:10 +00:00
2016-05-19 17:32:14 +00:00
eth : : AssemblyItem loopStart = m_context . newTag ( ) ;
eth : : AssemblyItem loopEnd = m_context . newTag ( ) ;
2018-12-10 18:02:39 +00:00
m_breakTags . emplace_back ( loopEnd , m_context . stackHeight ( ) ) ;
2018-05-03 14:40:33 +00:00
2016-05-19 17:32:14 +00:00
m_context < < loopStart ;
2016-07-30 07:13:05 +00:00
2018-05-14 13:26:10 +00:00
if ( _whileStatement . isDoWhile ( ) )
2016-07-30 07:13:05 +00:00
{
2018-05-14 13:26:10 +00:00
eth : : AssemblyItem condition = m_context . newTag ( ) ;
2018-12-10 18:02:39 +00:00
m_continueTags . emplace_back ( condition , m_context . stackHeight ( ) ) ;
2016-05-19 17:32:14 +00:00
2018-05-14 13:26:10 +00:00
_whileStatement . body ( ) . accept ( * this ) ;
2016-05-19 17:32:14 +00:00
2018-05-14 13:26:10 +00:00
m_context < < condition ;
compileExpression ( _whileStatement . condition ( ) ) ;
m_context < < Instruction : : ISZERO < < Instruction : : ISZERO ;
m_context . appendConditionalJumpTo ( loopStart ) ;
}
else
2016-07-30 07:13:05 +00:00
{
2018-12-10 18:02:39 +00:00
m_continueTags . emplace_back ( loopStart , m_context . stackHeight ( ) ) ;
2016-07-30 07:13:05 +00:00
compileExpression ( _whileStatement . condition ( ) ) ;
m_context < < Instruction : : ISZERO ;
m_context . appendConditionalJumpTo ( loopEnd ) ;
2018-05-14 13:26:10 +00:00
_whileStatement . body ( ) . accept ( * this ) ;
m_context . appendJumpTo ( loopStart ) ;
}
2016-05-19 17:32:14 +00:00
m_context < < loopEnd ;
m_continueTags . pop_back ( ) ;
m_breakTags . pop_back ( ) ;
checker . check ( ) ;
return false ;
}
bool ContractCompiler : : visit ( ForStatement const & _forStatement )
{
StackHeightChecker checker ( m_context ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _forStatement ) ;
eth : : AssemblyItem loopStart = m_context . newTag ( ) ;
eth : : AssemblyItem loopEnd = m_context . newTag ( ) ;
eth : : AssemblyItem loopNext = m_context . newTag ( ) ;
2018-05-03 14:40:33 +00:00
2018-07-04 12:14:41 +00:00
storeStackHeight ( & _forStatement ) ;
2016-05-19 17:32:14 +00:00
if ( _forStatement . initializationExpression ( ) )
_forStatement . initializationExpression ( ) - > accept ( * this ) ;
2018-12-10 18:02:39 +00:00
m_breakTags . emplace_back ( loopEnd , m_context . stackHeight ( ) ) ;
m_continueTags . emplace_back ( loopNext , m_context . stackHeight ( ) ) ;
2016-05-19 17:32:14 +00:00
m_context < < loopStart ;
// if there is no terminating condition in for, default is to always be true
if ( _forStatement . condition ( ) )
{
compileExpression ( * _forStatement . condition ( ) ) ;
m_context < < Instruction : : ISZERO ;
m_context . appendConditionalJumpTo ( loopEnd ) ;
}
_forStatement . body ( ) . accept ( * this ) ;
m_context < < loopNext ;
// for's loop expression if existing
if ( _forStatement . loopExpression ( ) )
_forStatement . loopExpression ( ) - > accept ( * this ) ;
m_context . appendJumpTo ( loopStart ) ;
2018-05-03 14:40:33 +00:00
2016-05-19 17:32:14 +00:00
m_context < < loopEnd ;
m_continueTags . pop_back ( ) ;
m_breakTags . pop_back ( ) ;
2018-05-03 14:40:33 +00:00
// For the case where no break/return is executed:
// loop initialization variables have to be freed
popScopedVariables ( & _forStatement ) ;
2016-05-19 17:32:14 +00:00
checker . check ( ) ;
return false ;
}
bool ContractCompiler : : visit ( Continue const & _continueStatement )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _continueStatement ) ;
2018-06-11 23:46:23 +00:00
solAssert ( ! m_continueTags . empty ( ) , " " ) ;
2018-07-04 12:14:41 +00:00
CompilerUtils ( m_context ) . popAndJump ( m_continueTags . back ( ) . second , m_continueTags . back ( ) . first ) ;
2016-05-19 17:32:14 +00:00
return false ;
}
bool ContractCompiler : : visit ( Break const & _breakStatement )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _breakStatement ) ;
2018-06-11 23:46:23 +00:00
solAssert ( ! m_breakTags . empty ( ) , " " ) ;
2018-07-04 12:14:41 +00:00
CompilerUtils ( m_context ) . popAndJump ( m_breakTags . back ( ) . second , m_breakTags . back ( ) . first ) ;
2016-05-19 17:32:14 +00:00
return false ;
}
bool ContractCompiler : : visit ( Return const & _return )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _return ) ;
if ( Expression const * expression = _return . expression ( ) )
{
solAssert ( _return . annotation ( ) . functionReturnParameters , " Invalid return parameters pointer. " ) ;
vector < ASTPointer < VariableDeclaration > > const & returnParameters =
_return . annotation ( ) . functionReturnParameters - > parameters ( ) ;
TypePointers types ;
for ( auto const & retVariable : returnParameters )
types . push_back ( retVariable - > annotation ( ) . type ) ;
TypePointer expectedType ;
if ( expression - > annotation ( ) . type - > category ( ) = = Type : : Category : : Tuple | | types . size ( ) ! = 1 )
2019-04-17 11:40:50 +00:00
expectedType = TypeProvider : : tuple ( move ( types ) ) ;
2016-05-19 17:32:14 +00:00
else
expectedType = types . front ( ) ;
compileExpression ( * expression , expectedType ) ;
for ( auto const & retVariable : boost : : adaptors : : reverse ( returnParameters ) )
CompilerUtils ( m_context ) . moveToStackVariable ( * retVariable ) ;
}
2018-05-03 14:40:33 +00:00
2018-07-04 12:14:41 +00:00
CompilerUtils ( m_context ) . popAndJump ( m_returnTags . back ( ) . second , m_returnTags . back ( ) . first ) ;
2016-05-19 17:32:14 +00:00
return false ;
}
2018-08-07 22:38:08 +00:00
bool ContractCompiler : : visit ( Throw const & )
2016-05-19 17:32:14 +00:00
{
2018-08-07 22:38:08 +00:00
solAssert ( false , " Throw statement is disallowed. " ) ;
2016-05-19 17:32:14 +00:00
return false ;
}
2018-02-16 15:55:21 +00:00
bool ContractCompiler : : visit ( EmitStatement const & _emit )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _emit ) ;
StackHeightChecker checker ( m_context ) ;
compileExpression ( _emit . eventCall ( ) ) ;
checker . check ( ) ;
return false ;
}
2016-05-19 17:32:14 +00:00
bool ContractCompiler : : visit ( VariableDeclarationStatement const & _variableDeclarationStatement )
{
CompilerContext : : LocationSetter locationSetter ( m_context , _variableDeclarationStatement ) ;
2018-05-03 14:40:33 +00:00
// Local variable slots are reserved when their declaration is visited,
// and freed in the end of their scope.
for ( auto _decl : _variableDeclarationStatement . declarations ( ) )
if ( _decl )
appendStackVariableInitialisation ( * _decl ) ;
StackHeightChecker checker ( m_context ) ;
2016-05-19 17:32:14 +00:00
if ( Expression const * expression = _variableDeclarationStatement . initialValue ( ) )
{
CompilerUtils utils ( m_context ) ;
compileExpression ( * expression ) ;
TypePointers valueTypes ;
2019-04-15 13:33:39 +00:00
if ( auto tupleType = dynamic_cast < TupleType const * > ( expression - > annotation ( ) . type ) )
2016-05-19 17:32:14 +00:00
valueTypes = tupleType - > components ( ) ;
else
valueTypes = TypePointers { expression - > annotation ( ) . type } ;
2018-07-04 16:34:24 +00:00
auto const & declarations = _variableDeclarationStatement . declarations ( ) ;
solAssert ( declarations . size ( ) = = valueTypes . size ( ) , " " ) ;
for ( size_t i = 0 ; i < declarations . size ( ) ; + + i )
2016-05-19 17:32:14 +00:00
{
2018-07-04 16:34:24 +00:00
size_t j = declarations . size ( ) - i - 1 ;
2016-05-19 17:32:14 +00:00
solAssert ( ! ! valueTypes [ j ] , " " ) ;
2018-07-04 16:34:24 +00:00
if ( VariableDeclaration const * varDecl = declarations [ j ] . get ( ) )
2016-05-19 17:32:14 +00:00
{
utils . convertType ( * valueTypes [ j ] , * varDecl - > annotation ( ) . type ) ;
utils . moveToStackVariable ( * varDecl ) ;
}
2018-07-04 16:34:24 +00:00
else
utils . popStackElement ( * valueTypes [ j ] ) ;
2016-05-19 17:32:14 +00:00
}
}
checker . check ( ) ;
return false ;
}
bool ContractCompiler : : visit ( ExpressionStatement const & _expressionStatement )
{
StackHeightChecker checker ( m_context ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _expressionStatement ) ;
Expression const & expression = _expressionStatement . expression ( ) ;
compileExpression ( expression ) ;
CompilerUtils ( m_context ) . popStackElement ( * expression . annotation ( ) . type ) ;
checker . check ( ) ;
return false ;
}
bool ContractCompiler : : visit ( PlaceholderStatement const & _placeholderStatement )
{
StackHeightChecker checker ( m_context ) ;
CompilerContext : : LocationSetter locationSetter ( m_context , _placeholderStatement ) ;
appendModifierOrFunctionCode ( ) ;
checker . check ( ) ;
return true ;
}
2018-05-03 14:40:33 +00:00
bool ContractCompiler : : visit ( Block const & _block )
{
2018-07-04 12:14:41 +00:00
storeStackHeight ( & _block ) ;
2018-05-03 14:40:33 +00:00
return true ;
}
void ContractCompiler : : endVisit ( Block const & _block )
{
// Frees local variables declared in the scope of this block.
popScopedVariables ( & _block ) ;
}
2016-05-19 17:32:14 +00:00
void ContractCompiler : : appendMissingFunctions ( )
{
while ( Declaration const * function = m_context . nextFunctionToCompile ( ) )
{
m_context . setStackOffset ( 0 ) ;
function - > accept ( * this ) ;
solAssert ( m_context . nextFunctionToCompile ( ) ! = function , " Compiled the wrong function? " ) ;
}
2017-01-19 16:21:55 +00:00
m_context . appendMissingLowLevelFunctions ( ) ;
2018-01-17 11:05:43 +00:00
auto abiFunctions = m_context . abiFunctions ( ) . requestedFunctions ( ) ;
if ( ! abiFunctions . first . empty ( ) )
2019-02-21 17:23:46 +00:00
m_context . appendInlineAssembly (
" { " + move ( abiFunctions . first ) + " } " ,
{ } ,
abiFunctions . second ,
true ,
2019-02-26 18:55:13 +00:00
m_optimiserSettings
2019-02-21 17:23:46 +00:00
) ;
2016-05-19 17:32:14 +00:00
}
void ContractCompiler : : appendModifierOrFunctionCode ( )
{
solAssert ( m_currentFunction , " " ) ;
2016-08-06 12:48:59 +00:00
unsigned stackSurplus = 0 ;
Block const * codeBlock = nullptr ;
2017-06-23 16:55:47 +00:00
vector < VariableDeclaration const * > addedVariables ;
2016-08-06 12:48:59 +00:00
m_modifierDepth + + ;
2016-05-19 17:32:14 +00:00
if ( m_modifierDepth > = m_currentFunction - > modifiers ( ) . size ( ) )
2016-06-06 17:36:19 +00:00
{
solAssert ( m_currentFunction - > isImplemented ( ) , " " ) ;
2016-08-06 12:48:59 +00:00
codeBlock = & m_currentFunction - > body ( ) ;
2016-06-06 17:36:19 +00:00
}
2016-05-19 17:32:14 +00:00
else
{
ASTPointer < ModifierInvocation > const & modifierInvocation = m_currentFunction - > modifiers ( ) [ m_modifierDepth ] ;
// constructor call should be excluded
if ( dynamic_cast < ContractDefinition const * > ( modifierInvocation - > name ( ) - > annotation ( ) . referencedDeclaration ) )
appendModifierOrFunctionCode ( ) ;
2016-08-06 12:48:59 +00:00
else
2016-05-19 17:32:14 +00:00
{
2018-03-13 15:50:44 +00:00
ModifierDefinition const & nonVirtualModifier = dynamic_cast < ModifierDefinition const & > (
* modifierInvocation - > name ( ) - > annotation ( ) . referencedDeclaration
) ;
ModifierDefinition const & modifier = m_context . resolveVirtualFunctionModifier ( nonVirtualModifier ) ;
2016-08-06 12:48:59 +00:00
CompilerContext : : LocationSetter locationSetter ( m_context , modifier ) ;
2018-04-10 09:22:26 +00:00
std : : vector < ASTPointer < Expression > > const & modifierArguments =
modifierInvocation - > arguments ( ) ? * modifierInvocation - > arguments ( ) : std : : vector < ASTPointer < Expression > > ( ) ;
solAssert ( modifier . parameters ( ) . size ( ) = = modifierArguments . size ( ) , " " ) ;
2016-08-06 12:48:59 +00:00
for ( unsigned i = 0 ; i < modifier . parameters ( ) . size ( ) ; + + i )
{
m_context . addVariable ( * modifier . parameters ( ) [ i ] ) ;
2017-06-23 16:55:47 +00:00
addedVariables . push_back ( modifier . parameters ( ) [ i ] . get ( ) ) ;
2016-08-06 12:48:59 +00:00
compileExpression (
2018-04-10 09:22:26 +00:00
* modifierArguments [ i ] ,
2016-08-06 12:48:59 +00:00
modifier . parameters ( ) [ i ] - > annotation ( ) . type
) ;
}
2018-05-03 14:40:33 +00:00
stackSurplus = CompilerUtils : : sizeOnStack ( modifier . parameters ( ) ) ;
2016-08-06 12:48:59 +00:00
codeBlock = & modifier . body ( ) ;
2016-05-19 17:32:14 +00:00
}
2016-08-06 12:48:59 +00:00
}
if ( codeBlock )
{
2018-12-10 18:02:39 +00:00
m_returnTags . emplace_back ( m_context . newTag ( ) , m_context . stackHeight ( ) ) ;
2016-08-06 12:48:59 +00:00
codeBlock - > accept ( * this ) ;
2016-05-19 17:32:14 +00:00
2016-08-06 12:48:59 +00:00
solAssert ( ! m_returnTags . empty ( ) , " " ) ;
2018-05-03 14:40:33 +00:00
m_context < < m_returnTags . back ( ) . first ;
2016-08-06 12:48:59 +00:00
m_returnTags . pop_back ( ) ;
2016-05-19 17:32:14 +00:00
2016-08-06 12:48:59 +00:00
CompilerUtils ( m_context ) . popStackSlots ( stackSurplus ) ;
2017-06-23 16:55:47 +00:00
for ( auto var : addedVariables )
m_context . removeVariable ( * var ) ;
2016-05-19 17:32:14 +00:00
}
2016-08-06 12:48:59 +00:00
m_modifierDepth - - ;
2016-05-19 17:32:14 +00:00
}
void ContractCompiler : : appendStackVariableInitialisation ( VariableDeclaration const & _variable )
{
CompilerContext : : LocationSetter location ( m_context , _variable ) ;
m_context . addVariable ( _variable ) ;
CompilerUtils ( m_context ) . pushZeroValue ( * _variable . annotation ( ) . type ) ;
}
void ContractCompiler : : compileExpression ( Expression const & _expression , TypePointer const & _targetType )
{
2019-02-21 17:13:52 +00:00
ExpressionCompiler expressionCompiler ( m_context , m_optimiserSettings . runOrderLiterals ) ;
2016-05-19 17:32:14 +00:00
expressionCompiler . compile ( _expression ) ;
if ( _targetType )
CompilerUtils ( m_context ) . convertType ( * _expression . annotation ( ) . type , * _targetType ) ;
}
2018-05-03 14:40:33 +00:00
void ContractCompiler : : popScopedVariables ( ASTNode const * _node )
{
2018-07-04 12:14:41 +00:00
unsigned blockHeight = m_scopeStackHeight . at ( m_modifierDepth ) . at ( _node ) ;
2018-07-10 16:39:26 +00:00
m_context . removeVariablesAboveStackHeight ( blockHeight ) ;
2018-07-06 07:56:27 +00:00
solAssert ( m_context . stackHeight ( ) > = blockHeight , " " ) ;
2018-05-03 14:40:33 +00:00
unsigned stackDiff = m_context . stackHeight ( ) - blockHeight ;
CompilerUtils ( m_context ) . popStackSlots ( stackDiff ) ;
m_scopeStackHeight [ m_modifierDepth ] . erase ( _node ) ;
2018-10-09 03:29:37 +00:00
if ( m_scopeStackHeight [ m_modifierDepth ] . empty ( ) )
2018-05-03 14:40:33 +00:00
m_scopeStackHeight . erase ( m_modifierDepth ) ;
}
2018-07-04 12:14:41 +00:00
void ContractCompiler : : storeStackHeight ( ASTNode const * _node )
2018-05-03 14:40:33 +00:00
{
2018-07-04 12:14:41 +00:00
m_scopeStackHeight [ m_modifierDepth ] [ _node ] = m_context . stackHeight ( ) ;
2018-05-03 14:40:33 +00:00
}