2014-10-30 21:52:15 +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 >
2015-01-09 06:39:30 +00:00
* @ author Gav Wood < g @ ethdev . com >
2014-10-30 21:52:15 +00:00
* @ date 2014
* Full - stack compiler that converts a source code string to bytecode .
*/
2015-01-21 19:31:14 +00:00
# include <boost/algorithm/string.hpp>
2014-10-30 21:52:15 +00:00
# include <libsolidity/AST.h>
# include <libsolidity/Scanner.h>
# include <libsolidity/Parser.h>
2014-11-20 17:33:23 +00:00
# include <libsolidity/GlobalContext.h>
2014-10-30 21:52:15 +00:00
# include <libsolidity/NameAndTypeResolver.h>
# include <libsolidity/Compiler.h>
# include <libsolidity/CompilerStack.h>
2014-12-03 15:40:37 +00:00
# include <libsolidity/InterfaceHandler.h>
2014-10-30 21:52:15 +00:00
2015-01-13 14:59:42 +00:00
# include <libdevcrypto/SHA3.h>
2014-10-30 21:52:15 +00:00
using namespace std ;
namespace dev
{
namespace solidity
{
2015-01-28 12:39:04 +00:00
const map < string , string > StandardSources = map < string , string > {
2015-01-30 12:57:23 +00:00
{ " coin " , R " (import " CoinReg " ;import " Config " ;import " configUser " ;contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}}) " } ,
2015-01-28 12:39:04 +00:00
{ " Coin " , R " (contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}) " } ,
{ " CoinReg " , R " (contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}) " } ,
{ " configUser " , R " (contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}}) " } ,
{ " Config " , R " (contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}) " } ,
{ " mortal " , R " (import " owned " ;contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}) " } ,
{ " named " , R " (import " Config " ;import " NameReg " ;import " configUser " ;contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}}) " } ,
{ " NameReg " , R " (contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}) " } ,
{ " owned " , R " (contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;}) " } ,
{ " service " , R " (import " Config " ;import " configUser " ;contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}}) " } ,
2015-01-30 12:57:23 +00:00
{ " std " , R " (import " owned " ;import " mortal " ;import " Config " ;import " configUser " ;import " NameReg " ;import " named " ;) " }
2015-01-28 12:39:04 +00:00
} ;
CompilerStack : : CompilerStack ( bool _addStandardSources ) :
m_addStandardSources ( _addStandardSources ) , m_parseSuccessful ( false )
{
if ( m_addStandardSources )
addSources ( StandardSources ) ;
}
2014-12-18 13:39:16 +00:00
bool CompilerStack : : addSource ( string const & _name , string const & _content )
2014-12-03 16:45:12 +00:00
{
2015-01-13 10:18:08 +00:00
bool existed = m_sources . count ( _name ) ! = 0 ;
2014-12-03 16:45:12 +00:00
reset ( true ) ;
2015-01-29 01:34:57 +00:00
m_sources [ _name ] . scanner = make_shared < Scanner > ( CharStream ( expanded ( _content ) ) , _name ) ;
2014-12-18 13:39:16 +00:00
return existed ;
2014-12-03 16:45:12 +00:00
}
2014-11-11 16:41:48 +00:00
void CompilerStack : : setSource ( string const & _sourceCode )
2014-10-30 21:52:15 +00:00
{
2014-11-11 16:41:48 +00:00
reset ( ) ;
2015-01-29 01:34:57 +00:00
addSource ( " " , expanded ( _sourceCode ) ) ;
2014-11-11 16:41:48 +00:00
}
void CompilerStack : : parse ( )
{
2014-12-03 16:45:12 +00:00
for ( auto & sourcePair : m_sources )
{
sourcePair . second . scanner - > reset ( ) ;
sourcePair . second . ast = Parser ( ) . parse ( sourcePair . second . scanner ) ;
}
resolveImports ( ) ;
2014-11-20 17:33:23 +00:00
m_globalContext = make_shared < GlobalContext > ( ) ;
2014-12-03 16:45:12 +00:00
NameAndTypeResolver resolver ( m_globalContext - > getDeclarations ( ) ) ;
for ( Source const * source : m_sourceOrder )
resolver . registerDeclarations ( * source - > ast ) ;
for ( Source const * source : m_sourceOrder )
for ( ASTPointer < ASTNode > const & node : source - > ast - > getNodes ( ) )
if ( ContractDefinition * contract = dynamic_cast < ContractDefinition * > ( node . get ( ) ) )
{
m_globalContext - > setCurrentContract ( * contract ) ;
resolver . updateDeclaration ( * m_globalContext - > getCurrentThis ( ) ) ;
2015-01-27 13:32:59 +00:00
resolver . updateDeclaration ( * m_globalContext - > getCurrentSuper ( ) ) ;
2014-12-03 16:45:12 +00:00
resolver . resolveNamesAndTypes ( * contract ) ;
m_contracts [ contract - > getName ( ) ] . contract = contract ;
}
2014-12-16 22:45:24 +00:00
for ( Source const * source : m_sourceOrder )
for ( ASTPointer < ASTNode > const & node : source - > ast - > getNodes ( ) )
if ( ContractDefinition * contract = dynamic_cast < ContractDefinition * > ( node . get ( ) ) )
{
m_globalContext - > setCurrentContract ( * contract ) ;
resolver . updateDeclaration ( * m_globalContext - > getCurrentThis ( ) ) ;
resolver . checkTypeRequirements ( * contract ) ;
m_contracts [ contract - > getName ( ) ] . contract = contract ;
}
2014-11-11 16:41:48 +00:00
m_parseSuccessful = true ;
}
void CompilerStack : : parse ( string const & _sourceCode )
{
setSource ( _sourceCode ) ;
parse ( ) ;
}
2014-12-06 01:39:58 +00:00
vector < string > CompilerStack : : getContractNames ( ) const
2014-11-11 16:41:48 +00:00
{
if ( ! m_parseSuccessful )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " Parsing was not successful. " ) ) ;
2014-12-03 17:52:28 +00:00
vector < string > contractNames ;
for ( auto const & contract : m_contracts )
contractNames . push_back ( contract . first ) ;
return contractNames ;
}
2015-01-29 01:34:57 +00:00
////// BEGIN: TEMPORARY ONLY
2015-01-30 12:57:23 +00:00
///
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
///
2015-01-29 01:34:57 +00:00
/// remove once import works properly and we have genesis contracts
string CompilerStack : : expanded ( string const & _sourceCode )
{
const map < string , string > c_standardSources = map < string , string > {
{ " Config " , " contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}} " } ,
{ " Coin " , " contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}} " } ,
{ " CoinReg " , " contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}} " } ,
{ " coin " , " #require CoinReg \n contract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}} " } ,
{ " service " , " #require Config \n contract service{function service(uint _n){Config().register(_n, this);}} " } ,
{ " owned " , " contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;} " } ,
{ " mortal " , " #require owned \n contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }} " } ,
{ " NameReg " , " contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}} " } ,
{ " named " , " #require Config NameReg \n contract named {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}} " } ,
{ " std " , " #require owned mortal Config NameReg named " } ,
} ;
string sub ;
set < string > got ;
function < string ( string const & ) > localExpanded ;
localExpanded = [ & ] ( string const & s ) - > string
{
string ret = s ;
for ( size_t p = 0 ; p ! = string : : npos ; )
if ( ( p = ret . find ( " #require " ) ) ! = string : : npos )
{
string n = ret . substr ( p + 9 , ret . find_first_of ( ' \n ' , p + 9 ) - p - 9 ) ;
ret . replace ( p , n . size ( ) + 9 , " " ) ;
vector < string > rs ;
boost : : split ( rs , n , boost : : is_any_of ( " \t , " ) , boost : : token_compress_on ) ;
for ( auto const & r : rs )
if ( ! got . count ( r ) )
{
if ( c_standardSources . count ( r ) )
sub . append ( " \n " + localExpanded ( c_standardSources . at ( r ) ) + " \n " ) ;
got . insert ( r ) ;
}
}
// TODO: remove once we have genesis contracts.
else if ( ( p = ret . find ( " Config() " ) ) ! = string : : npos )
ret . replace ( p , 8 , " Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b) " ) ;
return ret ;
} ;
return sub + localExpanded ( _sourceCode ) ;
}
////// END: TEMPORARY ONLY
2014-12-03 17:52:28 +00:00
void CompilerStack : : compile ( bool _optimize )
{
if ( ! m_parseSuccessful )
parse ( ) ;
2014-12-12 15:49:26 +00:00
map < ContractDefinition const * , bytes const * > contractBytecode ;
2014-12-03 16:45:12 +00:00
for ( Source const * source : m_sourceOrder )
for ( ASTPointer < ASTNode > const & node : source - > ast - > getNodes ( ) )
if ( ContractDefinition * contract = dynamic_cast < ContractDefinition * > ( node . get ( ) ) )
{
2014-12-11 16:35:23 +00:00
shared_ptr < Compiler > compiler = make_shared < Compiler > ( _optimize ) ;
2015-01-16 17:52:27 +00:00
compiler - > compileContract ( * contract , contractBytecode ) ;
2014-12-03 16:45:12 +00:00
Contract & compiledContract = m_contracts [ contract - > getName ( ) ] ;
2014-12-11 16:35:23 +00:00
compiledContract . bytecode = compiler - > getAssembledBytecode ( ) ;
2015-01-13 14:59:42 +00:00
compiledContract . runtimeBytecode = compiler - > getRuntimeBytecode ( ) ;
2014-12-03 16:45:12 +00:00
compiledContract . compiler = move ( compiler ) ;
2014-12-12 15:49:26 +00:00
contractBytecode [ compiledContract . contract ] = & compiledContract . bytecode ;
2014-12-03 16:45:12 +00:00
}
2014-11-11 16:41:48 +00:00
}
2014-10-30 21:52:15 +00:00
2014-11-11 16:41:48 +00:00
bytes const & CompilerStack : : compile ( string const & _sourceCode , bool _optimize )
{
2015-01-25 01:42:49 +00:00
parse ( _sourceCode ) ;
2014-12-03 16:45:12 +00:00
compile ( _optimize ) ;
return getBytecode ( ) ;
2014-10-30 21:52:15 +00:00
}
2014-12-06 01:39:58 +00:00
bytes const & CompilerStack : : getBytecode ( string const & _contractName ) const
2014-11-11 16:41:48 +00:00
{
2014-12-03 16:45:12 +00:00
return getContract ( _contractName ) . bytecode ;
2014-11-11 16:41:48 +00:00
}
2015-01-14 15:49:09 +00:00
bytes const & CompilerStack : : getRuntimeBytecode ( string const & _contractName ) const
2015-01-13 14:59:42 +00:00
{
return getContract ( _contractName ) . runtimeBytecode ;
}
2015-01-14 15:49:09 +00:00
dev : : h256 CompilerStack : : getContractCodeHash ( string const & _contractName ) const
2015-01-13 14:59:42 +00:00
{
return dev : : sha3 ( getRuntimeBytecode ( _contractName ) ) ;
}
2014-12-06 01:39:58 +00:00
void CompilerStack : : streamAssembly ( ostream & _outStream , string const & _contractName ) const
2014-11-11 16:41:48 +00:00
{
2014-12-03 16:45:12 +00:00
getContract ( _contractName ) . compiler - > streamAssembly ( _outStream ) ;
}
2014-12-06 01:39:58 +00:00
string const & CompilerStack : : getInterface ( string const & _contractName ) const
2014-12-03 16:45:12 +00:00
{
2015-01-09 07:09:30 +00:00
return getMetadata ( _contractName , DocumentationType : : ABI_INTERFACE ) ;
2014-12-05 14:27:07 +00:00
}
2015-01-08 23:22:06 +00:00
string const & CompilerStack : : getSolidityInterface ( string const & _contractName ) const
{
2015-01-09 07:09:30 +00:00
return getMetadata ( _contractName , DocumentationType : : ABI_SOLIDITY_INTERFACE ) ;
2015-01-08 23:22:06 +00:00
}
2015-01-09 07:09:30 +00:00
string const & CompilerStack : : getMetadata ( string const & _contractName , DocumentationType _type ) const
2014-12-01 16:03:04 +00:00
{
if ( ! m_parseSuccessful )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " Parsing was not successful. " ) ) ;
2014-12-01 17:01:42 +00:00
2014-12-06 01:39:58 +00:00
Contract const & contract = getContract ( _contractName ) ;
2014-12-03 12:50:04 +00:00
2014-12-06 01:39:58 +00:00
std : : unique_ptr < string const > * doc ;
2014-12-03 15:40:37 +00:00
switch ( _type )
2014-11-11 16:41:48 +00:00
{
2014-12-05 14:50:39 +00:00
case DocumentationType : : NATSPEC_USER :
2014-12-05 14:27:07 +00:00
doc = & contract . userDocumentation ;
break ;
2014-12-05 14:50:39 +00:00
case DocumentationType : : NATSPEC_DEV :
2014-12-05 14:27:07 +00:00
doc = & contract . devDocumentation ;
break ;
2014-12-05 14:50:39 +00:00
case DocumentationType : : ABI_INTERFACE :
2014-12-05 14:27:07 +00:00
doc = & contract . interface ;
break ;
2015-01-08 23:22:06 +00:00
case DocumentationType : : ABI_SOLIDITY_INTERFACE :
doc = & contract . solidityInterface ;
break ;
2014-12-05 14:27:07 +00:00
default :
BOOST_THROW_EXCEPTION ( InternalCompilerError ( ) < < errinfo_comment ( " Illegal documentation type. " ) ) ;
2014-11-11 16:41:48 +00:00
}
2014-12-05 14:27:07 +00:00
if ( ! * doc )
* doc = contract . interfaceHandler - > getDocumentation ( * contract . contract , _type ) ;
return * ( * doc ) ;
2014-12-03 16:45:12 +00:00
}
2014-12-06 01:39:58 +00:00
Scanner const & CompilerStack : : getScanner ( string const & _sourceName ) const
2014-12-03 16:45:12 +00:00
{
return * getSource ( _sourceName ) . scanner ;
}
2014-12-06 01:39:58 +00:00
SourceUnit const & CompilerStack : : getAST ( string const & _sourceName ) const
2014-12-03 16:45:12 +00:00
{
return * getSource ( _sourceName ) . ast ;
2014-11-11 16:41:48 +00:00
}
2014-12-18 13:39:16 +00:00
ContractDefinition const & CompilerStack : : getContractDefinition ( string const & _contractName ) const
{
return * getContract ( _contractName ) . contract ;
}
2014-11-11 16:41:48 +00:00
bytes CompilerStack : : staticCompile ( std : : string const & _sourceCode , bool _optimize )
{
CompilerStack stack ;
return stack . compile ( _sourceCode , _optimize ) ;
}
2014-12-03 16:45:12 +00:00
void CompilerStack : : reset ( bool _keepSources )
{
m_parseSuccessful = false ;
if ( _keepSources )
for ( auto sourcePair : m_sources )
sourcePair . second . reset ( ) ;
else
2015-01-28 12:39:04 +00:00
{
2014-12-03 16:45:12 +00:00
m_sources . clear ( ) ;
2015-01-28 12:39:04 +00:00
if ( m_addStandardSources )
addSources ( StandardSources ) ;
}
2014-12-03 16:45:12 +00:00
m_globalContext . reset ( ) ;
m_sourceOrder . clear ( ) ;
m_contracts . clear ( ) ;
}
void CompilerStack : : resolveImports ( )
{
// topological sorting (depth first search) of the import graph, cutting potential cycles
vector < Source const * > sourceOrder ;
set < Source const * > sourcesSeen ;
function < void ( Source const * ) > toposort = [ & ] ( Source const * _source )
{
if ( sourcesSeen . count ( _source ) )
return ;
sourcesSeen . insert ( _source ) ;
for ( ASTPointer < ASTNode > const & node : _source - > ast - > getNodes ( ) )
if ( ImportDirective const * import = dynamic_cast < ImportDirective * > ( node . get ( ) ) )
{
2014-12-05 14:35:05 +00:00
string const & id = import - > getIdentifier ( ) ;
if ( ! m_sources . count ( id ) )
2014-12-03 16:45:12 +00:00
BOOST_THROW_EXCEPTION ( ParserError ( )
< < errinfo_sourceLocation ( import - > getLocation ( ) )
< < errinfo_comment ( " Source not found. " ) ) ;
2014-12-05 14:35:05 +00:00
toposort ( & m_sources [ id ] ) ;
2014-12-03 16:45:12 +00:00
}
sourceOrder . push_back ( _source ) ;
} ;
for ( auto const & sourcePair : m_sources )
toposort ( & sourcePair . second ) ;
2014-11-11 16:41:48 +00:00
2014-12-03 16:45:12 +00:00
swap ( m_sourceOrder , sourceOrder ) ;
}
2014-11-11 16:41:48 +00:00
2014-12-06 01:39:58 +00:00
CompilerStack : : Contract const & CompilerStack : : getContract ( string const & _contractName ) const
2014-12-03 16:45:12 +00:00
{
if ( m_contracts . empty ( ) )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " No compiled contracts found. " ) ) ;
2014-12-17 17:33:55 +00:00
string contractName = _contractName ;
2014-12-03 16:45:12 +00:00
if ( _contractName . empty ( ) )
2015-01-28 13:16:15 +00:00
// try to find some user-supplied contract
for ( auto const & it : m_sources )
if ( ! StandardSources . count ( it . first ) )
for ( ASTPointer < ASTNode > const & node : it . second . ast - > getNodes ( ) )
if ( auto contract = dynamic_cast < ContractDefinition const * > ( node . get ( ) ) )
contractName = contract - > getName ( ) ;
2014-12-17 17:33:55 +00:00
auto it = m_contracts . find ( contractName ) ;
2014-12-03 16:45:12 +00:00
if ( it = = m_contracts . end ( ) )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " Contract " + _contractName + " not found. " ) ) ;
return it - > second ;
}
2014-12-06 01:39:58 +00:00
CompilerStack : : Source const & CompilerStack : : getSource ( string const & _sourceName ) const
2014-12-03 16:45:12 +00:00
{
auto it = m_sources . find ( _sourceName ) ;
if ( it = = m_sources . end ( ) )
BOOST_THROW_EXCEPTION ( CompilerError ( ) < < errinfo_comment ( " Given source file not found. " ) ) ;
return it - > second ;
}
2014-11-11 16:41:48 +00:00
2014-12-05 14:27:07 +00:00
CompilerStack : : Contract : : Contract ( ) : interfaceHandler ( make_shared < InterfaceHandler > ( ) ) { }
2014-11-11 16:41:48 +00:00
2014-10-30 21:52:15 +00:00
}
}