2014-10-20 10:41:56 +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
2014-10-30 00:20:32 +00:00
* Solidity compiler .
2014-10-20 10:41:56 +00:00
*/
2014-10-30 00:20:32 +00:00
# include <algorithm>
2014-11-07 01:06:37 +00:00
# include <libevmcore/Instruction.h>
# include <libevmcore/Assembly.h>
2014-10-20 10:41:56 +00:00
# include <libsolidity/AST.h>
# include <libsolidity/Compiler.h>
2014-10-30 00:20:32 +00:00
# include <libsolidity/ExpressionCompiler.h>
2014-12-08 17:52:30 +00:00
# include <libsolidity/CompilerUtils.h>
2014-12-15 21:57:39 +00:00
# include <libsolidity/CallGraph.h>
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
using namespace std ;
2014-10-20 10:41:56 +00:00
namespace dev {
namespace solidity {
2014-12-12 15:49:26 +00:00
void Compiler : : compileContract ( ContractDefinition const & _contract , vector < MagicVariableDeclaration const * > const & _magicGlobals ,
map < ContractDefinition const * , bytes const * > const & _contracts )
2014-10-20 10:41:56 +00:00
{
2014-10-30 00:20:32 +00:00
m_context = CompilerContext ( ) ; // clear it just in case
2014-12-15 21:57:39 +00:00
initializeContext ( _contract , _magicGlobals , _contracts ) ;
2014-11-21 18:14:56 +00:00
2014-10-30 00:20:32 +00:00
for ( ASTPointer < FunctionDefinition > const & function : _contract . getDefinedFunctions ( ) )
2014-11-19 09:24:22 +00:00
if ( function - > getName ( ) ! = _contract . getName ( ) ) // don't add the constructor here
m_context . addFunction ( * function ) ;
2014-10-30 21:52:15 +00:00
2014-11-19 09:24:22 +00:00
appendFunctionSelector ( _contract ) ;
2014-10-30 00:20:32 +00:00
for ( ASTPointer < FunctionDefinition > const & function : _contract . getDefinedFunctions ( ) )
2014-11-19 09:24:22 +00:00
if ( function - > getName ( ) ! = _contract . getName ( ) ) // don't add the constructor here
function - > accept ( * this ) ;
2014-10-30 21:52:15 +00:00
2014-12-15 21:57:39 +00:00
// Swap the runtime context with the creation-time context
2014-11-19 09:24:22 +00:00
CompilerContext runtimeContext ;
swap ( m_context , runtimeContext ) ;
2014-12-15 21:57:39 +00:00
initializeContext ( _contract , _magicGlobals , _contracts ) ;
packIntoContractCreator ( _contract , runtimeContext ) ;
}
2014-11-19 09:24:22 +00:00
2014-12-15 21:57:39 +00:00
void Compiler : : initializeContext ( ContractDefinition const & _contract , vector < MagicVariableDeclaration const * > const & _magicGlobals ,
map < ContractDefinition const * , bytes const * > const & _contracts )
{
m_context . setCompiledContracts ( _contracts ) ;
for ( MagicVariableDeclaration const * variable : _magicGlobals )
m_context . addMagicGlobal ( * variable ) ;
2014-11-19 09:24:22 +00:00
registerStateVariables ( _contract ) ;
2014-12-15 21:57:39 +00:00
}
2014-11-19 09:24:22 +00:00
2014-12-15 21:57:39 +00:00
void Compiler : : packIntoContractCreator ( ContractDefinition const & _contract , CompilerContext const & _runtimeContext )
{
set < FunctionDefinition const * > neededFunctions ;
FunctionDefinition const * constructor = _contract . getConstructor ( ) ;
2014-11-19 09:24:22 +00:00
if ( constructor )
2014-12-15 21:57:39 +00:00
neededFunctions = getFunctionsNeededByConstructor ( * constructor ) ;
for ( FunctionDefinition const * fun : neededFunctions )
m_context . addFunction ( * fun ) ;
2014-11-19 09:24:22 +00:00
2014-12-15 21:57:39 +00:00
if ( constructor )
appendConstructorCall ( * constructor ) ;
eth : : AssemblyItem sub = m_context . addSubroutine ( _runtimeContext . getAssembly ( ) ) ;
// stack contains sub size
2014-11-19 09:24:22 +00:00
m_context < < eth : : Instruction : : DUP1 < < sub < < u256 ( 0 ) < < eth : : Instruction : : CODECOPY ;
m_context < < u256 ( 0 ) < < eth : : Instruction : : RETURN ;
2014-12-15 21:57:39 +00:00
// note that we have to explicitly include all used functions because of absolute jump
// labels
for ( FunctionDefinition const * fun : neededFunctions )
fun - > accept ( * this ) ;
}
void Compiler : : appendConstructorCall ( FunctionDefinition const & _constructor )
{
eth : : AssemblyItem returnTag = m_context . pushNewTag ( ) ;
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0 ;
for ( ASTPointer < VariableDeclaration > const & var : _constructor . getParameters ( ) )
argumentSize + = var - > getType ( ) - > getCalldataEncodedSize ( ) ;
if ( argumentSize > 0 )
{
m_context < < u256 ( argumentSize ) ;
m_context . appendProgramSize ( ) ;
m_context < < u256 ( 1 ) ; // copy it to byte one as expected for ABI calls
m_context < < eth : : Instruction : : CODECOPY ;
appendCalldataUnpacker ( _constructor , true ) ;
}
m_context . appendJumpTo ( m_context . getFunctionEntryLabel ( _constructor ) ) ;
m_context < < returnTag ;
}
set < FunctionDefinition const * > Compiler : : getFunctionsNeededByConstructor ( FunctionDefinition const & _constructor )
{
CallGraph callgraph ;
callgraph . addFunction ( _constructor ) ;
callgraph . computeCallGraph ( ) ;
return callgraph . getCalls ( ) ;
2014-10-20 10:41:56 +00:00
}
2014-11-19 09:24:22 +00:00
void Compiler : : appendFunctionSelector ( ContractDefinition const & _contract )
2014-10-20 10:41:56 +00:00
{
2015-01-07 15:39:21 +00:00
map < FixedHash < 4 > , FunctionDefinition const * > interfaceFunctions = _contract . getInterfaceFunctions ( ) ;
2014-11-19 09:24:22 +00:00
vector < eth : : AssemblyItem > callDataUnpackerEntryPoints ;
2014-10-30 17:15:25 +00:00
2015-01-07 15:39:21 +00:00
if ( interfaceFunctions . size ( ) > 4294967295 ) // 2 ** 32
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " More than 4294967295 public functions for contract. " ) ) ;
2014-10-30 17:15:25 +00:00
2015-01-07 23:19:19 +00:00
// retrieve the function signature hash from the calldata
m_context < < u256 ( 1 ) < < u256 ( 0 ) < < u256 ( 4294967296 ) * u256 ( 4294967296 ) * u256 ( 4294967296 ) * u256 ( 4294967296 ) * u256 ( 4294967296 ) * u256 ( 4294967296 ) * u256 ( 4294967296 ) // some constants
2015-01-07 15:39:21 +00:00
< < eth : : dupInstruction ( 2 ) < < eth : : Instruction : : CALLDATALOAD
< < eth : : Instruction : : DIV ;
2014-11-10 12:13:40 +00:00
2015-01-07 23:19:19 +00:00
// stack now is: 1 0 <funhash>
2015-01-07 15:39:21 +00:00
for ( auto it = interfaceFunctions . begin ( ) ; it ! = interfaceFunctions . end ( ) ; + + it )
2014-11-10 12:13:40 +00:00
{
2014-11-19 09:24:22 +00:00
callDataUnpackerEntryPoints . push_back ( m_context . newTag ( ) ) ;
2015-01-07 15:39:21 +00:00
m_context < < eth : : dupInstruction ( 1 ) < < u256 ( FixedHash < 4 > : : Arith ( it - > first ) ) < < eth : : Instruction : : EQ ;
2014-11-19 09:24:22 +00:00
m_context . appendConditionalJumpTo ( callDataUnpackerEntryPoints . back ( ) ) ;
2014-11-10 12:13:40 +00:00
}
m_context < < eth : : Instruction : : STOP ; // function not found
2014-10-30 17:15:25 +00:00
2015-01-07 15:39:21 +00:00
unsigned funid = 0 ;
for ( auto it = interfaceFunctions . begin ( ) ; it ! = interfaceFunctions . end ( ) ; + + it , + + funid )
2014-10-30 17:15:25 +00:00
{
2015-01-07 15:39:21 +00:00
FunctionDefinition const & function = * it - > second ;
2014-11-19 09:24:22 +00:00
m_context < < callDataUnpackerEntryPoints [ funid ] ;
2014-10-31 16:47:43 +00:00
eth : : AssemblyItem returnTag = m_context . pushNewTag ( ) ;
appendCalldataUnpacker ( function ) ;
m_context . appendJumpTo ( m_context . getFunctionEntryLabel ( function ) ) ;
m_context < < returnTag ;
appendReturnValuePacker ( function ) ;
}
2014-10-30 17:15:25 +00:00
}
2014-11-19 09:24:22 +00:00
unsigned Compiler : : appendCalldataUnpacker ( FunctionDefinition const & _function , bool _fromMemory )
2014-10-30 17:15:25 +00:00
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1 ;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for ( ASTPointer < VariableDeclaration > const & var : _function . getParameters ( ) )
{
unsigned const numBytes = var - > getType ( ) - > getCalldataEncodedSize ( ) ;
2014-12-11 13:19:11 +00:00
if ( numBytes > 32 )
2014-10-30 17:15:25 +00:00
BOOST_THROW_EXCEPTION ( CompilerError ( )
< < errinfo_sourceLocation ( var - > getLocation ( ) )
2014-11-07 01:06:37 +00:00
< < errinfo_comment ( " Type " + var - > getType ( ) - > toString ( ) + " not yet supported. " ) ) ;
2014-12-10 16:15:17 +00:00
bool leftAligned = var - > getType ( ) - > getCategory ( ) = = Type : : Category : : STRING ;
CompilerUtils ( m_context ) . loadFromMemory ( dataOffset , numBytes , leftAligned , ! _fromMemory ) ;
2014-10-30 17:15:25 +00:00
dataOffset + = numBytes ;
}
2014-11-19 09:24:22 +00:00
return dataOffset ;
2014-10-30 17:15:25 +00:00
}
void Compiler : : appendReturnValuePacker ( FunctionDefinition const & _function )
{
//@todo this can be also done more efficiently
unsigned dataOffset = 0 ;
vector < ASTPointer < VariableDeclaration > > const & parameters = _function . getReturnParameters ( ) ;
2014-12-10 11:51:26 +00:00
unsigned stackDepth = CompilerUtils ( m_context ) . getSizeOnStack ( parameters ) ;
2014-10-31 16:47:43 +00:00
for ( unsigned i = 0 ; i < parameters . size ( ) ; + + i )
2014-10-30 17:15:25 +00:00
{
2014-11-07 01:06:37 +00:00
Type const & paramType = * parameters [ i ] - > getType ( ) ;
unsigned numBytes = paramType . getCalldataEncodedSize ( ) ;
2014-12-11 13:19:11 +00:00
if ( numBytes > 32 )
2014-10-30 17:15:25 +00:00
BOOST_THROW_EXCEPTION ( CompilerError ( )
< < errinfo_sourceLocation ( parameters [ i ] - > getLocation ( ) )
2014-11-07 01:06:37 +00:00
< < errinfo_comment ( " Type " + paramType . toString ( ) + " not yet supported. " ) ) ;
2014-12-10 11:51:26 +00:00
CompilerUtils ( m_context ) . copyToStackTop ( stackDepth , paramType ) ;
2014-12-10 16:15:17 +00:00
bool const leftAligned = paramType . getCategory ( ) = = Type : : Category : : STRING ;
CompilerUtils ( m_context ) . storeInMemory ( dataOffset , numBytes , leftAligned ) ;
2014-12-10 11:51:26 +00:00
stackDepth - = paramType . getSizeOnStack ( ) ;
2014-10-30 17:15:25 +00:00
dataOffset + = numBytes ;
}
// note that the stack is not cleaned up here
m_context < < u256 ( dataOffset ) < < u256 ( 0 ) < < eth : : Instruction : : RETURN ;
2014-10-20 10:41:56 +00:00
}
2014-11-19 09:24:22 +00:00
void Compiler : : registerStateVariables ( ContractDefinition const & _contract )
{
//@todo sort them?
for ( ASTPointer < VariableDeclaration > const & variable : _contract . getStateVariables ( ) )
m_context . addStateVariable ( * variable ) ;
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( FunctionDefinition const & _function )
2014-10-20 10:41:56 +00:00
{
2014-10-30 17:15:25 +00:00
//@todo to simplify this, the calling convention could by changed such that
2014-10-30 00:20:32 +00:00
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
2014-10-25 14:52:22 +00:00
2014-10-30 00:20:32 +00:00
m_context . startNewFunction ( ) ;
m_returnTag = m_context . newTag ( ) ;
m_breakTags . clear ( ) ;
m_continueTags . clear ( ) ;
2014-10-25 14:52:22 +00:00
2014-10-30 00:20:32 +00:00
m_context < < m_context . getFunctionEntryLabel ( _function ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
2014-10-20 10:41:56 +00:00
2014-12-08 17:19:25 +00:00
for ( ASTPointer < VariableDeclaration const > const & variable : _function . getParameters ( ) )
2014-10-30 00:20:32 +00:00
m_context . addVariable ( * variable ) ;
2014-12-08 17:19:25 +00:00
for ( ASTPointer < VariableDeclaration const > const & variable : _function . getReturnParameters ( ) )
2014-12-08 15:56:41 +00:00
m_context . addAndInitializeVariable ( * variable ) ;
2014-10-30 00:20:32 +00:00
for ( VariableDeclaration const * localVariable : _function . getLocalVariables ( ) )
2014-12-08 15:56:41 +00:00
m_context . addAndInitializeVariable ( * localVariable ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
_function . getBody ( ) . accept ( * this ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
m_context < < m_returnTag ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +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.
2014-10-20 10:41:56 +00:00
2014-12-08 21:18:19 +00:00
unsigned const argumentsSize = CompilerUtils : : getSizeOnStack ( _function . getParameters ( ) ) ;
unsigned const returnValuesSize = CompilerUtils : : getSizeOnStack ( _function . getReturnParameters ( ) ) ;
unsigned const localVariablesSize = CompilerUtils : : getSizeOnStack ( _function . getLocalVariables ( ) ) ;
2014-12-08 17:19:25 +00:00
2014-10-30 00:20:32 +00:00
vector < int > stackLayout ;
2014-12-08 17:19:25 +00:00
stackLayout . push_back ( returnValuesSize ) ; // target of return address
stackLayout + = vector < int > ( argumentsSize , - 1 ) ; // discard all arguments
for ( unsigned i = 0 ; i < returnValuesSize ; + + i )
2014-10-30 00:20:32 +00:00
stackLayout . push_back ( i ) ;
2014-12-08 17:19:25 +00:00
stackLayout + = vector < int > ( localVariablesSize , - 1 ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
while ( stackLayout . back ( ) ! = int ( stackLayout . size ( ) - 1 ) )
if ( stackLayout . back ( ) < 0 )
{
m_context < < eth : : Instruction : : POP ;
stackLayout . pop_back ( ) ;
}
else
{
m_context < < eth : : swapInstruction ( stackLayout . size ( ) - stackLayout . back ( ) - 1 ) ;
swap ( stackLayout [ stackLayout . back ( ) ] , stackLayout . back ( ) ) ;
}
2014-10-31 16:47:43 +00:00
//@todo assert that everything is in place now
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
m_context < < eth : : Instruction : : JUMP ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( IfStatement const & _ifStatement )
2014-10-20 10:41:56 +00:00
{
2014-12-11 16:35:23 +00:00
compileExpression ( _ifStatement . getCondition ( ) ) ;
2014-10-30 00:20:32 +00:00
eth : : AssemblyItem trueTag = m_context . appendConditionalJump ( ) ;
if ( _ifStatement . getFalseStatement ( ) )
_ifStatement . getFalseStatement ( ) - > accept ( * this ) ;
2014-11-10 16:31:09 +00:00
eth : : AssemblyItem endTag = m_context . appendJumpToNew ( ) ;
2014-10-30 00:20:32 +00:00
m_context < < trueTag ;
_ifStatement . getTrueStatement ( ) . accept ( * this ) ;
m_context < < endTag ;
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( WhileStatement const & _whileStatement )
2014-10-20 10:41:56 +00:00
{
2014-10-30 00:20:32 +00:00
eth : : AssemblyItem loopStart = m_context . newTag ( ) ;
eth : : AssemblyItem loopEnd = m_context . newTag ( ) ;
m_continueTags . push_back ( loopStart ) ;
m_breakTags . push_back ( loopEnd ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
m_context < < loopStart ;
2014-12-11 16:35:23 +00:00
compileExpression ( _whileStatement . getCondition ( ) ) ;
2014-10-31 16:20:27 +00:00
m_context < < eth : : Instruction : : ISZERO ;
2014-10-30 00:20:32 +00:00
m_context . appendConditionalJumpTo ( loopEnd ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
_whileStatement . getBody ( ) . accept ( * this ) ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
m_context . appendJumpTo ( loopStart ) ;
m_context < < loopEnd ;
2014-10-20 10:41:56 +00:00
2014-10-30 00:20:32 +00:00
m_continueTags . pop_back ( ) ;
m_breakTags . pop_back ( ) ;
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-15 16:45:18 +00:00
bool Compiler : : visit ( ForStatement const & _forStatement )
{
2014-12-16 15:52:47 +00:00
eth : : AssemblyItem loopStart = m_context . newTag ( ) ;
eth : : AssemblyItem loopEnd = m_context . newTag ( ) ;
m_continueTags . push_back ( loopStart ) ;
m_breakTags . push_back ( loopEnd ) ;
if ( _forStatement . getInitializationExpression ( ) )
_forStatement . getInitializationExpression ( ) - > accept ( * this ) ;
m_context < < loopStart ;
// if there is no terminating condition in for, default is to always be true
if ( _forStatement . getCondition ( ) )
{
compileExpression ( * _forStatement . getCondition ( ) ) ;
m_context < < eth : : Instruction : : ISZERO ;
m_context . appendConditionalJumpTo ( loopEnd ) ;
}
_forStatement . getBody ( ) . accept ( * this ) ;
// for's loop expression if existing
if ( _forStatement . getLoopExpression ( ) )
_forStatement . getLoopExpression ( ) - > accept ( * this ) ;
m_context . appendJumpTo ( loopStart ) ;
m_context < < loopEnd ;
m_continueTags . pop_back ( ) ;
m_breakTags . pop_back ( ) ;
2014-12-15 16:45:18 +00:00
return false ;
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( Continue const & )
2014-10-20 10:41:56 +00:00
{
2014-11-05 17:44:05 +00:00
if ( ! m_continueTags . empty ( ) )
m_context . appendJumpTo ( m_continueTags . back ( ) ) ;
2014-10-30 00:20:32 +00:00
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( Break const & )
2014-10-20 10:41:56 +00:00
{
2014-11-05 17:44:05 +00:00
if ( ! m_breakTags . empty ( ) )
m_context . appendJumpTo ( m_breakTags . back ( ) ) ;
2014-10-30 00:20:32 +00:00
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( Return const & _return )
2014-10-20 10:41:56 +00:00
{
2014-10-30 00:20:32 +00:00
//@todo modifications are needed to make this work with functions returning multiple values
2014-12-06 01:19:10 +00:00
if ( Expression const * expression = _return . getExpression ( ) )
2014-10-20 10:41:56 +00:00
{
2014-12-11 16:35:23 +00:00
compileExpression ( * expression ) ;
2014-10-30 00:20:32 +00:00
VariableDeclaration const & firstVariable = * _return . getFunctionReturnParameters ( ) . getParameters ( ) . front ( ) ;
2014-11-04 18:13:03 +00:00
ExpressionCompiler : : appendTypeConversion ( m_context , * expression - > getType ( ) , * firstVariable . getType ( ) ) ;
2014-11-07 01:06:37 +00:00
2014-12-08 17:52:30 +00:00
CompilerUtils ( m_context ) . moveToStackVariable ( firstVariable ) ;
2014-10-20 10:41:56 +00:00
}
2014-10-30 00:20:32 +00:00
m_context . appendJumpTo ( m_returnTag ) ;
return false ;
2014-10-20 10:41:56 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( VariableDefinition const & _variableDefinition )
2014-10-25 14:52:22 +00:00
{
2014-12-06 01:19:10 +00:00
if ( Expression const * expression = _variableDefinition . getExpression ( ) )
2014-10-25 14:52:22 +00:00
{
2014-12-11 16:35:23 +00:00
compileExpression ( * expression ) ;
2014-11-04 18:13:03 +00:00
ExpressionCompiler : : appendTypeConversion ( m_context ,
* expression - > getType ( ) ,
* _variableDefinition . getDeclaration ( ) . getType ( ) ) ;
2014-12-08 17:52:30 +00:00
CompilerUtils ( m_context ) . moveToStackVariable ( _variableDefinition . getDeclaration ( ) ) ;
2014-10-25 14:52:22 +00:00
}
2014-10-30 00:20:32 +00:00
return false ;
2014-10-25 14:52:22 +00:00
}
2014-12-06 00:06:24 +00:00
bool Compiler : : visit ( ExpressionStatement const & _expressionStatement )
2014-10-25 14:52:22 +00:00
{
2014-12-06 01:19:10 +00:00
Expression const & expression = _expressionStatement . getExpression ( ) ;
2014-12-11 16:35:23 +00:00
compileExpression ( expression ) ;
2014-12-08 17:52:30 +00:00
CompilerUtils ( m_context ) . popStackElement ( * expression . getType ( ) ) ;
2014-10-30 00:20:32 +00:00
return false ;
2014-10-25 14:52:22 +00:00
}
2014-12-11 16:35:23 +00:00
void Compiler : : compileExpression ( Expression const & _expression )
{
ExpressionCompiler : : compileExpression ( m_context , _expression , m_optimize ) ;
}
2014-10-20 10:41:56 +00:00
}
}