Browse Source

Merge remote-tracking branch 'upstream/develop' into bugfix

Conflicts:
	libevm/VM.h
cl-refactor
CJentzsch 10 years ago
parent
commit
563132bde0
  1. 21
      alethzero/MainWin.cpp
  2. 1
      alethzero/MainWin.h
  3. 18
      eth/main.cpp
  4. 1
      libdevcore/vector_ref.h
  5. 6
      libdevcrypto/CryptoPP.cpp
  6. 14
      libethcore/BlockInfo.cpp
  7. 12
      libethcore/BlockInfo.h
  8. 2
      libethcore/CommonEth.cpp
  9. 5
      libethereum/BlockChain.cpp
  10. 3
      libethereum/BlockDetails.cpp
  11. 3
      libethereum/BlockDetails.h
  12. 2
      libethereum/Client.cpp
  13. 111
      libethereum/Executive.cpp
  14. 34
      libethereum/Executive.h
  15. 36
      libethereum/ExtVM.cpp
  16. 31
      libethereum/ExtVM.h
  17. 46
      libethereum/Manifest.cpp
  18. 73
      libethereum/Manifest.h
  19. 125
      libethereum/MessageFilter.cpp
  20. 40
      libethereum/MessageFilter.h
  21. 28
      libethereum/PastMessage.cpp
  22. 56
      libethereum/PastMessage.h
  23. 206
      libethereum/State.cpp
  24. 28
      libethereum/State.h
  25. 6
      libethereum/TransactionReceipt.h
  26. 20
      libevm/ExtVMFace.h
  27. 4
      libevm/FeeStructure.cpp
  28. 2
      libevm/FeeStructure.h
  29. 6
      libevm/VM.cpp
  30. 68
      libevm/VM.h
  31. 55
      libevm/VMFace.h
  32. 42
      libevm/VMFactory.cpp
  33. 42
      libevm/VMFactory.h
  34. 5
      liblll/CompilerState.cpp
  35. 2
      libp2p/Common.h
  36. 526
      libp2p/Host.cpp
  37. 45
      libp2p/Host.h
  38. 187
      libp2p/Network.cpp
  39. 63
      libp2p/Network.h
  40. 6
      libp2p/Session.cpp
  41. 282
      libsolidity/AST.cpp
  42. 171
      libsolidity/AST.h
  43. 2
      libsolidity/ASTForward.h
  44. 131
      libsolidity/ASTPrinter.cpp
  45. 129
      libsolidity/ASTPrinter.h
  46. 76
      libsolidity/ASTVisitor.h
  47. 493
      libsolidity/AST_accept.h
  48. 10
      libsolidity/BaseTypes.h
  49. 4
      libsolidity/CMakeLists.txt
  50. 99
      libsolidity/Compiler.cpp
  51. 26
      libsolidity/Compiler.h
  52. 30
      libsolidity/CompilerContext.cpp
  53. 13
      libsolidity/CompilerContext.h
  54. 212
      libsolidity/CompilerStack.cpp
  55. 83
      libsolidity/CompilerStack.h
  56. 111
      libsolidity/CompilerUtils.cpp
  57. 75
      libsolidity/CompilerUtils.h
  58. 6
      libsolidity/DeclarationContainer.cpp
  59. 15
      libsolidity/DeclarationContainer.h
  60. 2
      libsolidity/Exceptions.h
  61. 279
      libsolidity/ExpressionCompiler.cpp
  62. 48
      libsolidity/ExpressionCompiler.h
  63. 9
      libsolidity/GlobalContext.cpp
  64. 10
      libsolidity/GlobalContext.h
  65. 340
      libsolidity/InterfaceHandler.cpp
  66. 123
      libsolidity/InterfaceHandler.h
  67. 31
      libsolidity/NameAndTypeResolver.cpp
  68. 18
      libsolidity/NameAndTypeResolver.h
  69. 91
      libsolidity/Parser.cpp
  70. 4
      libsolidity/Parser.h
  71. 11
      libsolidity/Scanner.cpp
  72. 14
      libsolidity/Scanner.h
  73. 41
      libsolidity/SourceReferenceFormatter.cpp
  74. 4
      libsolidity/SourceReferenceFormatter.h
  75. 35
      libsolidity/Token.h
  76. 110
      libsolidity/Types.cpp
  77. 55
      libsolidity/Types.h
  78. 2
      libweb3jsonrpc/WebThreeStubServer.cpp
  79. 4
      libwebthree/WebThree.cpp
  80. 5
      libwebthree/WebThree.h
  81. 14
      libwhisper/Common.cpp
  82. 9
      libwhisper/Message.cpp
  83. 9
      libwhisper/Message.h
  84. 2
      libwhisper/WhisperHost.cpp
  85. 2
      libwhisper/WhisperHost.h
  86. 26
      libwhisper/WhisperPeer.cpp
  87. 2
      libwhisper/WhisperPeer.h
  88. 2
      mix/ConstantCompilationModel.cpp
  89. 2
      solc/CMakeLists.txt
  90. 383
      solc/CommandLineInterface.cpp
  91. 71
      solc/CommandLineInterface.h
  92. 120
      solc/main.cpp
  93. 20
      test/TestHelper.cpp
  94. 4
      test/TestHelper.h
  95. 10
      test/createRandomTest.cpp
  96. 8
      test/crypto.cpp
  97. 2
      test/genesis.cpp
  98. 4
      test/jsonrpc.cpp
  99. 43
      test/solidityCompiler.cpp
  100. 343
      test/solidityEndToEndTest.cpp

21
alethzero/MainWin.cpp

@ -1261,9 +1261,12 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Gas used/limit: <b>" << info.gasUsed << "</b>/<b>" << info.gasLimit << "</b>";
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Hash w/o nonce: <b>" << info.headerHashWithoutNonce() << "</b>";
s << "<br/>Hash w/o nonce: <b>" << info.headerHash(WithoutNonce) << "</b>";
s << "<br/>Difficulty: <b>" << info.difficulty << "</b>";
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
if (info.number)
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHash(WithoutNonce), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
else
s << "<br/>Proof-of-Work: <i>Phil has nothing to prove</i>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
@ -1280,7 +1283,7 @@ void Main::on_blocks_currentItemChanged()
if (info.parentHash)
s << "<br/>Pre: <b>" << BlockInfo(ethereum()->blockChain().block(info.parentHash)).stateRoot << "</b>";
else
s << "<br/>Pre: <i>Nothing is before the Gensesis</i>";
s << "<br/>Pre: <i>Nothing is before Phil</i>";
for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>";
@ -1347,7 +1350,7 @@ void Main::on_debugCurrent_triggered()
{
unsigned txi = item->data(Qt::UserRole + 1).toInt();
m_executiveState = ethereum()->state(txi + 1, h);
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState));
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState, 0));
Transaction t = m_executiveState.pending()[txi];
m_executiveState = m_executiveState.fromPending(txi);
auto r = t.rlp();
@ -1395,10 +1398,10 @@ void Main::populateDebugger(dev::bytesConstRef _r)
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
{
dev::eth::VM& vm = *(dev::eth::VM*)voidVM;
dev::eth::ExtVM const& ext = *(dev::eth::ExtVM const*)voidExt;
dev::eth::VM& vm = *voidVM;
dev::eth::ExtVM const& ext = *static_cast<dev::eth::ExtVM const*>(voidExt);
if (ext.code != lastExtCode)
{
lastExtCode = ext.code;
@ -1626,7 +1629,7 @@ void Main::on_data_textChanged()
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner());
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
}
catch (...)
@ -1852,7 +1855,7 @@ void Main::on_debug_clicked()
{
Secret s = i.secret();
m_executiveState = ethereum()->postState();
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState));
m_currentExecution = unique_ptr<Executive>(new Executive(m_executiveState, 0));
Transaction t = isCreation() ?
Transaction(value(), gasPrice(), ui->gas->value(), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s) :
Transaction(value(), gasPrice(), ui->gas->value(), fromString(ui->destination->currentText()), m_data, m_executiveState.transactionsFrom(dev::toAddress(s)), s);

1
alethzero/MainWin.h

@ -33,6 +33,7 @@
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libqethereum/QEthereum.h>
#include <libwebthree/WebThree.h>

18
eth/main.cpp

@ -620,7 +620,7 @@ int main(int argc, char** argv)
dev::eth::State state =c->state(index + 1,c->blockChain().numberHash(block));
if (index < state.pending().size())
{
Executive e(state);
Executive e(state, 0);
Transaction t = state.pending()[index];
state = state.fromPending(index);
bytes r = t.rlp();
@ -630,10 +630,10 @@ int main(int argc, char** argv)
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, void* vvm, void const* vextVM)
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << endl << " STACK" << endl;
for (auto i: vm->stack())
f << (h256)i << endl;
@ -644,17 +644,17 @@ int main(int argc, char** argv)
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, void* vvm, void const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = (VM*)vvm;
dev::eth::ExtVM const* ext = (ExtVM const*)vextVM;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;

1
libdevcore/vector_ref.h

@ -40,6 +40,7 @@ public:
vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); }
void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; }
void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); }
void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); }
_T* begin() { return m_data; }
_T* end() { return m_data + m_count; }

6
libdevcrypto/CryptoPP.cpp

@ -52,6 +52,12 @@ void Secp256k1::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
initializeDLScheme(_k, d);
if (!io_text.size())
{
io_text.resize(1);
io_text[0] = 0;
}
size_t clen = io_text.size();
bytes plain;

14
libethcore/BlockInfo.cpp

@ -49,19 +49,19 @@ BlockInfo BlockInfo::fromHeader(bytesConstRef _block)
return ret;
}
h256 BlockInfo::headerHashWithoutNonce() const
h256 BlockInfo::headerHash(IncludeNonce _n) const
{
RLPStream s;
streamRLP(s, false);
streamRLP(s, _n);
return sha3(s.out());
}
void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
void BlockInfo::streamRLP(RLPStream& _s, IncludeNonce _n) const
{
_s.appendList(_nonce ? 14 : 13)
_s.appendList(_n == WithNonce ? 14 : 13)
<< parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
<< difficulty << number << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
if (_n == WithNonce)
_s << nonce;
}
@ -100,8 +100,8 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
}
// check it hashes according to proof of work or that it's the genesis block.
if (_checkNonce && parentHash && !ProofOfWork::verify(headerHashWithoutNonce(), nonce, difficulty))
BOOST_THROW_EXCEPTION(InvalidBlockNonce(headerHashWithoutNonce(), nonce, difficulty));
if (_checkNonce && parentHash && !ProofOfWork::verify(headerHash(WithoutNonce), nonce, difficulty))
BOOST_THROW_EXCEPTION(InvalidBlockNonce(headerHash(WithoutNonce), nonce, difficulty));
if (gasUsed > gasLimit)
BOOST_THROW_EXCEPTION(TooMuchGasUsed());

12
libethcore/BlockInfo.h

@ -32,6 +32,12 @@ namespace eth
extern u256 c_genesisDifficulty;
enum IncludeNonce
{
WithoutNonce = 0,
WithNonce = 1
};
/** @brief Encapsulation of a block header.
* Class to contain all of a block header's data. It is able to parse a block header and populate
* from some given RLP block serialisation with the static fromHeader(), through the method
@ -48,7 +54,7 @@ extern u256 c_genesisDifficulty;
*
* The difficulty and gas-limit derivations may be calculated with the calculateDifficulty()
* and calculateGasLimit() and the object serialised to RLP with streamRLP. To determine the
* header hash without the nonce (for mining), the method headerHashWithoutNonce() is provided.
* header hash without the nonce (for mining), the method headerHash(WithoutNonce) is provided.
*
* The default constructor creates an empty object, which can be tested against with the boolean
* conversion operator.
@ -113,8 +119,8 @@ public:
u256 calculateGasLimit(BlockInfo const& _parent) const;
/// No-nonce sha3 of the header only.
h256 headerHashWithoutNonce() const;
void streamRLP(RLPStream& _s, bool _nonce) const;
h256 headerHash(IncludeNonce _n) const;
void streamRLP(RLPStream& _s, IncludeNonce _n) const;
};
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)

2
libethcore/CommonEth.cpp

@ -33,7 +33,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 48;
const unsigned c_protocolVersion = 49;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =

5
libethereum/BlockChain.cpp

@ -145,7 +145,7 @@ void BlockChain::open(std::string _path, bool _killExisting)
if (!details(m_genesisHash))
{
// Insert details of genesis block.
m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {}, h256());
m_details[m_genesisHash] = BlockDetails(0, c_genesisDifficulty, h256(), {});
auto r = m_details[m_genesisHash].rlp();
m_extrasDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)dev::ref(r));
}
@ -303,7 +303,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// Get total difficulty increase and update state, checking it.
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
auto b = s.oldBloom();
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
@ -320,7 +319,7 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
// All ok - insert into DB
{
WriteGuard l(x_details);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b);
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {});
m_details[bi.parentHash].children.push_back(newHash);
}
{

3
libethereum/BlockDetails.cpp

@ -32,10 +32,9 @@ BlockDetails::BlockDetails(RLP const& _r)
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
bloom = _r[4].toHash<h256>();
}
bytes BlockDetails::rlp() const
{
return rlpList(number, totalDifficulty, parent, children, bloom);
return rlpList(number, totalDifficulty, parent, children);
}

3
libethereum/BlockDetails.h

@ -40,7 +40,7 @@ namespace eth
struct BlockDetails
{
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c, h256 _bloom): number(_n), totalDifficulty(_tD), parent(_p), children(_c), bloom(_bloom) {}
BlockDetails(unsigned _n, u256 _tD, h256 _p, h256s _c): number(_n), totalDifficulty(_tD), parent(_p), children(_c) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
@ -51,7 +51,6 @@ struct BlockDetails
u256 totalDifficulty;
h256 parent;
h256s children;
h256 bloom;
};
struct BlockLogBlooms

2
libethereum/Client.cpp

@ -217,7 +217,7 @@ void Client::noteChanged(h256Set const& _filters)
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
cwatch << "!!!" << i.first << i.second.id;
// cwatch << "!!!" << i.first << i.second.id;
i.second.changes++;
}
}

111
libethereum/Executive.cpp

@ -19,11 +19,13 @@
* @date 2014
*/
#include "Executive.h"
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "Executive.h"
#include "State.h"
#include "ExtVM.h"
using namespace std;
@ -32,13 +34,6 @@ using namespace dev::eth;
#define ETH_VMTRACE 1
Executive::~Executive()
{
// TODO: Make safe.
delete m_ext;
delete m_vm;
}
u256 Executive::gasUsed() const
{
return m_t.gas() - m_endGas;
@ -91,30 +86,39 @@ bool Executive::setup(bytesConstRef _rlp)
clog(StateDetail) << "Paying" << formatBalance(cost) << "from sender (includes" << m_t.gas() << "gas at" << formatBalance(m_t.gasPrice()) << ")";
m_s.subBalance(m_sender, cost);
if (m_ms)
{
m_ms->from = m_sender;
m_ms->to = m_t.receiveAddress();
m_ms->value = m_t.value();
m_ms->input = m_t.data();
}
if (m_t.isCreation())
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender);
else
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
m_s.addBalance(_receiveAddress, _value);
if (m_s.addressHasCode(_receiveAddress))
auto it = !(_codeAddress & ~h160(0xffffffff)) ? State::precompiled().find((unsigned)(u160)_codeAddress) : State::precompiled().end();
if (it != State::precompiled().end())
{
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
m_excepted = true;
}
else
{
m_endGas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
{
m_vm = new VM(_gas);
bytes const& c = m_s.code(_receiveAddress);
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms);
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
else
m_endGas = _gas;
@ -123,6 +127,8 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _init, Address _origin)
{
m_isCreation = true;
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
@ -131,17 +137,17 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception);
// Execute _init.
m_vm = new VM(_gas);
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms);
m_vm = VMFactory::create(_gas);
m_ext = make_shared<ExtVM>(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
return _init.empty();
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, void* voidVM, void const* voidExt)
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *(ExtVM const*)voidExt;
VM& vm = *(VM*)voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
ostringstream o;
o << endl << " STACK" << endl;
@ -161,17 +167,20 @@ bool Executive::go(OnOpFunc const& _onOp)
if (m_vm)
{
boost::timer t;
auto sgas = m_vm->gas();
bool revert = false;
// auto sgas = m_vm->gas();
try
{
m_out = m_vm->go(*m_ext, _onOp);
if (m_ext)
m_endGas = m_vm->gas();
if (m_isCreation)
{
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_logs = m_ext->sub.logs;
if (m_out.size() * c_createDataGas <= m_endGas)
m_endGas -= m_out.size() * c_createDataGas;
else
m_out.reset();
m_s.m_cache[m_newAddress].setCode(m_out);
}
m_endGas = m_vm->gas();
}
catch (StepsDone const&)
{
@ -181,7 +190,10 @@ bool Executive::go(OnOpFunc const& _onOp)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
m_endGas = 0;//m_vm->gas();
revert = true;
m_excepted = true;
// Write state out only in the case of a non-excepted transaction.
m_ext->revert();
}
catch (Exception const& _e)
{
@ -193,46 +205,33 @@ bool Executive::go(OnOpFunc const& _onOp)
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
// Write state out only in the case of a non-excepted transaction.
if (revert)
{
m_ext->revert();
// Explicitly delete a newly created address - this will still be in the reverted state.
if (m_newAddress)
{
m_s.m_cache.erase(m_newAddress);
m_newAddress = Address();
}
}
// cnote << "VM took:" << t.elapsed() << "; gas used: " << (sgas - m_endGas);
}
return true;
}
u256 Executive::gas() const
/*u256 Executive::gas() const
{
return m_vm ? m_vm->gas() : m_endGas;
}
}*/
void Executive::finalize(OnOpFunc const&)
{
if (m_t.isCreation() && m_newAddress && m_out.size())
// non-reverted creation - put code in place.
m_s.m_cache[m_newAddress].setCode(m_out);
// SSTORE refunds.
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_sender, m_endGas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
// cnote << "Transferring" << formatBalance(gasSpent) << "to miner.";
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
if (m_ms)
m_ms->output = m_out.toBytes();
// Suicides...
if (m_ext)
for (auto a: m_ext->sub.suicides)
m_s.m_cache[a].kill();
// Logs
m_logs = m_ext->sub.logs;
}

34
libethereum/Executive.h

@ -25,30 +25,32 @@
#include <libdevcore/Log.h>
#include <libevmcore/Instruction.h>
#include <libethcore/CommonEth.h>
#include <libevm/ExtVMFace.h>
#include <libevm/VMFace.h>
#include "Transaction.h"
#include "Manifest.h"
namespace dev
{
namespace eth
{
class VM;
class ExtVM;
class State;
class ExtVM;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; };
class Executive
{
public:
Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {}
~Executive();
Executive(State& _s, unsigned _level): m_s(_s), m_depth(_level) {}
~Executive() = default;
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
bool setup(bytesConstRef _transaction);
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool go(OnOpFunc const& _onOp = OnOpFunc());
void finalize(OnOpFunc const& _onOp = OnOpFunc());
u256 gasUsed() const;
@ -57,25 +59,29 @@ public:
Transaction const& t() const { return m_t; }
u256 gas() const;
u256 endGas() const { return m_endGas; }
bytesConstRef out() const { return m_out; }
h160 newAddress() const { return m_newAddress; }
LogEntries const& logs() const { return m_logs; }
bool excepted() const { return m_excepted; }
VM const& vm() const { return *m_vm; }
State const& state() const { return m_s; }
VMFace const& vm() const { return *m_vm; }
ExtVM const& ext() const { return *m_ext; }
State const& state() const { return m_s; }
private:
State& m_s;
ExtVM* m_ext = nullptr; // TODO: make safe.
VM* m_vm = nullptr;
Manifest* m_ms = nullptr;
bytesConstRef m_out;
std::shared_ptr<ExtVM> m_ext;
std::unique_ptr<VMFace> m_vm;
bytes m_precompiledOut; ///< Used for the output when there is no VM for a contract (i.e. precompiled).
bytesConstRef m_out; ///< Holds the copyable output.
Address m_newAddress;
Transaction m_t;
bool m_isCreation;
bool m_excepted = false;
unsigned m_depth = 0;
Address m_sender;
u256 m_endGas;

36
libethereum/ExtVM.cpp

@ -21,5 +21,37 @@
#include "ExtVM.h"
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; };
#include "Executive.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
bool ExtVM::call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp, Address _myAddressOverride, Address _codeAddressOverride)
{
Executive e(m_s, depth + 1);
if (!e.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, io_gas, origin))
{
e.go(_onOp);
sub += e.ext().sub;
}
io_gas = e.endGas();
e.out().copyTo(_out);
return !e.excepted();
}
h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp)
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
Executive e(m_s, depth + 1);
if (!e.create(myAddress, _endowment, gasPrice, io_gas, _code, origin))
{
e.go(_onOp);
sub += e.ext().sub;
}
io_gas = e.endGas();
return e.newAddress();
}

31
libethereum/ExtVM.h

@ -39,8 +39,8 @@ class ExtVM: public ExtVMFace
{
public:
/// Full constructor.
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms)
ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, unsigned _depth = 0):
ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code.toBytes(), _s.m_previousBlock, _s.m_currentBlock, _depth), m_s(_s), m_origCache(_s.m_cache)
{
m_s.ensureCached(_myAddress, true, true);
}
@ -49,34 +49,16 @@ public:
virtual u256 store(u256 _n) override final { return m_s.storage(myAddress, _n); }
/// Write a value in storage.
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); }
virtual void setStore(u256 _n, u256 _v) override final { m_s.setStorage(myAddress, _n, _v); }
/// Read address's code.
virtual bytes const& codeAt(Address _a) override final { return m_s.code(_a); }
/// Create a new contract.
virtual h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) override final
{
// Increment associated nonce for sender.
m_s.noteSending(myAddress);
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
}
virtual h160 create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc const& _onOp = {}) override final;
/// Create a new message call. Leave _myAddressOverride as the default to use the present address as caller.
virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final
{
if (m_ms)
m_ms->internal.resize(m_ms->internal.size() + 1);
auto ret = m_s.call(_receiveAddress, _codeAddressOverride ? _codeAddressOverride : _receiveAddress, _myAddressOverride ? _myAddressOverride : myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &sub, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, depth + 1);
if (m_ms && !m_ms->internal.back().from)
m_ms->internal.pop_back();
return ret;
}
virtual bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256& io_gas, bytesRef _out, OnOpFunc const& _onOp = {}, Address _myAddressOverride = {}, Address _codeAddressOverride = {}) override final;
/// Read address's balance.
virtual u256 balance(Address _a) override final { return m_s.balance(_a); }
@ -96,14 +78,13 @@ public:
/// Revert any changes made (by any of the other calls).
/// @TODO check call site for the parent manifest being discarded.
virtual void revert() override final { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; }
virtual void revert() override final { m_s.m_cache = m_origCache; sub.clear(); }
State& state() const { return m_s; }
private:
State& m_s; ///< A reference to the base state.
std::map<Address, Account> m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution.
Manifest* m_ms;
};
}

46
libethereum/Manifest.cpp

@ -1,46 +0,0 @@
/*
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/>.
*/
/** @file Manifest.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Manifest.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
Manifest::Manifest(bytesConstRef _r)
{
RLP r(_r);
from = r[0].toHash<Address>();
to = r[1].toHash<Address>();
value = r[2].toInt<u256>();
altered = r[3].toVector<u256>();
input = r[4].toBytes();
output = r[5].toBytes();
for (auto const& i: r[6])
internal.emplace_back(i.data());
}
void Manifest::streamRLP(RLPStream& _s) const
{
_s.appendList(7) << from << to << value << altered << input << output;
_s.appendList(internal.size());
for (auto const& i: internal)
i.streamRLP(_s);
}

73
libethereum/Manifest.h

@ -1,73 +0,0 @@
/*
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/>.
*/
/** @file Manifest.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <iostream>
#include <sstream>
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
namespace dev
{
namespace eth
{
struct Manifest;
using Manifests = std::vector<Manifest>;
/**
* @brief A record of the state-interaction of a transaction/call/create.
*/
struct Manifest
{
Manifest() {}
Manifest(bytesConstRef _r);
void streamRLP(RLPStream& _s) const;
h256 bloom() const { h256 ret = from.bloom() | to.bloom(); for (auto const& i: internal) ret |= i.bloom(); for (auto const& i: altered) ret |= h256(i).bloom(); return ret; }
std::string toString(unsigned _indent = 0) const
{
std::ostringstream oss;
oss << std::string(_indent * 3, ' ') << from << " -> " << to << " [" << value << "]: {";
if (internal.size())
{
oss << std::endl;
for (auto const& m: internal)
oss << m.toString(_indent + 1) << std::endl;
oss << std::string(_indent * 3, ' ');
}
oss << "} I:" << toHex(input) << "; O:" << toHex(output);
return oss.str();
}
Address from;
Address to;
u256 value;
u256s altered;
bytes input;
bytes output;
Manifests internal;
};
}
}

125
libethereum/MessageFilter.cpp

@ -27,131 +27,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void MessageFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(8) << m_from << m_to << m_stateAltered << m_altered << m_earliest << m_latest << m_max << m_skip;
}
h256 MessageFilter::sha3() const
{
RLPStream s;
streamRLP(s);
return dev::sha3(s.out());
}
bool MessageFilter::matches(h256 _bloom) const
{
auto have = [=](Address const& a) { return _bloom.contains(a.bloom()); };
if (m_from.size())
{
for (auto i: m_from)
if (have(i))
goto OK1;
return false;
}
OK1:
if (m_to.size())
{
for (auto i: m_to)
if (have(i))
goto OK2;
return false;
}
OK2:
if (m_stateAltered.size() || m_altered.size())
{
for (auto i: m_altered)
if (have(i))
goto OK3;
for (auto i: m_stateAltered)
if (have(i.first) && _bloom.contains(h256(i.second).bloom()))
goto OK3;
return false;
}
OK3:
return true;
}
bool MessageFilter::matches(State const& _s, unsigned _i) const
{
h256 b = _s.changesFromPending(_i).bloom();
if (!matches(b))
return false;
Transaction t = _s.pending()[_i];
if (!m_to.empty() && !m_to.count(t.receiveAddress()))
return false;
if (!m_from.empty() && !m_from.count(t.sender()))
return false;
if (m_stateAltered.empty() && m_altered.empty())
return true;
StateDiff d = _s.pendingDiff(_i);
if (!m_altered.empty())
{
for (auto const& s: m_altered)
if (d.accounts.count(s))
return true;
return false;
}
if (!m_stateAltered.empty())
{
for (auto const& s: m_stateAltered)
if (d.accounts.count(s.first) && d.accounts.at(s.first).storage.count(s.second))
return true;
return false;
}
return true;
}
PastMessages MessageFilter::matches(Manifest const& _m, unsigned _i) const
{
PastMessages ret;
matches(_m, vector<unsigned>(1, _i), _m.from, PastMessages(), ret);
return ret;
}
bool MessageFilter::matches(Manifest const& _m, vector<unsigned> _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const
{
bool ret;
if ((m_from.empty() || m_from.count(_m.from)) && (m_to.empty() || m_to.count(_m.to)))
_limbo.push_back(PastMessage(_m, _p, _o));
// Handle limbos, by checking against all addresses in alteration.
bool alters = m_altered.empty() && m_stateAltered.empty();
alters = alters || m_altered.count(_m.from) || m_altered.count(_m.to);
if (!alters)
for (auto const& i: _m.altered)
if (m_altered.count(_m.to) || m_stateAltered.count(make_pair(_m.to, i)))
{
alters = true;
break;
}
// If we do alter stuff,
if (alters)
{
o_ret += _limbo;
_limbo.clear();
ret = true;
}
_p.push_back(0);
for (auto const& m: _m.internal)
{
if (matches(m, _p, _o, _limbo, o_ret))
{
_limbo.clear();
ret = true;
}
_p.back()++;
}
return ret;
}
void LogFilter::streamRLP(RLPStream& _s) const
{
_s.appendList(6) << m_addresses << m_topics << m_earliest << m_latest << m_max << m_skip;

40
libethereum/MessageFilter.h

@ -24,7 +24,6 @@
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libethcore/CommonEth.h>
#include "PastMessage.h"
#include "TransactionReceipt.h"
namespace dev
@ -32,47 +31,8 @@ namespace dev
namespace eth
{
struct Manifest;
class State;
class MessageFilter
{
public:
MessageFilter(int _earliest = 0, int _latest = -1, unsigned _max = 10, unsigned _skip = 0): m_earliest(_earliest), m_latest(_latest), m_max(_max), m_skip(_skip) {}
void streamRLP(RLPStream& _s) const;
h256 sha3() const;
int earliest() const { return m_earliest; }
int latest() const { return m_latest; }
unsigned max() const { return m_max; }
unsigned skip() const { return m_skip; }
bool matches(h256 _bloom) const;
bool matches(State const& _s, unsigned _i) const;
PastMessages matches(Manifest const& _m, unsigned _i) const;
MessageFilter from(Address _a) { m_from.insert(_a); return *this; }
MessageFilter to(Address _a) { m_to.insert(_a); return *this; }
MessageFilter altered(Address _a, u256 _l) { m_stateAltered.insert(std::make_pair(_a, _l)); return *this; }
MessageFilter altered(Address _a) { m_altered.insert(_a); return *this; }
MessageFilter withMax(unsigned _m) { m_max = _m; return *this; }
MessageFilter withSkip(unsigned _m) { m_skip = _m; return *this; }
MessageFilter withEarliest(int _e) { m_earliest = _e; return *this; }
MessageFilter withLatest(int _e) { m_latest = _e; return *this; }
private:
bool matches(Manifest const& _m, std::vector<unsigned> _p, Address _o, PastMessages _limbo, PastMessages& o_ret) const;
std::set<Address> m_from;
std::set<Address> m_to;
std::set<std::pair<Address, u256>> m_stateAltered;
std::set<Address> m_altered;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;
unsigned m_skip;
};
class LogFilter
{
public:

28
libethereum/PastMessage.cpp

@ -1,28 +0,0 @@
/*
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/>.
*/
/** @file PastMessage.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "PastMessage.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace { char dummy; };

56
libethereum/PastMessage.h

@ -1,56 +0,0 @@
/*
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/>.
*/
/** @file PastMessage.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <libethcore/CommonEth.h>
#include "Manifest.h"
namespace dev
{
namespace eth
{
struct PastMessage
{
PastMessage(Manifest const& _m, std::vector<unsigned> _path, Address _o): to(_m.to), from(_m.from), value(_m.value), input(_m.input), output(_m.output), path(_path), origin(_o) {}
PastMessage& polish(h256 _b, u256 _ts, unsigned _n, Address _coinbase) { block = _b; timestamp = _ts; number = _n; coinbase = _coinbase; return *this; }
Address to; ///< The receiving address of the transaction. Address() in the case of a creation.
Address from; ///< The receiving address of the transaction. Address() in the case of a creation.
u256 value; ///< The value associated with the call.
bytes input; ///< The data associated with the message, or the initialiser if it's a creation transaction.
bytes output; ///< The data returned by the message, or the body code if it's a creation transaction.
std::vector<unsigned> path; ///< Call path into the block transaction. size() is always > 0. First item is the transaction index in the block.
Address origin; ///< Originating sender of the transaction.
Address coinbase; ///< Block coinbase.
h256 block; ///< Block hash.
u256 timestamp; ///< Block timestamp.
unsigned number; ///< Block number.
};
typedef std::vector<PastMessage> PastMessages;
}
}

206
libethereum/State.cpp

@ -29,10 +29,11 @@
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include "BlockChain.h"
#include "Defaults.h"
#include "ExtVM.h"
#include "Executive.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
@ -41,7 +42,7 @@ using namespace dev::eth;
static const u256 c_blockReward = 1500 * finney;
void ecrecoverCode(bytesConstRef _in, bytesRef _out)
bytes ecrecoverCode(bytesConstRef _in)
{
struct inType
{
@ -53,45 +54,45 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
memset(_out.data(), 0, _out.size());
h256 ret;
if ((u256)in.v > 28)
return;
return ret.asBytes();
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid())
return;
return ret.asBytes();
h256 ret;
byte pubkey[65];
int pubkeylen = 65;
secp256k1_start();
if (secp256k1_ecdsa_recover_compact(in.hash.data(), 32, in.r.data(), pubkey, &pubkeylen, 0, (int)(u256)in.v - 27))
ret = dev::sha3(bytesConstRef(&(pubkey[1]), 64));
memset(ret.data(), 0, 12);
memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret)));
return ret.asBytes();
}
void sha256Code(bytesConstRef _in, bytesRef _out)
bytes sha256Code(bytesConstRef _in)
{
h256 ret;
sha256(_in, bytesRef(ret.data(), 32));
memcpy(_out.data(), &ret, min(_out.size(), sizeof(ret)));
bytes ret(32);
sha256(_in, &ret);
return ret;
}
void ripemd160Code(bytesConstRef _in, bytesRef _out)
bytes ripemd160Code(bytesConstRef _in)
{
h256 ret;
ripemd160(_in, bytesRef(ret.data(), 32));
memset(_out.data(), 0, std::min<int>(12, _out.size()));
if (_out.size() > 12)
memcpy(_out.data() + 12, &ret, min(_out.size() - 12, sizeof(ret)));
bytes ret(32);
ripemd160(_in, &ret);
// leaves the 20-byte hash left-aligned. we want it right-aligned:
memmove(ret.data() + 12, ret.data(), 20);
memset(ret.data(), 0, 12);
return ret;
}
const std::map<unsigned, PrecompiledAddress> State::c_precompiled =
{
{ 1, { 500, ecrecoverCode }},
{ 2, { 100, sha256Code }},
{ 3, { 100, ripemd160Code }}
{ 1, { [](bytesConstRef) -> bigint { return (bigint)500; }, ecrecoverCode }},
{ 2, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, sha256Code }},
{ 3, { [](bytesConstRef i) -> bigint { return (bigint)50 + (i.size() + 31) / 32 * 50; }, ripemd160Code }}
};
OverlayDB State::openDB(std::string _path, bool _killExisting)
@ -764,7 +765,7 @@ bool State::amIJustParanoid(BlockChain const& _bc)
// Compile block:
RLPStream block;
block.appendList(3);
m_currentBlock.streamRLP(block, true);
m_currentBlock.streamRLP(block, WithNonce);
block.appendRaw(m_currentTxs);
block.appendRaw(m_currentUncles);
@ -791,14 +792,6 @@ bool State::amIJustParanoid(BlockChain const& _bc)
return false;
}
h256 State::oldBloom() const
{
h256 ret = m_currentBlock.coinbaseAddress.bloom();
for (auto const& i: m_receipts)
ret |= i.changes().bloom();
return ret;
}
LogBloom State::logBloom() const
{
LogBloom ret;
@ -839,7 +832,7 @@ void State::commitToMine(BlockChain const& _bc)
if (!knownUncles.count(u)) // ignore any uncles/mainline blocks that we know about.
{
BlockInfo ubi(_bc.block(u));
ubi.streamRLP(unclesData, true);
ubi.streamRLP(unclesData, WithNonce);
++unclesCount;
uncleAddresses.push_back(ubi.coinbaseAddress);
}
@ -903,13 +896,13 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo)
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
// TODO: Miner class that keeps dagger between mine calls (or just non-polling mining).
auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout, true, _turbo);
auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo);
if (!ret.completed)
m_currentBytes.clear();
else
{
cnote << "Completed" << m_currentBlock.headerHashWithoutNonce().abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHashWithoutNonce(), m_currentBlock.nonce, m_currentBlock.difficulty);
cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty);
}
return ret;
@ -923,7 +916,7 @@ void State::completeMine()
// Compile block:
RLPStream ret;
ret.appendList(3);
m_currentBlock.streamRLP(ret, true);
m_currentBlock.streamRLP(ret, WithNonce);
ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
@ -1101,7 +1094,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return false;
}
}
catch (InvalidTrie)
catch (InvalidTrie const&)
{
cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]";
cnote << m_db.keys();
@ -1126,9 +1119,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
auto h = rootHash();
#endif
Manifest ms;
Executive e(*this, &ms);
Executive e(*this, 0);
e.setup(_rlp);
u256 startGasUsed = gasUsed();
@ -1178,148 +1169,11 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
// Add to the user-originated transactions that we've executed.
m_transactions.push_back(e.t());
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs(), ms));
m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()));
m_transactionSet.insert(e.t().sha3());
return e.gasUsed();
}
bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_originAddress)
_originAddress = _senderAddress;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
addBalance(_receiveAddress, _value);
if (o_ms)
{
o_ms->from = _senderAddress;
o_ms->to = _receiveAddress;
o_ms->value = _value;
o_ms->input = _data.toBytes();
}
auto it = !(_codeAddress & ~h160(0xffffffff)) ? c_precompiled.find((unsigned)(u160)_codeAddress) : c_precompiled.end();
if (it != c_precompiled.end())
{
if (*_gas < it->second.gas)
{
*_gas = 0;
return false;
}
*_gas -= it->second.gas;
it->second.exec(_data, _out);
}
else if (addressHasCode(_codeAddress))
{
VM vm(*_gas);
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level);
bool revert = false;
try
{
auto out = vm.go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
if (o_sub)
*o_sub += evm.sub;
if (o_ms)
o_ms->output = out.toBytes();
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
catch (std::exception const& _e)
{
cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
return !revert;
}
return true;
}
h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, SubState* o_sub, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level)
{
if (!_origin)
_origin = _sender;
if (o_ms)
{
o_ms->from = _sender;
o_ms->to = Address();
o_ms->value = _endowment;
o_ms->input = _code.toBytes();
}
Address newAddress = right160(sha3(rlpList(_sender, transactionsFrom(_sender) - 1)));
// Set up new account...
m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception);
// Execute init code.
VM vm(*_gas);
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level);
bool revert = false;
bytesConstRef out;
try
{
out = vm.go(evm, _onOp);
if (o_ms)
o_ms->output = out.toBytes();
if (o_sub)
*o_sub += evm.sub;
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected exception in VM. There may be a bug in this implementation. " << diagnostic_information(_e);
}
catch (std::exception const& _e)
{
// TODO: AUDIT: check that this can never reasonably happen. Consider what to do if it does.
cwarn << "Unexpected std::exception in VM. This is probably unrecoverable. " << _e.what();
}
// TODO: CHECK: AUDIT: IS THIS CORRECT?! (esp. given account created prior to revertion init.)
// Write state out only in the case of a non-out-of-gas transaction.
if (revert)
{
evm.revert();
m_cache.erase(newAddress);
newAddress = Address();
}
else
// Set code.
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
return newAddress;
}
State State::fromPending(unsigned _i) const
{
State ret = *this;

28
libethereum/State.h

@ -36,7 +36,6 @@
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "Executive.h"
#include "AccountDiff.h"
namespace dev
@ -55,8 +54,8 @@ struct StateDetail: public LogChannel { static const char* name() { return "/S/"
struct PrecompiledAddress
{
unsigned gas;
std::function<void(bytesConstRef, bytesRef)> exec;
std::function<bigint(bytesConstRef)> gas;
std::function<bytes(bytesConstRef)> exec;
};
/**
@ -211,15 +210,6 @@ public:
/// Get the list of pending transactions.
Transactions const& pending() const { return m_transactions; }
/// Get the list of pending transactions. TODO: PoC-7: KILL
Manifest changesFromPending(unsigned _i) const { return m_receipts[_i].changes(); }
/// Get the bloom filter of all changes happened in the block. TODO: PoC-7: KILL
h256 oldBloom() const;
/// Get the bloom filter of a particular transaction that happened in the block. TODO: PoC-7: KILL
h256 oldBloom(unsigned _i) const { return m_receipts[_i].changes().bloom(); }
/// Get the transaction receipt for the transaction of the given index.
TransactionReceipt const& receipt(unsigned _i) const { return m_receipts[_i]; }
@ -259,6 +249,9 @@ public:
/// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit);
/// Info on precompiled contract accounts baked into the protocol.
static std::map<unsigned, PrecompiledAddress> const& precompiled() { return c_precompiled; }
private:
/// Undo the changes to the state for committing to mine.
void uncommitToMine();
@ -279,17 +272,6 @@ private:
/// Throws on failure.
u256 enact(bytesConstRef _block, BlockChain const* _bc = nullptr, bool _checkNonce = true);
// Two priviledged entry points for the VM (these don't get added to the Transaction lists):
// We assume all instrinsic fees are paid up before this point.
/// Execute a contract-creation transaction.
h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Execute a call.
/// @a _gas points to the amount of gas to use for the call, and will lower it accordingly.
/// @returns false if the call ran out of gas before completion. true otherwise.
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), SubState* o_sub = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0);
/// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock).
void resetCurrent();

6
libethereum/TransactionReceipt.h

@ -38,9 +38,7 @@ class TransactionReceipt
{
public:
TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); m_stateRoot = (h256)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; for (auto const& i: r[3]) m_log.emplace_back(i); }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log, Manifest const& _ms): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log), m_changes(_ms) {}
Manifest const& changes() const { return m_changes; }
TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {}
h256 const& stateRoot() const { return m_stateRoot; }
u256 const& gasUsed() const { return m_gasUsed; }
@ -62,8 +60,6 @@ private:
u256 m_gasUsed;
LogBloom m_bloom;
LogEntries m_log;
Manifest m_changes; ///< TODO: PoC-7: KILL
};
using TransactionReceipts = std::vector<TransactionReceipt>;

20
libevm/ExtVMFace.h

@ -83,9 +83,19 @@ struct SubState
logs += _s.logs;
return *this;
}
void clear()
{
suicides.clear();
logs.clear();
refunds = 0;
}
};
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, void/*VM*/*, void/*ExtVM*/ const*)>;
class ExtVMFace;
class VM;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>;
/**
* @brief Interface and null implementation of the class for specifying VM externalities.
@ -126,10 +136,10 @@ public:
virtual void suicide(Address) { sub.suicides.insert(myAddress); }
/// Create a new (contract) account.
virtual h160 create(u256, u256*, bytesConstRef, OnOpFunc const&) { return h160(); }
virtual h160 create(u256, u256&, bytesConstRef, OnOpFunc const&) { return h160(); }
/// Make a new message call.
virtual bool call(Address, u256, bytesConstRef, u256*, bytesRef, OnOpFunc const&, Address, Address) { return false; }
virtual bool call(Address, u256, bytesConstRef, u256&, bytesRef, OnOpFunc const&, Address, Address) { return false; }
/// Revert any changes made (by any of the other calls).
virtual void log(h256s&& _topics, bytesConstRef _data) { sub.logs.push_back(LogEntry(myAddress, std::move(_topics), _data.toBytes())); }
@ -146,11 +156,11 @@ public:
u256 value; ///< Value (in Wei) that was passed to this address.
u256 gasPrice; ///< Price of gas (that we already paid).
bytesConstRef data; ///< Current input data.
bytes code; ///< Current code that is executing.
bytes code; ///< Current code that is executing.
BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs).
unsigned depth; ///< Depth of the present call.
unsigned depth = 0; ///< Depth of the present call.
};
}

4
libevm/FeeStructure.cpp

@ -27,12 +27,14 @@ using namespace dev::eth;
u256 const dev::eth::c_stepGas = 1;
u256 const dev::eth::c_balanceGas = 20;
u256 const dev::eth::c_sha3Gas = 20;
u256 const dev::eth::c_sha3Gas = 10;
u256 const dev::eth::c_sha3WordGas = 10;
u256 const dev::eth::c_sloadGas = 20;
u256 const dev::eth::c_sstoreSetGas = 300;
u256 const dev::eth::c_sstoreResetGas = 100;
u256 const dev::eth::c_sstoreRefundGas = 100;
u256 const dev::eth::c_createGas = 100;
u256 const dev::eth::c_createDataGas = 5;
u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_expGas = 1;
u256 const dev::eth::c_expByteGas = 1;

2
libevm/FeeStructure.h

@ -31,11 +31,13 @@ namespace eth
extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL.
extern u256 const c_balanceGas; ///< Once per BALANCE operation.
extern u256 const c_sha3Gas; ///< Once per SHA3 operation.
extern u256 const c_sha3WordGas;
extern u256 const c_sloadGas; ///< Once per SLOAD operation.
extern u256 const c_sstoreSetGas; ///< Once per SSTORE operation if the zeroness changes from zero.
extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeroness doesn't change.
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_createDataGas;
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_expGas; ///< Once per EXP instuction.
extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction.

6
libevm/VM.cpp

@ -20,14 +20,14 @@
*/
#include "VM.h"
#include <libethereum/ExtVM.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 _gas)
void VM::reset(u256 _gas) noexcept
{
m_gas = _gas;
VMFace::reset(_gas);
m_curPC = 0;
m_jumpDests.clear();
}

68
libevm/VM.h

@ -28,21 +28,13 @@
#include <libdevcrypto/SHA3.h>
#include <libethcore/BlockInfo.h>
#include "FeeStructure.h"
#include "ExtVMFace.h"
#include "VMFace.h"
namespace dev
{
namespace eth
{
struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual public VMException {};
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash.
// Currently we just pull out the right (low-order in BE) 160-bits.
inline Address asAddress(u256 _item)
@ -57,28 +49,27 @@ inline u256 fromAddress(Address _a)
/**
*/
class VM
class VM: public VMFace
{
public:
/// Construct VM object.
explicit VM(u256 _gas = 0) { reset(_gas); }
void reset(u256 _gas = 0);
virtual void reset(u256 _gas = 0) noexcept override final;
template <class Ext>
bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1);
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 gas() const { return m_gas; }
u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
private:
u256 m_gas = 0;
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): VMFace(_gas) {}
u256 m_curPC = 0;
bytes m_temp;
u256s m_stack;
@ -86,10 +77,8 @@ private:
std::function<void()> m_onFail;
};
}
// INLINE:
template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps)
// TODO: Move it to cpp file. Not done to make review easier.
inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
@ -164,7 +153,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SLOAD:
require(1);
runGas = c_sloadGas;
runGas = c_sloadGas;
break;
// These all operate on memory and therefore potentially expand it:
@ -186,7 +175,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::SHA3:
require(2);
runGas = c_sha3Gas;
runGas = c_sha3Gas + (m_stack[m_stack.size() - 2] + 31) / 32 * c_sha3WordGas;
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]);
break;
case Instruction::CALLDATACOPY:
@ -412,7 +401,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SDIV:
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) / u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::MOD:
@ -420,7 +409,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SMOD:
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack[m_stack.size() - 2] = m_stack[m_stack.size() - 2] ? s2u(u2s(m_stack.back()) % u2s(m_stack[m_stack.size() - 2])) : 0;
m_stack.pop_back();
break;
case Instruction::EXP:
@ -443,11 +432,11 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
break;
case Instruction::SLT:
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) < u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::SGT:
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack[m_stack.size() - 2] = u2s(m_stack.back()) > u2s(m_stack[m_stack.size() - 2]) ? 1 : 0;
m_stack.pop_back();
break;
case Instruction::EQ:
@ -560,24 +549,27 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back();
unsigned size = (unsigned)m_stack.back();
m_stack.pop_back();
bytes toBeCopied;
unsigned sizeToBeCopied;
switch(inst)
{
case Instruction::CALLDATACOPY:
toBeCopied = _ext.data.toBytes();
sizeToBeCopied = index + (bigint)size > (u256)_ext.data.size() ? (u256)_ext.data.size() < index ? 0 : _ext.data.size() - (unsigned)index : size;
memcpy(m_temp.data() + offset, _ext.data.data() + (unsigned)index, sizeToBeCopied);
break;
case Instruction::CODECOPY:
toBeCopied = _ext.code;
sizeToBeCopied = index + (bigint)size > (u256)_ext.code.size() ? (u256)_ext.code.size() < index ? 0 : _ext.code.size() - (unsigned)index : size;
memcpy(m_temp.data() + offset, _ext.code.data() + (unsigned)index, sizeToBeCopied);
break;
case Instruction::EXTCODECOPY:
toBeCopied = _ext.codeAt(a);
sizeToBeCopied = index + (bigint)size > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < index ? 0 : _ext.codeAt(a).size() - (unsigned)index : size;
memcpy(m_temp.data() + offset, _ext.codeAt(a).data() + (unsigned)index, sizeToBeCopied);
break;
default:
// this is unreachable, but if someone introduces a bug in the future, he may get here.
BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested."));
break;
}
unsigned el = index + (bigint)size > (u256)toBeCopied.size() ? (u256)toBeCopied.size() < index ? 0 : toBeCopied.size() - (unsigned)index : size;
memcpy(m_temp.data() + offset, toBeCopied.data() + (unsigned)index, el);
memset(m_temp.data() + offset + el, 0, size - el);
memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied);
break;
}
case Instruction::GASPRICE:
@ -806,7 +798,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(endowment);
m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
}
else
m_stack.push_back(0);
@ -836,7 +828,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
if (_ext.depth == 1024)
BOOST_THROW_EXCEPTION(OutOfGas());
_ext.subBalance(value);
m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, Address(), receiveAddress));
m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress));
}
else
m_stack.push_back(0);
@ -867,4 +859,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();
}
}
}

55
libevm/VMFace.h

@ -0,0 +1,55 @@
/*
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/>.
*/
#pragma once
#include <memory>
#include <libdevcore/Exceptions.h>
#include "ExtVMFace.h"
namespace dev
{
namespace eth
{
struct VMException: virtual Exception {};
struct StepsDone: virtual VMException {};
struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual VMException {};
/// EVM Virtual Machine interface
class VMFace
{
public:
explicit VMFace(u256 _gas): m_gas(_gas) {}
virtual ~VMFace() = default;
VMFace(VMFace const&) = delete;
void operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; }
u256 gas() const noexcept { return m_gas; }
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
protected:
u256 m_gas = 0;
};
}
}

42
libevm/VMFactory.cpp

@ -0,0 +1,42 @@
/*
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/>.
*/
#include "VMFactory.h"
#include "VM.h"
namespace dev
{
namespace eth
{
namespace
{
VMKind g_kind = VMKind::Interpreter;
}
void VMFactory::setKind(VMKind _kind)
{
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
{
asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now");
return std::unique_ptr<VMFace>(new VM(_gas));
}
}
}

42
libevm/VMFactory.h

@ -0,0 +1,42 @@
/*
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/>.
*/
#pragma once
#include "VMFace.h"
namespace dev
{
namespace eth
{
enum class VMKind: bool
{
Interpreter,
JIT
};
class VMFactory
{
public:
VMFactory() = delete;
static std::unique_ptr<VMFace> create(u256 _gas);
static void setKind(VMKind _kind);
};
}
}

5
liblll/CompilerState.cpp

@ -72,6 +72,11 @@ void CompilerState::populateStandard()
"(def 'regname (name) { [32]'register [64]name (call allgas namereg 0 32 64 0 0) })"
"(def 'regcoin (name) { [32]name (call allgas coinreg 0 32 32 0 0) })"
"(def 'regcoin (name denom) { [32]name [64]denom (call allgas coinreg 0 32 64 0 0) })"
"(def 'ecrecover (r s v hash) { [0] r [32] s [64] v [96] hash (msg allgas 1 0 0 128) })"
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
"(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })"
"}";
CodeFragment::compile(s, *this);
}

2
libp2p/Common.h

@ -57,7 +57,7 @@ class Session;
struct NetWarn: public LogChannel { static const char* name() { return "!N!"; } static const int verbosity = 0; };
struct NetNote: public LogChannel { static const char* name() { return "*N*"; } static const int verbosity = 1; };
struct NetMessageSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 2; };
struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 4; };
struct NetConnect: public LogChannel { static const char* name() { return "+N+"; } static const int verbosity = 10; };
struct NetMessageDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 5; };
struct NetTriviaSummary: public LogChannel { static const char* name() { return "-N-"; } static const int verbosity = 10; };
struct NetTriviaDetail: public LogChannel { static const char* name() { return "=N="; } static const int verbosity = 11; };

526
libp2p/Host.cpp

@ -15,22 +15,11 @@
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Host.cpp
* @authors:
* Gav Wood <i@gavwood.com>
* Eric Lombrozo <elombrozo@gmail.com> (Windows version of populateAddresses())
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include "Host.h"
#include <sys/types.h>
#ifdef _WIN32
// winsock is already included
// #include <winsock.h>
#else
#include <ifaddrs.h>
#endif
#include <set>
#include <chrono>
#include <thread>
@ -43,166 +32,18 @@
#include "Common.h"
#include "Capability.h"
#include "UPnP.h"
#include "Host.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
std::vector<bi::address> Host::getInterfaceAddresses()
{
std::vector<bi::address> addresses;
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
BOOST_THROW_EXCEPTION(NoNetworking());
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
struct hostent* phe = gethostbyname(ac);
if (phe == 0)
{
clog(NetWarn) << "Bad host lookup.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
for (int i = 0; phe->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
char *addrStr = inet_ntoa(addr);
bi::address address(bi::address::from_string(addrStr));
if (!isLocalHostAddress(address))
addresses.push_back(address.to_v4());
}
WSACleanup();
#else
ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1)
BOOST_THROW_EXCEPTION(NoNetworking());
for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0")
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr));
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr);
in6_addr addr = sockaddr->sin6_addr;
boost::asio::ip::address_v6::bytes_type bytes;
memcpy(&bytes[0], addr.s6_addr, 16);
boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id);
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
}
if (ifaddr!=NULL)
freeifaddrs(ifaddr);
#endif
return std::move(addresses);
}
int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort)
{
int retport = -1;
for (unsigned i = 0; i < 2; ++i)
{
// try to connect w/listenPort, else attempt net-allocated port
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort);
try
{
_acceptor->open(endpoint.protocol());
_acceptor->set_option(ba::socket_base::reuse_address(true));
_acceptor->bind(endpoint);
_acceptor->listen();
retport = _acceptor->local_endpoint().port();
break;
}
catch (...)
{
if (i)
{
// both attempts failed
cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information();
}
// first attempt failed
_acceptor->close();
continue;
}
}
return retport;
}
bi::tcp::endpoint Host::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr)
{
asserts(_listenPort != 0);
UPnP* upnp = nullptr;
try
{
upnp = new UPnP;
}
// let m_upnp continue as null - we handle it properly.
catch (NoUPnPDevice) {}
bi::tcp::endpoint upnpep;
if (upnp && upnp->isValid())
{
bi::address paddr;
int extPort = 0;
for (auto const& addr: _ifAddresses)
if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort)))
{
paddr = addr;
break;
}
auto eip = upnp->externalIP();
bi::address eipaddr(bi::address::from_string(eip));
if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr))
{
clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << ".";
clog(NetNote) << "External addr:" << eip;
o_upnpifaddr = paddr;
upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort);
}
else
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place).";
if (upnp)
delete upnp;
}
return upnpep;
}
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start):
Worker("p2p", 0),
m_clientVersion(_clientVersion),
m_netPrefs(_n),
m_ifAddresses(getInterfaceAddresses()),
m_ioService(new ba::io_service(2)),
m_acceptor(new bi::tcp::acceptor(*m_ioService)),
m_socket(new bi::tcp::socket(*m_ioService)),
m_ifAddresses(Network::getInterfaceAddresses()),
m_ioService(2),
m_acceptorV4(m_ioService),
m_key(KeyPair::create())
{
for (auto address: m_ifAddresses)
@ -216,7 +57,7 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool
Host::~Host()
{
quit();
stop();
}
void Host::start()
@ -226,30 +67,78 @@ void Host::start()
void Host::stop()
{
// called to force io_service to kill any remaining tasks it might have -
// such tasks may involve socket reads from Capabilities that maintain references
// to resources we're about to free.
{
// prevent m_run from being set to false at same time as set to true by start()
// Although m_run is set by stop() or start(), it effects m_runTimer so x_runTimer is used instead of a mutex for m_run.
// when m_run == false, run() will cause this::run() to stop() ioservice
Guard l(x_runTimer);
// once m_run is false the scheduler will shutdown network and stopWorking()
// ignore if already stopped/stopping
if (!m_run)
return;
m_run = false;
}
// we know shutdown is complete when m_timer is reset
while (m_timer)
// wait for m_timer to reset (indicating network scheduler has stopped)
while (!!m_timer)
this_thread::sleep_for(chrono::milliseconds(50));
// stop worker thread
stopWorking();
}
void Host::quit()
void Host::doneWorking()
{
// called to force io_service to kill any remaining tasks it might have -
// such tasks may involve socket reads from Capabilities that maintain references
// to resources we're about to free.
if (isWorking())
stop();
m_acceptor.reset();
m_socket.reset();
// reset ioservice (allows manually polling network, below)
m_ioService.reset();
// shutdown acceptor
m_acceptorV4.cancel();
if (m_acceptorV4.is_open())
m_acceptorV4.close();
// There maybe an incoming connection which started but hasn't finished.
// Wait for acceptor to end itself instead of assuming it's complete.
// This helps ensure a peer isn't stopped at the same time it's starting
// and that socket for pending connection is closed.
while (m_accepting)
m_ioService.poll();
// stop capabilities (eth: stops syncing or block/tx broadcast)
for (auto const& h: m_capabilities)
h.second->onStopping();
// disconnect peers
for (unsigned n = 0;; n = 0)
{
{
RecursiveGuard l(x_peers);
for (auto i: m_peers)
if (auto p = i.second.lock())
if (p->isOpen())
{
p->disconnect(ClientQuit);
n++;
}
}
if (!n)
break;
// poll so that peers send out disconnect packets
m_ioService.poll();
}
// stop network (again; helpful to call before subsequent reset())
m_ioService.stop();
// reset network (allows reusing ioservice in future)
m_ioService.reset();
// m_acceptor & m_socket are DANGEROUS now.
// finally, clear out peers (in case they're lingering)
RecursiveGuard l(x_peers);
m_peers.clear();
}
unsigned Host::protocolVersion() const
@ -407,7 +296,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp)
if (_upnp)
{
bi::address upnpifaddr;
bi::tcp::endpoint upnpep = traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr);
bi::tcp::endpoint upnpep = Network::traverseNAT(m_ifAddresses, m_listenPort, upnpifaddr);
if (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified())
{
if (!m_peerAddresses.count(upnpep.address()))
@ -431,18 +320,18 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp)
m_public = bi::tcp::endpoint(bi::address(), m_listenPort);
}
void Host::ensureAccepting()
void Host::runAcceptor()
{
// return if there's no io-server (quit called) or we're not listening
if (!m_ioService || m_listenPort < 1)
return;
if (!m_accepting)
assert(m_listenPort > 0);
if (m_run && !m_accepting)
{
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")";
m_accepting = true;
m_acceptor->async_accept(*m_socket, [=](boost::system::error_code ec)
m_socket.reset(new bi::tcp::socket(m_ioService));
m_acceptorV4.async_accept(*m_socket, [=](boost::system::error_code ec)
{
bool success = false;
if (!ec)
{
try
@ -452,8 +341,9 @@ void Host::ensureAccepting()
} catch (...){}
bi::address remoteAddress = m_socket->remote_endpoint().address();
// Port defaults to 0 - we let the hello tell us which port the peer listens to
auto p = std::make_shared<Session>(this, std::move(*m_socket), bi::tcp::endpoint(remoteAddress, 0));
auto p = std::make_shared<Session>(this, std::move(*m_socket.release()), bi::tcp::endpoint(remoteAddress, 0));
p->start();
success = true;
}
catch (Exception const& _e)
{
@ -464,9 +354,17 @@ void Host::ensureAccepting()
clog(NetWarn) << "ERROR: " << _e.what();
}
}
if (!success && m_socket->is_open())
{
boost::system::error_code ec;
m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket->close();
}
m_accepting = false;
if (ec.value() < 1)
ensureAccepting();
runAcceptor();
});
}
}
@ -480,17 +378,16 @@ string Host::pocHost()
void Host::connect(std::string const& _addr, unsigned short _port) noexcept
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
if (!m_run)
return;
for (int i = 0; i < 2; ++i)
for (auto first: {true, false})
{
try
{
if (i == 0)
if (first)
{
bi::tcp::resolver r(*m_ioService);
bi::tcp::resolver r(m_ioService);
connect(r.resolve({_addr, toString(_port)})->endpoint());
}
else
@ -512,12 +409,11 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept
void Host::connect(bi::tcp::endpoint const& _ep)
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
if (!m_run)
return;
clog(NetConnect) << "Attempting single-shot connection to " << _ep;
bi::tcp::socket* s = new bi::tcp::socket(*m_ioService);
bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_ep, [=](boost::system::error_code const& ec)
{
if (ec)
@ -534,8 +430,7 @@ void Host::connect(bi::tcp::endpoint const& _ep)
void Host::connect(std::shared_ptr<Node> const& _n)
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
if (!m_run)
return;
// prevent concurrently connecting to a node; todo: better abstraction
@ -551,27 +446,32 @@ void Host::connect(std::shared_ptr<Node> const& _n)
_n->lastAttempted = std::chrono::system_clock::now();
_n->failedAttempts++;
m_ready -= _n->index;
bi::tcp::socket* s = new bi::tcp::socket(*m_ioService);
s->async_connect(_n->address, [=](boost::system::error_code const& ec)
{
if (ec)
{
clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")";
_n->lastDisconnect = TCPError;
_n->lastAttempted = std::chrono::system_clock::now();
m_ready += _n->index;
}
else
bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
auto n = node(_n->id);
if (n)
s->async_connect(_n->address, [=](boost::system::error_code const& ec)
{
clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address;
_n->lastConnected = std::chrono::system_clock::now();
auto p = make_shared<Session>(this, std::move(*s), node(_n->id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism.
p->start();
}
delete s;
Guard l(x_pendingNodeConns);
m_pendingNodeConns.erase(nptr);
});
if (ec)
{
clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")";
_n->lastDisconnect = TCPError;
_n->lastAttempted = std::chrono::system_clock::now();
m_ready += _n->index;
}
else
{
clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address;
_n->lastConnected = std::chrono::system_clock::now();
auto p = make_shared<Session>(this, std::move(*s), n, true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism.
p->start();
}
delete s;
Guard l(x_pendingNodeConns);
m_pendingNodeConns.erase(nptr);
});
else
clog(NetWarn) << "Trying to connect to node not in node table.";
}
bool Host::havePeer(NodeId _id) const
@ -680,8 +580,7 @@ void Host::prunePeers()
PeerInfos Host::peers(bool _updatePing) const
{
// if there's no ioService, it means we've had quit() called - bomb out - we're not allowed in here.
if (!m_ioService)
if (!m_run)
return PeerInfos();
RecursiveGuard l(x_peers);
@ -698,152 +597,95 @@ PeerInfos Host::peers(bool _updatePing) const
return ret;
}
void Host::run(boost::system::error_code const& error)
void Host::run(boost::system::error_code const&)
{
m_lastTick += c_timerInterval;
if (error || !m_ioService)
if (!m_run)
{
// timer died or io service went away, so stop here
// stopping io service allows running manual network operations for shutdown
// and also stops blocking worker thread, allowing worker thread to exit
m_ioService.stop();
// resetting timer signals network that nothing else can be scheduled to run
m_timer.reset();
return;
}
// network running
if (m_run)
m_lastTick += c_timerInterval;
if (m_lastTick >= c_timerInterval * 10)
{
if (m_lastTick >= c_timerInterval * 10)
{
growPeers();
prunePeers();
m_lastTick = 0;
}
if (m_hadNewNodes)
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
m_hadNewNodes = false;
}
if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s.
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60))
pp->disconnect(PingTimeout);
pingAll();
}
auto runcb = [this](boost::system::error_code const& error) -> void { run(error); };
m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval));
m_timer->async_wait(runcb);
return;
growPeers();
prunePeers();
m_lastTick = 0;
}
// network stopping
if (!m_run)
if (m_hadNewNodes)
{
// close acceptor
if (m_acceptor->is_open())
{
if (m_accepting)
m_acceptor->cancel();
m_acceptor->close();
m_accepting = false;
}
// stop capabilities (eth: stops syncing or block/tx broadcast)
for (auto const& h: m_capabilities)
h.second->onStopping();
// disconnect peers
for (unsigned n = 0;; n = 0)
{
{
RecursiveGuard l(x_peers);
for (auto i: m_peers)
if (auto p = i.second.lock())
if (p->isOpen())
{
p->disconnect(ClientQuit);
n++;
}
}
if (!n)
break;
this_thread::sleep_for(chrono::milliseconds(100));
}
if (m_socket->is_open())
m_socket->close();
for (auto p: m_peers)
if (auto pp = p.second.lock())
pp->serviceNodesRequest();
// m_run is false, so we're stopping; kill timer
m_lastTick = 0;
// causes parent thread's stop() to continue which calls stopWorking()
m_timer.reset();
// stop ioservice (stops blocking worker thread, allowing thread to join)
if (!!m_ioService)
m_ioService->stop();
return;
m_hadNewNodes = false;
}
if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s.
{
for (auto p: m_peers)
if (auto pp = p.second.lock())
if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60))
pp->disconnect(PingTimeout);
pingAll();
}
auto runcb = [this](boost::system::error_code const& error) -> void { run(error); };
m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval));
m_timer->async_wait(runcb);
}
void Host::startedWorking()
{
if (!m_timer)
asserts(!m_timer);
{
// no timer means this is first run and network must be started
// (run once when host worker thread calls startedWorking())
{
// prevent m_run from being set to true at same time as set to false by stop()
// don't release mutex until m_timer is set so in case stop() is called at same
// time, stop will wait on m_timer and graceful network shutdown.
Guard l(x_runTimer);
// reset io service and create deadline timer
m_timer.reset(new boost::asio::deadline_timer(*m_ioService));
m_run = true;
}
m_ioService->reset();
// try to open acceptor (todo: ipv6)
m_listenPort = listen4(m_acceptor.get(), m_netPrefs.listenPort);
// start capability threads
for (auto const& h: m_capabilities)
h.second->onStarting();
// determine public IP, but only if we're able to listen for connections
// todo: GUI when listen is unavailable in UI
if (m_listenPort)
{
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp);
ensureAccepting();
}
// if m_public address is valid then add us to node list
// todo: abstract empty() and emplace logic
if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id()))
noteNode(id(), m_public, Origin::Perfect, false);
// prevent m_run from being set to true at same time as set to false by stop()
// don't release mutex until m_timer is set so in case stop() is called at same
// time, stop will wait on m_timer and graceful network shutdown.
Guard l(x_runTimer);
// create deadline timer
m_timer.reset(new boost::asio::deadline_timer(m_ioService));
m_run = true;
}
// try to open acceptor (todo: ipv6)
m_listenPort = Network::listen4(m_acceptorV4, m_netPrefs.listenPort);
// start capability threads
for (auto const& h: m_capabilities)
h.second->onStarting();
// determine public IP, but only if we're able to listen for connections
// todo: GUI when listen is unavailable in UI
if (m_listenPort)
{
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp);
clog(NetNote) << "Id:" << id().abridged();
if (m_listenPort > 0)
runAcceptor();
}
// if m_public address is valid then add us to node list
// todo: abstract empty() and emplace logic
if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id()))
noteNode(id(), m_public, Origin::Perfect, false);
clog(NetNote) << "Id:" << id().abridged();
run(boost::system::error_code());
}
void Host::doWork()
{
// no ioService means we've had quit() called - bomb out - we're not allowed in here.
if (asserts(!!m_ioService))
return;
m_ioService->run();
if (m_run)
m_ioService.run();
}
void Host::pingAll()

45
libp2p/Host.h

@ -14,7 +14,8 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.h
/** @file Host.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
@ -34,9 +35,10 @@
#include <libdevcore/RangeMask.h>
#include <libdevcrypto/Common.h>
#include "HostCapability.h"
#include "Network.h"
#include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace bi = ba::ip;
namespace dev
{
@ -101,16 +103,6 @@ struct Node
using Nodes = std::vector<Node>;
struct NetworkPreferences
{
NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {}
unsigned short listenPort = 30303;
std::string publicIP;
bool upnp = true;
bool localNetworking = false;
};
/**
* @brief The Host class
* Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe.
@ -120,17 +112,6 @@ class Host: public Worker
friend class Session;
friend class HostCapabilityFace;
friend struct Node;
/// Static network interface methods
public:
/// @returns public and private interface addresses
static std::vector<bi::address> getInterfaceAddresses();
/// Try to bind and listen on _listenPort, else attempt net-allocated port.
static int listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort);
/// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port.
static bi::tcp::endpoint traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr);
public:
/// Start server, listening for connections on the given port.
@ -187,14 +168,12 @@ public:
void start();
/// Stop network. @threadsafe
/// Resets acceptor, socket, and IO service. Called by deallocator.
void stop();
/// @returns if network is running.
bool isStarted() const { return m_run; }
/// Reset acceptor, socket, and IO service. Called by deallocator. Maybe called by implementation when ordered deallocation is required.
void quit();
NodeId id() const { return m_key.pub(); }
void registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps);
@ -205,7 +184,8 @@ private:
/// Populate m_peerAddresses with available public addresses.
void determinePublic(std::string const& _publicAddress, bool _upnp);
void ensureAccepting();
/// Called only from startedWorking().
void runAcceptor();
void seal(bytes& _b);
@ -214,11 +194,14 @@ private:
/// Called by Worker. Not thread-safe; to be called only by worker.
virtual void startedWorking();
/// Called by startedWorking. Not thread-safe; to be called only be worker callback.
/// Called by startedWorking. Not thread-safe; to be called only be Worker.
void run(boost::system::error_code const& error); ///< Run network. Called serially via ASIO deadline timer. Manages connection state transitions.
/// Run network
/// Run network. Not thread-safe; to be called only by worker.
virtual void doWork();
/// Shutdown network. Not thread-safe; to be called only by worker.
virtual void doneWorking();
std::shared_ptr<Node> noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask<unsigned> const& _known);
@ -235,8 +218,8 @@ private:
int m_listenPort = -1; ///< What port are we listening on. -1 means binding failed or acceptor hasn't been initialized.
std::unique_ptr<ba::io_service> m_ioService; ///< IOService for network stuff.
std::unique_ptr<bi::tcp::acceptor> m_acceptor; ///< Listening acceptor.
ba::io_service m_ioService; ///< IOService for network stuff.
bi::tcp::acceptor m_acceptorV4; ///< Listening acceptor.
std::unique_ptr<bi::tcp::socket> m_socket; ///< Listening socket.
std::unique_ptr<boost::asio::deadline_timer> m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms.

187
libp2p/Network.cpp

@ -0,0 +1,187 @@
/*
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/>.
*/
/** @file Network.cpp
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @author Eric Lombrozo <elombrozo@gmail.com> (Windows version of getInterfaceAddresses())
* @date 2014
*/
#include <sys/types.h>
#ifndef _WIN32
#include <ifaddrs.h>
#endif
#include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libethcore/Exceptions.h>
#include "Common.h"
#include "UPnP.h"
#include "Network.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
std::vector<bi::address> Network::getInterfaceAddresses()
{
std::vector<bi::address> addresses;
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
BOOST_THROW_EXCEPTION(NoNetworking());
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR)
{
clog(NetWarn) << "Error " << WSAGetLastError() << " when getting local host name.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
struct hostent* phe = gethostbyname(ac);
if (phe == 0)
{
clog(NetWarn) << "Bad host lookup.";
WSACleanup();
BOOST_THROW_EXCEPTION(NoNetworking());
}
for (int i = 0; phe->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
char *addrStr = inet_ntoa(addr);
bi::address address(bi::address::from_string(addrStr));
if (!isLocalHostAddress(address))
addresses.push_back(address.to_v4());
}
WSACleanup();
#else
ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1)
BOOST_THROW_EXCEPTION(NoNetworking());
for (auto ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr || string(ifa->ifa_name) == "lo0")
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
in_addr addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
boost::asio::ip::address_v4 address(boost::asio::detail::socket_ops::network_to_host_long(addr.s_addr));
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
sockaddr_in6* sockaddr = ((struct sockaddr_in6 *)ifa->ifa_addr);
in6_addr addr = sockaddr->sin6_addr;
boost::asio::ip::address_v6::bytes_type bytes;
memcpy(&bytes[0], addr.s6_addr, 16);
boost::asio::ip::address_v6 address(bytes, sockaddr->sin6_scope_id);
if (!isLocalHostAddress(address))
addresses.push_back(address);
}
}
if (ifaddr!=NULL)
freeifaddrs(ifaddr);
#endif
return std::move(addresses);
}
int Network::listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort)
{
int retport = -1;
for (unsigned i = 0; i < 2; ++i)
{
// try to connect w/listenPort, else attempt net-allocated port
bi::tcp::endpoint endpoint(bi::tcp::v4(), i ? 0 : _listenPort);
try
{
_acceptor.open(endpoint.protocol());
_acceptor.set_option(ba::socket_base::reuse_address(true));
_acceptor.bind(endpoint);
_acceptor.listen();
retport = _acceptor.local_endpoint().port();
break;
}
catch (...)
{
if (i)
{
// both attempts failed
cwarn << "Couldn't start accepting connections on host. Something very wrong with network?\n" << boost::current_exception_diagnostic_information();
}
// first attempt failed
_acceptor.close();
continue;
}
}
return retport;
}
bi::tcp::endpoint Network::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr)
{
asserts(_listenPort != 0);
UPnP* upnp = nullptr;
try
{
upnp = new UPnP;
}
// let m_upnp continue as null - we handle it properly.
catch (NoUPnPDevice) {}
bi::tcp::endpoint upnpep;
if (upnp && upnp->isValid())
{
bi::address paddr;
int extPort = 0;
for (auto const& addr: _ifAddresses)
if (addr.is_v4() && isPrivateAddress(addr) && (extPort = upnp->addRedirect(addr.to_string().c_str(), _listenPort)))
{
paddr = addr;
break;
}
auto eip = upnp->externalIP();
bi::address eipaddr(bi::address::from_string(eip));
if (extPort && eip != string("0.0.0.0") && !isPrivateAddress(eipaddr))
{
clog(NetNote) << "Punched through NAT and mapped local port" << _listenPort << "onto external port" << extPort << ".";
clog(NetNote) << "External addr:" << eip;
o_upnpifaddr = paddr;
upnpep = bi::tcp::endpoint(eipaddr, (unsigned short)extPort);
}
else
clog(NetWarn) << "Couldn't punch through NAT (or no NAT in place).";
if (upnp)
delete upnp;
}
return upnpep;
}

63
libp2p/Network.h

@ -0,0 +1,63 @@
/*
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/>.
*/
/** @file Network.h
* @author Alex Leverington <nessence@gmail.com>
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <vector>
namespace ba = boost::asio;
namespace bi = ba::ip;
namespace dev
{
namespace p2p
{
struct NetworkPreferences
{
NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {}
unsigned short listenPort = 30303;
std::string publicIP;
bool upnp = true;
bool localNetworking = false;
};
/**
* @brief Network Class
* Static network operations and interface(s).
*/
class Network
{
public:
/// @returns public and private interface addresses
static std::vector<bi::address> getInterfaceAddresses();
/// Try to bind and listen on _listenPort, else attempt net-allocated port.
static int listen4(bi::tcp::acceptor& _acceptor, unsigned short _listenPort);
/// Return public endpoint of upnp interface. If successful o_upnpifaddr will be a private interface address and endpoint will contain public address and port.
static bi::tcp::endpoint traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr);
};
}
}

6
libp2p/Session.cpp

@ -75,7 +75,11 @@ Session::~Session()
try
{
if (m_socket.is_open())
{
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close();
}
}
catch (...){}
}
@ -479,6 +483,8 @@ void Session::drop(DisconnectReason _reason)
try
{
clogS(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")";
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close();
}
catch (...) {}

282
libsolidity/AST.cpp

@ -25,6 +25,7 @@
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h>
using namespace std;
@ -33,38 +34,34 @@ namespace dev
namespace solidity
{
void ContractDefinition::accept(ASTVisitor& _visitor)
TypeError ASTNode::createTypeError(string const& _description) const
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
void StructDefinition::accept(ASTVisitor& _visitor)
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
if (_visitor.visit(*this))
listAccept(m_members, _visitor);
_visitor.endVisit(*this);
}
vector<FunctionDefinition const*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
exportedFunctions.push_back(f.get());
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
{
return _a->getName().compare(_b->getName()) < 0;
};
void StructDefinition::checkValidityOfMembers()
{
checkMemberTypes();
checkRecursion();
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}
void StructDefinition::checkMemberTypes()
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
}
void StructDefinition::checkRecursion()
void StructDefinition::checkRecursion() const
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this};
@ -79,239 +76,12 @@ void StructDefinition::checkRecursion()
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName());
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
}
}
}
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_parameters, _visitor);
_visitor.endVisit(*this);
}
void FunctionDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void UserDefinedTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Mapping::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_keyType->accept(_visitor);
m_valueType->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Statement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_statements, _visitor);
_visitor.endVisit(*this);
}
void IfStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_trueBody->accept(_visitor);
if (m_falseBody)
m_falseBody->accept(_visitor);
}
_visitor.endVisit(*this);
}
void BreakableStatement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void WhileStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Continue::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Break::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Return::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void ExpressionStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Assignment::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_leftHandSide->accept(_visitor);
m_rightHandSide->accept(_visitor);
}
_visitor.endVisit(*this);
}
void UnaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_subExpression->accept(_visitor);
_visitor.endVisit(*this);
}
void BinaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_left->accept(_visitor);
m_right->accept(_visitor);
}
_visitor.endVisit(*this);
}
void FunctionCall::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_expression->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Identifier::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Literal::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
TypeError ASTNode::createTypeError(string const& _description) const
{
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
vector<FunctionDefinition const*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
exportedFunctions.push_back(f.get());
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
{
return _a->getName().compare(_b->getName()) < 0;
};
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}
void FunctionDefinition::checkTypeRequirements()
{
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
@ -519,7 +289,7 @@ void Identifier::checkTypeRequirements()
if (asserts(m_referencedDeclaration))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved."));
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration);
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
if (variable)
{
if (!variable->getType())
@ -529,29 +299,29 @@ void Identifier::checkTypeRequirements()
return;
}
//@todo can we unify these with TypeName::toType()?
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration);
StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
if (structDef)
{
// note that we do not have a struct type here
m_type = make_shared<TypeType>(make_shared<StructType>(*structDef));
m_type = make_shared<TypeType const>(make_shared<StructType const>(*structDef));
return;
}
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration);
FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
if (functionDef)
{
// a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
m_type = make_shared<FunctionType const>(*functionDef);
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef)
{
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
return;
}
MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration);
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
if (magicVariable)
{
m_type = magicVariable->getType();
@ -562,7 +332,7 @@ void Identifier::checkTypeRequirements()
void ElementaryTypeNameExpression::checkTypeRequirements()
{
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
m_type = make_shared<TypeType const>(Type::fromElementaryTypeName(m_typeToken));
}
void Literal::checkTypeRequirements()

171
libsolidity/AST.h

@ -39,6 +39,7 @@ namespace solidity
{
class ASTVisitor;
class ASTConstVisitor;
/**
@ -54,12 +55,19 @@ public:
virtual ~ASTNode() {}
virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0;
template <class T>
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor)
{
for (ASTPointer<T>& element: _list)
element->accept(_visitor);
}
template <class T>
static void listAccept(std::vector<ASTPointer<T>> const& _list, ASTConstVisitor& _visitor)
{
for (ASTPointer<T> const& element: _list)
element->accept(_visitor);
}
/// Returns the source code location of this node.
Location const& getLocation() const { return m_location; }
@ -79,6 +87,44 @@ private:
Location m_location;
};
/**
* Source unit containing import directives and contract definitions.
*/
class SourceUnit: public ASTNode
{
public:
SourceUnit(Location const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_location), m_nodes(_nodes) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<ASTNode>> getNodes() const { return m_nodes; }
private:
std::vector<ASTPointer<ASTNode>> m_nodes;
};
/**
* Import directive for referencing other files / source objects.
* Example: import "abc.sol"
* Source objects are identified by a string which can be a file name but does not have to be.
*/
class ImportDirective: public ASTNode
{
public:
ImportDirective(Location const& _location, ASTPointer<ASTString> const& _identifier):
ASTNode(_location), m_identifier(_identifier) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& getIdentifier() const { return *m_identifier; }
private:
ASTPointer<ASTString> m_identifier;
};
/**
* Abstract AST class for a declaration (contract, function, struct, variable).
*/
@ -86,18 +132,18 @@ class Declaration: public ASTNode
{
public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {}
ASTNode(_location), m_name(_name), m_scope(nullptr) {}
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; }
Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
private:
ASTPointer<ASTString> m_name;
Declaration* m_scope;
Declaration const* m_scope;
};
/**
@ -110,21 +156,28 @@ class ContractDefinition: public Declaration
public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
@ -132,6 +185,7 @@ private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
};
class StructDefinition: public Declaration
@ -142,16 +196,17 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _members):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers();
void checkValidityOfMembers() const;
private:
void checkMemberTypes();
void checkRecursion();
void checkMemberTypes() const;
void checkRecursion() const;
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
@ -168,6 +223,7 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _parameters):
ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; }
@ -194,17 +250,18 @@ public:
{}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList& getParameterList() { return *m_parameters; }
ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; }
Block const& getBody() const { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() { return m_documentation; }
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
@ -234,15 +291,16 @@ public:
ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
TypeName* getTypeName() const { return m_typeName.get(); }
TypeName const* getTypeName() const { return m_typeName.get(); }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition*>(getScope()); }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -261,6 +319,8 @@ public:
Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; }
@ -279,11 +339,12 @@ class TypeName: public ASTNode
public:
explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type> toType() const = 0;
virtual std::shared_ptr<Type const> toType() const = 0;
};
/**
@ -299,7 +360,8 @@ public:
if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromElementaryTypeName(m_type); }
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; }
@ -314,18 +376,19 @@ class UserDefinedTypeName: public TypeName
{
public:
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name):
TypeName(_location), m_name(_name) {}
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromUserDefinedTypeName(*this); }
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
Declaration* m_referencedDeclaration;
Declaration const* m_referencedDeclaration;
};
/**
@ -338,7 +401,8 @@ public:
ASTPointer<TypeName> const& _valueType):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromMapping(*this); }
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); }
ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; }
@ -361,7 +425,6 @@ class Statement: public ASTNode
{
public:
explicit Statement(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
/// Check all type requirements, throws exception if some requirement is not met.
/// This includes checking that operators are applicable to their arguments but also that
@ -378,6 +441,7 @@ public:
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements):
Statement(_location), m_statements(_statements) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
@ -397,12 +461,13 @@ public:
Statement(_location),
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; }
Statement& getTrueStatement() const { return *m_trueBody; }
Expression const& getCondition() const { return *m_condition; }
Statement const& getTrueStatement() const { return *m_trueBody; }
/// @returns the "else" part of the if statement or nullptr if there is no "else" part.
Statement* getFalseStatement() const { return m_falseBody.get(); }
Statement const* getFalseStatement() const { return m_falseBody.get(); }
private:
ASTPointer<Expression> m_condition;
@ -411,13 +476,12 @@ private:
};
/**
* Statement in which a break statement is legal.
* Statement in which a break statement is legal (abstract class).
*/
class BreakableStatement: public Statement
{
public:
BreakableStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
};
class WhileStatement: public BreakableStatement
@ -427,10 +491,11 @@ public:
ASTPointer<Statement> const& _body):
BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; }
Statement& getBody() const { return *m_body; }
Expression const& getCondition() const { return *m_condition; }
Statement const& getBody() const { return *m_body; }
private:
ASTPointer<Expression> m_condition;
@ -442,6 +507,7 @@ class Continue: public Statement
public:
Continue(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {}
};
@ -450,6 +516,7 @@ class Break: public Statement
public:
Break(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {}
};
@ -457,8 +524,9 @@ class Return: public Statement
{
public:
Return(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {}
Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
@ -468,7 +536,7 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError());
return *m_returnParameters;
}
Expression* getExpression() const { return m_expression.get(); }
Expression const* getExpression() const { return m_expression.get(); }
private:
ASTPointer<Expression> m_expression; ///< value to return, optional
@ -489,10 +557,11 @@ public:
ASTPointer<Expression> _value):
Statement(_location), m_variable(_variable), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; }
Expression* getExpression() const { return m_value.get(); }
Expression const* getExpression() const { return m_value.get(); }
private:
ASTPointer<VariableDeclaration> m_variable;
@ -508,9 +577,10 @@ public:
ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; }
Expression const& getExpression() const { return *m_expression; }
private:
ASTPointer<Expression> m_expression;
@ -572,11 +642,12 @@ public:
if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getLeftHandSide() const { return *m_leftHandSide; }
Expression const& getLeftHandSide() const { return *m_leftHandSide; }
Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
Expression& getRightHandSide() const { return *m_rightHandSide; }
Expression const& getRightHandSide() const { return *m_rightHandSide; }
private:
ASTPointer<Expression> m_leftHandSide;
@ -599,11 +670,12 @@ public:
if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
Expression& getSubExpression() const { return *m_subExpression; }
Expression const& getSubExpression() const { return *m_subExpression; }
private:
Token::Value m_operator;
@ -625,10 +697,11 @@ public:
if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getLeftExpression() const { return *m_left; }
Expression& getRightExpression() const { return *m_right; }
Expression const& getLeftExpression() const { return *m_left; }
Expression const& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; }
Type const& getCommonType() const { return *m_commonType; }
@ -652,10 +725,11 @@ public:
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
@ -676,7 +750,8 @@ public:
ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override;
Expression& getExpression() const { return *m_expression; }
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& getExpression() const { return *m_expression; }
ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override;
@ -695,10 +770,11 @@ public:
ASTPointer<Expression> const& _index):
Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Expression& getBaseExpression() const { return *m_base; }
Expression& getIndexExpression() const { return *m_index; }
Expression const& getBaseExpression() const { return *m_base; }
Expression const& getIndexExpression() const { return *m_index; }
private:
ASTPointer<Expression> m_base;
@ -722,20 +798,21 @@ class Identifier: public PrimaryExpression
{
public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name) {}
PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
/// Declaration the name refers to.
Declaration* m_referencedDeclaration;
Declaration const* m_referencedDeclaration;
};
/**
@ -752,6 +829,7 @@ public:
if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError());
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Token::Value getTypeToken() const { return m_typeToken; }
@ -769,6 +847,7 @@ public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; }

2
libsolidity/ASTForward.h

@ -34,6 +34,8 @@ namespace solidity
{
class ASTNode;
class SourceUnit;
class ImportDirective;
class Declaration;
class ContractDefinition;
class StructDefinition;

131
libsolidity/ASTPrinter.cpp

@ -30,7 +30,7 @@ namespace dev
namespace solidity
{
ASTPrinter::ASTPrinter(ASTNode& _ast, string const& _source):
ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(&_ast)
{
}
@ -43,28 +43,35 @@ void ASTPrinter::print(ostream& _stream)
}
bool ASTPrinter::visit(ContractDefinition& _node)
bool ASTPrinter::visit(ImportDirective const& _node)
{
writeLine("ImportDirective \"" + _node.getIdentifier() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ContractDefinition const& _node)
{
writeLine("ContractDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(StructDefinition& _node)
bool ASTPrinter::visit(StructDefinition const& _node)
{
writeLine("StructDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ParameterList& _node)
bool ASTPrinter::visit(ParameterList const& _node)
{
writeLine("ParameterList");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(FunctionDefinition& _node)
bool ASTPrinter::visit(FunctionDefinition const& _node)
{
writeLine("FunctionDefinition \"" + _node.getName() + "\"" +
(_node.isPublic() ? " - public" : "") +
@ -73,112 +80,112 @@ bool ASTPrinter::visit(FunctionDefinition& _node)
return goDeeper();
}
bool ASTPrinter::visit(VariableDeclaration& _node)
bool ASTPrinter::visit(VariableDeclaration const& _node)
{
writeLine("VariableDeclaration \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(TypeName& _node)
bool ASTPrinter::visit(TypeName const& _node)
{
writeLine("TypeName");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ElementaryTypeName& _node)
bool ASTPrinter::visit(ElementaryTypeName const& _node)
{
writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(UserDefinedTypeName& _node)
bool ASTPrinter::visit(UserDefinedTypeName const& _node)
{
writeLine("UserDefinedTypeName \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Mapping& _node)
bool ASTPrinter::visit(Mapping const& _node)
{
writeLine("Mapping");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Statement& _node)
bool ASTPrinter::visit(Statement const& _node)
{
writeLine("Statement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Block& _node)
bool ASTPrinter::visit(Block const& _node)
{
writeLine("Block");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(IfStatement& _node)
bool ASTPrinter::visit(IfStatement const& _node)
{
writeLine("IfStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(BreakableStatement& _node)
bool ASTPrinter::visit(BreakableStatement const& _node)
{
writeLine("BreakableStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(WhileStatement& _node)
bool ASTPrinter::visit(WhileStatement const& _node)
{
writeLine("WhileStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Continue& _node)
bool ASTPrinter::visit(Continue const& _node)
{
writeLine("Continue");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Break& _node)
bool ASTPrinter::visit(Break const& _node)
{
writeLine("Break");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Return& _node)
bool ASTPrinter::visit(Return const& _node)
{
writeLine("Return");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(VariableDefinition& _node)
bool ASTPrinter::visit(VariableDefinition const& _node)
{
writeLine("VariableDefinition");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ExpressionStatement& _node)
bool ASTPrinter::visit(ExpressionStatement const& _node)
{
writeLine("ExpressionStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Expression& _node)
bool ASTPrinter::visit(Expression const& _node)
{
writeLine("Expression");
printType(_node);
@ -186,7 +193,7 @@ bool ASTPrinter::visit(Expression& _node)
return goDeeper();
}
bool ASTPrinter::visit(Assignment& _node)
bool ASTPrinter::visit(Assignment const& _node)
{
writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node);
@ -194,7 +201,7 @@ bool ASTPrinter::visit(Assignment& _node)
return goDeeper();
}
bool ASTPrinter::visit(UnaryOperation& _node)
bool ASTPrinter::visit(UnaryOperation const& _node)
{
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator()));
@ -203,7 +210,7 @@ bool ASTPrinter::visit(UnaryOperation& _node)
return goDeeper();
}
bool ASTPrinter::visit(BinaryOperation& _node)
bool ASTPrinter::visit(BinaryOperation const& _node)
{
writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node);
@ -211,7 +218,7 @@ bool ASTPrinter::visit(BinaryOperation& _node)
return goDeeper();
}
bool ASTPrinter::visit(FunctionCall& _node)
bool ASTPrinter::visit(FunctionCall const& _node)
{
writeLine("FunctionCall");
printType(_node);
@ -219,7 +226,7 @@ bool ASTPrinter::visit(FunctionCall& _node)
return goDeeper();
}
bool ASTPrinter::visit(MemberAccess& _node)
bool ASTPrinter::visit(MemberAccess const& _node)
{
writeLine("MemberAccess to member " + _node.getMemberName());
printType(_node);
@ -227,7 +234,7 @@ bool ASTPrinter::visit(MemberAccess& _node)
return goDeeper();
}
bool ASTPrinter::visit(IndexAccess& _node)
bool ASTPrinter::visit(IndexAccess const& _node)
{
writeLine("IndexAccess");
printType(_node);
@ -235,7 +242,7 @@ bool ASTPrinter::visit(IndexAccess& _node)
return goDeeper();
}
bool ASTPrinter::visit(PrimaryExpression& _node)
bool ASTPrinter::visit(PrimaryExpression const& _node)
{
writeLine("PrimaryExpression");
printType(_node);
@ -243,7 +250,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
return goDeeper();
}
bool ASTPrinter::visit(Identifier& _node)
bool ASTPrinter::visit(Identifier const& _node)
{
writeLine(string("Identifier ") + _node.getName());
printType(_node);
@ -251,7 +258,7 @@ bool ASTPrinter::visit(Identifier& _node)
return goDeeper();
}
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
{
writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node);
@ -259,7 +266,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
return goDeeper();
}
bool ASTPrinter::visit(Literal& _node)
bool ASTPrinter::visit(Literal const& _node)
{
char const* tokenString = Token::toString(_node.getToken());
if (!tokenString)
@ -270,157 +277,157 @@ bool ASTPrinter::visit(Literal& _node)
return goDeeper();
}
void ASTPrinter::endVisit(ASTNode&)
void ASTPrinter::endVisit(ImportDirective const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ContractDefinition&)
void ASTPrinter::endVisit(ContractDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(StructDefinition&)
void ASTPrinter::endVisit(StructDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ParameterList&)
void ASTPrinter::endVisit(ParameterList const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(FunctionDefinition&)
void ASTPrinter::endVisit(FunctionDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(VariableDeclaration&)
void ASTPrinter::endVisit(VariableDeclaration const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(TypeName&)
void ASTPrinter::endVisit(TypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ElementaryTypeName&)
void ASTPrinter::endVisit(ElementaryTypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(UserDefinedTypeName&)
void ASTPrinter::endVisit(UserDefinedTypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Mapping&)
void ASTPrinter::endVisit(Mapping const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Statement&)
void ASTPrinter::endVisit(Statement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Block&)
void ASTPrinter::endVisit(Block const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IfStatement&)
void ASTPrinter::endVisit(IfStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(BreakableStatement&)
void ASTPrinter::endVisit(BreakableStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(WhileStatement&)
void ASTPrinter::endVisit(WhileStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Continue&)
void ASTPrinter::endVisit(Continue const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Break&)
void ASTPrinter::endVisit(Break const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Return&)
void ASTPrinter::endVisit(Return const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(VariableDefinition&)
void ASTPrinter::endVisit(VariableDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ExpressionStatement&)
void ASTPrinter::endVisit(ExpressionStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Expression&)
void ASTPrinter::endVisit(Expression const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Assignment&)
void ASTPrinter::endVisit(Assignment const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(UnaryOperation&)
void ASTPrinter::endVisit(UnaryOperation const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(BinaryOperation&)
void ASTPrinter::endVisit(BinaryOperation const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(FunctionCall&)
void ASTPrinter::endVisit(FunctionCall const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(MemberAccess&)
void ASTPrinter::endVisit(MemberAccess const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IndexAccess&)
void ASTPrinter::endVisit(IndexAccess const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(PrimaryExpression&)
void ASTPrinter::endVisit(PrimaryExpression const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Identifier&)
void ASTPrinter::endVisit(Identifier const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ElementaryTypeNameExpression&)
void ASTPrinter::endVisit(ElementaryTypeNameExpression const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Literal&)
void ASTPrinter::endVisit(Literal const&)
{
m_indentation--;
}

129
libsolidity/ASTPrinter.h

@ -33,77 +33,78 @@ namespace solidity
/**
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
*/
class ASTPrinter: public ASTVisitor
class ASTPrinter: public ASTConstVisitor
{
public:
/// Create a printer for the given abstract syntax tree. If the source is specified,
/// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTNode& _ast, std::string const& _source = std::string());
ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string());
/// Output the string representation of the AST to _stream.
void print(std::ostream& _stream);
bool visit(ContractDefinition& _node) override;
bool visit(StructDefinition& _node) override;
bool visit(ParameterList& _node) override;
bool visit(FunctionDefinition& _node) override;
bool visit(VariableDeclaration& _node) override;
bool visit(TypeName& _node) override;
bool visit(ElementaryTypeName& _node) override;
bool visit(UserDefinedTypeName& _node) override;
bool visit(Mapping& _node) override;
bool visit(Statement& _node) override;
bool visit(Block& _node) override;
bool visit(IfStatement& _node) override;
bool visit(BreakableStatement& _node) override;
bool visit(WhileStatement& _node) override;
bool visit(Continue& _node) override;
bool visit(Break& _node) override;
bool visit(Return& _node) override;
bool visit(VariableDefinition& _node) override;
bool visit(ExpressionStatement& _node) override;
bool visit(Expression& _node) override;
bool visit(Assignment& _node) override;
bool visit(UnaryOperation& _node) override;
bool visit(BinaryOperation& _node) override;
bool visit(FunctionCall& _node) override;
bool visit(MemberAccess& _node) override;
bool visit(IndexAccess& _node) override;
bool visit(PrimaryExpression& _node) override;
bool visit(Identifier& _node) override;
bool visit(ElementaryTypeNameExpression& _node) override;
bool visit(Literal& _node) override;
bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override;
bool visit(StructDefinition const& _node) override;
bool visit(ParameterList const& _node) override;
bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration const& _node) override;
bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override;
bool visit(Statement const& _node) override;
bool visit(Block const& _node) override;
bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override;
bool visit(Continue const& _node) override;
bool visit(Break const& _node) override;
bool visit(Return const& _node) override;
bool visit(VariableDefinition const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Expression const& _node) override;
bool visit(Assignment const& _node) override;
bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override;
bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess const& _node) override;
bool visit(PrimaryExpression const& _node) override;
bool visit(Identifier const& _node) override;
bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
void endVisit(ASTNode& _node) override;
void endVisit(ContractDefinition&) override;
void endVisit(StructDefinition&) override;
void endVisit(ParameterList&) override;
void endVisit(FunctionDefinition&) override;
void endVisit(VariableDeclaration&) override;
void endVisit(TypeName&) override;
void endVisit(ElementaryTypeName&) override;
void endVisit(UserDefinedTypeName&) override;
void endVisit(Mapping&) override;
void endVisit(Statement&) override;
void endVisit(Block&) override;
void endVisit(IfStatement&) override;
void endVisit(BreakableStatement&) override;
void endVisit(WhileStatement&) override;
void endVisit(Continue&) override;
void endVisit(Break&) override;
void endVisit(Return&) override;
void endVisit(VariableDefinition&) override;
void endVisit(ExpressionStatement&) override;
void endVisit(Expression&) override;
void endVisit(Assignment&) override;
void endVisit(UnaryOperation&) override;
void endVisit(BinaryOperation&) override;
void endVisit(FunctionCall&) override;
void endVisit(MemberAccess&) override;
void endVisit(IndexAccess&) override;
void endVisit(PrimaryExpression&) override;
void endVisit(Identifier&) override;
void endVisit(ElementaryTypeNameExpression&) override;
void endVisit(Literal&) override;
void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition const&) override;
void endVisit(StructDefinition const&) override;
void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration const&) override;
void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override;
void endVisit(Statement const&) override;
void endVisit(Block const&) override;
void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override;
void endVisit(Continue const&) override;
void endVisit(Break const&) override;
void endVisit(Return const&) override;
void endVisit(VariableDefinition const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Expression const&) override;
void endVisit(Assignment const&) override;
void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override;
void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression const&) override;
void endVisit(Identifier const&) override;
void endVisit(ElementaryTypeNameExpression const&) override;
void endVisit(Literal const&) override;
private:
void printSourcePart(ASTNode const& _node);
@ -114,7 +115,7 @@ private:
int m_indentation;
std::string m_source;
ASTNode* m_ast;
ASTNode const* m_ast;
std::ostream* m_ostream;
};

76
libsolidity/ASTVisitor.h

@ -42,6 +42,8 @@ class ASTVisitor
{
public:
virtual bool visit(ASTNode&) { return true; }
virtual bool visit(SourceUnit&) { return true; }
virtual bool visit(ImportDirective&) { return true; }
virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; }
virtual bool visit(ParameterList&) { return true; }
@ -74,6 +76,8 @@ public:
virtual bool visit(Literal&) { return true; }
virtual void endVisit(ASTNode&) { }
virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(StructDefinition&) { }
virtual void endVisit(ParameterList&) { }
@ -106,5 +110,77 @@ public:
virtual void endVisit(Literal&) { }
};
class ASTConstVisitor
{
public:
virtual bool visit(ASTNode const&) { return true; }
virtual bool visit(SourceUnit const&) { return true; }
virtual bool visit(ImportDirective const&) { return true; }
virtual bool visit(ContractDefinition const&) { return true; }
virtual bool visit(StructDefinition const&) { return true; }
virtual bool visit(ParameterList const&) { return true; }
virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; }
virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; }
virtual bool visit(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; }
virtual bool visit(Continue const&) { return true; }
virtual bool visit(Break const&) { return true; }
virtual bool visit(Return const&) { return true; }
virtual bool visit(VariableDefinition const&) { return true; }
virtual bool visit(ExpressionStatement const&) { return true; }
virtual bool visit(Expression const&) { return true; }
virtual bool visit(Assignment const&) { return true; }
virtual bool visit(UnaryOperation const&) { return true; }
virtual bool visit(BinaryOperation const&) { return true; }
virtual bool visit(FunctionCall const&) { return true; }
virtual bool visit(MemberAccess const&) { return true; }
virtual bool visit(IndexAccess const&) { return true; }
virtual bool visit(PrimaryExpression const&) { return true; }
virtual bool visit(Identifier const&) { return true; }
virtual bool visit(ElementaryTypeNameExpression const&) { return true; }
virtual bool visit(Literal const&) { return true; }
virtual void endVisit(ASTNode const&) { }
virtual void endVisit(SourceUnit const&) { }
virtual void endVisit(ImportDirective const&) { }
virtual void endVisit(ContractDefinition const&) { }
virtual void endVisit(StructDefinition const&) { }
virtual void endVisit(ParameterList const&) { }
virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { }
virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { }
virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { }
virtual void endVisit(Continue const&) { }
virtual void endVisit(Break const&) { }
virtual void endVisit(Return const&) { }
virtual void endVisit(VariableDefinition const&) { }
virtual void endVisit(ExpressionStatement const&) { }
virtual void endVisit(Expression const&) { }
virtual void endVisit(Assignment const&) { }
virtual void endVisit(UnaryOperation const&) { }
virtual void endVisit(BinaryOperation const&) { }
virtual void endVisit(FunctionCall const&) { }
virtual void endVisit(MemberAccess const&) { }
virtual void endVisit(IndexAccess const&) { }
virtual void endVisit(PrimaryExpression const&) { }
virtual void endVisit(Identifier const&) { }
virtual void endVisit(ElementaryTypeNameExpression const&) { }
virtual void endVisit(Literal const&) { }
};
}
}

493
libsolidity/AST_accept.h

@ -0,0 +1,493 @@
/*
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
* Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that
* file with these mechanical implementations.
*/
#pragma once
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
namespace dev
{
namespace solidity
{
void SourceUnit::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_nodes, _visitor);
_visitor.endVisit(*this);
}
void SourceUnit::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
listAccept(m_nodes, _visitor);
_visitor.endVisit(*this);
}
void ImportDirective::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ImportDirective::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ContractDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_members, _visitor);
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
listAccept(m_members, _visitor);
_visitor.endVisit(*this);
}
void StructDefinition::checkValidityOfMembers() const
{
checkMemberTypes();
checkRecursion();
}
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_parameters, _visitor);
_visitor.endVisit(*this);
}
void ParameterList::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
listAccept(m_parameters, _visitor);
_visitor.endVisit(*this);
}
void FunctionDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void UserDefinedTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Mapping::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_keyType->accept(_visitor);
m_valueType->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Mapping::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_keyType->accept(_visitor);
m_valueType->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_statements, _visitor);
_visitor.endVisit(*this);
}
void Block::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
listAccept(m_statements, _visitor);
_visitor.endVisit(*this);
}
void IfStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_trueBody->accept(_visitor);
if (m_falseBody)
m_falseBody->accept(_visitor);
}
_visitor.endVisit(*this);
}
void IfStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_trueBody->accept(_visitor);
if (m_falseBody)
m_falseBody->accept(_visitor);
}
_visitor.endVisit(*this);
}
void WhileStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void WhileStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Continue::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Continue::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Break::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Break::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Return::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void Return::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void ExpressionStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Assignment::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_leftHandSide->accept(_visitor);
m_rightHandSide->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Assignment::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_leftHandSide->accept(_visitor);
m_rightHandSide->accept(_visitor);
}
_visitor.endVisit(*this);
}
void UnaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_subExpression->accept(_visitor);
_visitor.endVisit(*this);
}
void UnaryOperation::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_subExpression->accept(_visitor);
_visitor.endVisit(*this);
}
void BinaryOperation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_left->accept(_visitor);
m_right->accept(_visitor);
}
_visitor.endVisit(*this);
}
void BinaryOperation::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_left->accept(_visitor);
m_right->accept(_visitor);
}
_visitor.endVisit(*this);
}
void FunctionCall::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_expression->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void FunctionCall::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_expression->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Identifier::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Identifier::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Literal::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Literal::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
}
}

10
libsolidity/BaseTypes.h

@ -22,6 +22,8 @@
#pragma once
#include <memory>
#include <string>
#include <ostream>
namespace dev
@ -35,19 +37,19 @@ namespace solidity
*/
struct Location
{
Location(int _start, int _end): start(_start), end(_end) { }
Location(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { }
bool IsValid() const { return start >= 0 && end >= start; }
int start;
int end;
std::shared_ptr<std::string const> sourceName;
};
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{
return _out << "[" << _location.start << "," << _location.end << ")";
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
}
}

4
libsolidity/CMakeLists.txt

@ -16,6 +16,10 @@ endif()
include_directories(..)
target_link_libraries(${EXECUTABLE} evmcore devcore)
# TODO: Temporary until PR 532 https://github.com/ethereum/cpp-ethereum/pull/532
# gets accepted. Then we can simply add jsoncpp as a dependency and not the
# whole of JSONRPC as we are doing right here
target_link_libraries(${EXECUTABLE} ${JSONRPC_LS})
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

99
libsolidity/Compiler.cpp

@ -26,13 +26,14 @@
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
using namespace std;
namespace dev {
namespace solidity {
void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
m_context = CompilerContext(); // clear it just in case
@ -108,8 +109,8 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
callDataUnpackerEntryPoints.push_back(m_context.newTag());
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
//@todo avoid the last ADD (or remove it in the optimizer)
if (funid < interfaceFunctions.size() - 1)
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
}
m_context << eth::Instruction::STOP; // function not found
@ -129,21 +130,17 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0)
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
if (numBytes == 32)
m_context << u256(dataOffset) << load;
else
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
<< load << eth::Instruction::DIV;
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
dataOffset += numBytes;
}
return dataOffset;
@ -154,18 +151,19 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
//@todo this can be also done more efficiently
unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i)
{
Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0)
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
m_context << eth::dupInstruction(parameters.size() - i);
if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes;
}
// note that the stack is not cleaned up here
@ -179,7 +177,7 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable);
}
bool Compiler::visit(FunctionDefinition& _function)
bool Compiler::visit(FunctionDefinition const& _function)
{
//@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
@ -195,15 +193,12 @@ bool Compiler::visit(FunctionDefinition& _function)
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned const numArguments = _function.getParameters().size();
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters() + _function.getReturnParameters())
for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
m_context.addVariable(*variable);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addVariable(*localVariable);
m_context.initializeLocalVariables(numReturnValues + numLocalVariables);
m_context.addAndInitializeVariable(*localVariable);
_function.getBody().accept(*this);
@ -215,12 +210,16 @@ bool Compiler::visit(FunctionDefinition& _function)
// Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work.
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
vector<int> stackLayout;
stackLayout.push_back(numReturnValues); // target of return address
stackLayout += vector<int>(numArguments, -1); // discard all arguments
for (unsigned i = 0; i < numReturnValues; ++i)
stackLayout.push_back(returnValuesSize); // target of return address
stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < returnValuesSize; ++i)
stackLayout.push_back(i);
stackLayout += vector<int>(numLocalVariables, -1);
stackLayout += vector<int>(localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0)
@ -240,9 +239,9 @@ bool Compiler::visit(FunctionDefinition& _function)
return false;
}
bool Compiler::visit(IfStatement& _ifStatement)
bool Compiler::visit(IfStatement const& _ifStatement)
{
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement())
_ifStatement.getFalseStatement()->accept(*this);
@ -253,7 +252,7 @@ bool Compiler::visit(IfStatement& _ifStatement)
return false;
}
bool Compiler::visit(WhileStatement& _whileStatement)
bool Compiler::visit(WhileStatement const& _whileStatement)
{
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
@ -261,7 +260,7 @@ bool Compiler::visit(WhileStatement& _whileStatement)
m_breakTags.push_back(loopEnd);
m_context << loopStart;
ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
compileExpression(_whileStatement.getCondition());
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
@ -275,60 +274,60 @@ bool Compiler::visit(WhileStatement& _whileStatement)
return false;
}
bool Compiler::visit(Continue&)
bool Compiler::visit(Continue const&)
{
if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back());
return false;
}
bool Compiler::visit(Break&)
bool Compiler::visit(Break const&)
{
if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back());
return false;
}
bool Compiler::visit(Return& _return)
bool Compiler::visit(Return const& _return)
{
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression* expression = _return.getExpression())
if (Expression const* expression = _return.getExpression())
{
ExpressionCompiler::compileExpression(m_context, *expression);
compileExpression(*expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable));
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
CompilerUtils(m_context).moveToStackVariable(firstVariable);
}
m_context.appendJumpTo(m_returnTag);
return false;
}
bool Compiler::visit(VariableDefinition& _variableDefinition)
bool Compiler::visit(VariableDefinition const& _variableDefinition)
{
if (Expression* expression = _variableDefinition.getExpression())
if (Expression const* expression = _variableDefinition.getExpression())
{
ExpressionCompiler::compileExpression(m_context, *expression);
compileExpression(*expression);
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration());
unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
}
return false;
}
bool Compiler::visit(ExpressionStatement& _expressionStatement)
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
// Type::Category category = expression.getType()->getCategory();
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
Expression const& expression = _expressionStatement.getExpression();
compileExpression(expression);
CompilerUtils(m_context).popStackElement(*expression.getType());
return false;
}
void Compiler::compileExpression(Expression const& _expression)
{
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
}
}
}

26
libsolidity/Compiler.h

@ -27,13 +27,13 @@
namespace dev {
namespace solidity {
class Compiler: private ASTVisitor
class Compiler: private ASTConstVisitor
{
public:
Compiler(): m_returnTag(m_context.newTag()) {}
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
@ -48,16 +48,18 @@ private:
void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(FunctionDefinition& _function) override;
virtual bool visit(IfStatement& _ifStatement) override;
virtual bool visit(WhileStatement& _whileStatement) override;
virtual bool visit(Continue& _continue) override;
virtual bool visit(Break& _break) override;
virtual bool visit(Return& _return) override;
virtual bool visit(VariableDefinition& _variableDefinition) override;
virtual bool visit(ExpressionStatement& _expressionStatement) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(Continue const& _continue) override;
virtual bool visit(Break const& _break) override;
virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
void compileExpression(Expression const& _expression);
bool const m_optimize;
CompilerContext m_context;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement

30
libsolidity/CompilerContext.cpp

@ -41,20 +41,30 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize();
}
void CompilerContext::initializeLocalVariables(unsigned _numVariables)
void CompilerContext::addVariable(VariableDeclaration const& _declaration)
{
if (_numVariables > 0)
{
m_localVariables[&_declaration] = m_localVariablesSize;
m_localVariablesSize += _declaration.getType()->getSizeOnStack();
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{
addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
*this << u256(0);
for (unsigned i = 1; i < _numVariables; ++i)
*this << eth::Instruction::DUP1;
m_asm.adjustDeposit(-_numVariables);
}
m_asm.adjustDeposit(-size);
}
void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{
return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end();
return m_localVariables.count(_declaration) > 0;
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
@ -67,10 +77,10 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration);
auto res = m_localVariables.find(&_declaration);
if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack."));
return unsigned(end(m_localVariables) - res - 1);
return m_localVariablesSize - res->second - 1;
}
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const

13
libsolidity/CompilerContext.h

@ -25,6 +25,7 @@
#include <ostream>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Types.h>
namespace dev {
@ -43,9 +44,9 @@ public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables);
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); }
void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); }
void addVariable(VariableDeclaration const& _declaration);
void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function);
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
@ -98,8 +99,10 @@ private:
u256 m_stateVariablesSize;
/// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack.
std::vector<Declaration const*> m_localVariables;
/// Offsets of local variables on the stack (relative to stack base).
std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
};

212
libsolidity/CompilerStack.cpp

@ -27,6 +27,7 @@
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
using namespace std;
@ -35,20 +36,43 @@ namespace dev
namespace solidity
{
void CompilerStack::addSource(string const& _name, string const& _content)
{
if (m_sources.count(_name))
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists."));
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
}
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
m_scanner = make_shared<Scanner>(CharStream(_sourceCode));
addSource("", _sourceCode);
}
void CompilerStack::parse()
{
if (!m_scanner)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available."));
m_contractASTNode = Parser().parse(m_scanner);
for (auto& sourcePair: m_sources)
{
sourcePair.second.scanner->reset();
sourcePair.second.ast = Parser().parse(sourcePair.second.scanner);
}
resolveImports();
m_globalContext = make_shared<GlobalContext>();
m_globalContext->setCurrentContract(*m_contractASTNode);
NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode);
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());
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
m_parseSuccessful = true;
}
@ -58,71 +82,90 @@ void CompilerStack::parse(string const& _sourceCode)
parse();
}
bytes const& CompilerStack::compile(bool _optimize)
vector<string> CompilerStack::getContractNames() const
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear();
m_compiler = make_shared<Compiler>();
m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables());
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
vector<string> contractNames;
for (auto const& contract: m_contracts)
contractNames.push_back(contract.first);
return contractNames;
}
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
parse();
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);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.compiler = move(compiler);
}
}
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
parse(_sourceCode);
return compile(_optimize);
compile(_optimize);
return getBytecode();
}
bytes const& CompilerStack::getBytecode(string const& _contractName) const
{
return getContract(_contractName).bytecode;
}
void CompilerStack::streamAssembly(ostream& _outStream)
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{
if (!m_compiler || m_bytecode.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
m_compiler->streamAssembly(_outStream);
getContract(_contractName).compiler->streamAssembly(_outStream);
}
string const& CompilerStack::getInterface()
string const& CompilerStack::getInterface(string const& _contractName) const
{
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
}
string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
if (m_interface.empty())
Contract const& contract = getContract(_contractName);
std::unique_ptr<string const>* doc;
switch (_type)
{
stringstream interface;
interface << '[';
vector<FunctionDefinition const*> exportedFunctions = m_contractASTNode->getInterfaceFunctions();
unsigned functionsCount = exportedFunctions.size();
for (FunctionDefinition const* f: exportedFunctions)
{
auto streamVariables = [&](vector<ASTPointer<VariableDeclaration>> const& _vars)
{
unsigned varCount = _vars.size();
for (ASTPointer<VariableDeclaration> const& var: _vars)
{
interface << "{"
<< "\"name\":" << escaped(var->getName(), false) << ","
<< "\"type\":" << escaped(var->getType()->toString(), false)
<< "}";
if (--varCount > 0)
interface << ",";
}
};
interface << '{'
<< "\"name\":" << escaped(f->getName(), false) << ","
<< "\"inputs\":[";
streamVariables(f->getParameters());
interface << "],"
<< "\"outputs\":[";
streamVariables(f->getReturnParameters());
interface << "]"
<< "}";
if (--functionsCount > 0)
interface << ",";
}
interface << ']';
m_interface = interface.str();
case DocumentationType::NATSPEC_USER:
doc = &contract.userDocumentation;
break;
case DocumentationType::NATSPEC_DEV:
doc = &contract.devDocumentation;
break;
case DocumentationType::ABI_INTERFACE:
doc = &contract.interface;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
return m_interface;
if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
return *(*doc);
}
Scanner const& CompilerStack::getScanner(string const& _sourceName) const
{
return *getSource(_sourceName).scanner;
}
SourceUnit const& CompilerStack::getAST(string const& _sourceName) const
{
return *getSource(_sourceName).ast;
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
@ -131,7 +174,70 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize);
}
void CompilerStack::reset(bool _keepSources)
{
m_parseSuccessful = false;
if (_keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
m_sources.clear();
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()))
{
string const& id = import->getIdentifier();
if (!m_sources.count(id))
BOOST_THROW_EXCEPTION(ParserError()
<< errinfo_sourceLocation(import->getLocation())
<< errinfo_comment("Source not found."));
toposort(&m_sources[id]);
}
sourceOrder.push_back(_source);
};
for (auto const& sourcePair: m_sources)
toposort(&sourcePair.second);
swap(m_sourceOrder, sourceOrder);
}
CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const
{
if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
if (_contractName.empty())
return m_contracts.begin()->second;
auto it = m_contracts.find(_contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
return it->second;
}
CompilerStack::Source const& CompilerStack::getSource(string const& _sourceName) const
{
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;
}
CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
}
}

83
libsolidity/CompilerStack.h

@ -25,6 +25,7 @@
#include <ostream>
#include <string>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
namespace dev {
@ -33,52 +34,102 @@ namespace solidity {
// forward declarations
class Scanner;
class ContractDefinition;
class SourceUnit;
class Compiler;
class GlobalContext;
class InterfaceHandler;
enum class DocumentationType: uint8_t
{
NATSPEC_USER = 1,
NATSPEC_DEV,
ABI_INTERFACE
};
/**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
* It holds state and can be used to either step through the compilation stages (and abort e.g.
* before compilation to bytecode) or run the whole compilation in one call.
*/
class CompilerStack
class CompilerStack: boost::noncopyable
{
public:
CompilerStack() {}
void reset() { *this = CompilerStack(); }
CompilerStack(): m_parseSuccessful(false) {}
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
void addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse();
/// Sets the given source code as the only source unit and parses it.
void parse(std::string const& _sourceCode);
/// Compiles the contract that was previously parsed.
bytes const& compile(bool _optimize = false);
/// Returns a list of the contract names in the sources.
std::vector<std::string> getContractNames() const;
/// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false);
/// Parses and compiles the given source code.
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
bytes const& getBytecode() const { return m_bytecode; }
bytes const& getBytecode(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream);
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
std::string const& getInterface();
std::string const& getInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner() const { return *m_scanner; }
ContractDefinition& getAST() const { return *m_contractASTNode; }
Scanner const& getScanner(std::string const& _sourceName = "") const;
SourceUnit const& getAST(std::string const& _sourceName = "") const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
private:
std::shared_ptr<Scanner> m_scanner;
std::shared_ptr<GlobalContext> m_globalContext;
std::shared_ptr<ContractDefinition> m_contractASTNode;
/**
* Information pertaining to one source unit, filled gradually during parsing and compilation.
*/
struct Source
{
std::shared_ptr<Scanner> scanner;
std::shared_ptr<SourceUnit> ast;
std::string interface;
void reset() { scanner.reset(); ast.reset(); interface.clear(); }
};
struct Contract
{
ContractDefinition* contract;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> userDocumentation;
mutable std::unique_ptr<std::string const> devDocumentation;
Contract();
};
void reset(bool _keepSources = false);
void resolveImports();
Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const;
bool m_parseSuccessful;
std::string m_interface;
std::shared_ptr<Compiler> m_compiler;
bytes m_bytecode;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;
std::vector<Source const*> m_sourceOrder;
std::map<std::string const, Contract> m_contracts;
};
}

111
libsolidity/CompilerUtils.cpp

@ -0,0 +1,111 @@
/*
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
* Routines used by both the compiler and the expression compiler.
*/
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/AST.h>
#include <libevmcore/Instruction.h>
using namespace std;
namespace dev
{
namespace solidity
{
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
{
if (_bytes == 0)
{
m_context << u256(0);
return;
}
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested."));
if (_bytes == 32)
m_context << u256(_offset) << load;
else
{
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
m_context << shiftFactor;
if (_leftAligned)
m_context << eth::Instruction::DUP1;
m_context << u256(_offset) << load << eth::Instruction::DIV;
if (_leftAligned)
m_context << eth::Instruction::MUL;
}
}
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
{
if (_bytes == 0)
{
m_context << eth::Instruction::POP;
return;
}
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested."));
if (_bytes != 32 && !_leftAligned)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
unsigned const size = _variable.getType()->getSizeOnStack();
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < size; ++i)
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
}
void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type)
{
if (_stackDepth > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::dupInstruction(_stackDepth);
}
void CompilerUtils::popStackElement(Type const& _type)
{
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
{
unsigned size = 0;
for (shared_ptr<Type const> const& type: _variableTypes)
size += type->getSizeOnStack();
return size;
}
}
}

75
libsolidity/CompilerUtils.h

@ -0,0 +1,75 @@
/*
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
* Routines used by both the compiler and the expression compiler.
*/
#pragma once
#include <libsolidity/CompilerContext.h>
#include <libsolidity/ASTForward.h>
namespace dev {
namespace solidity {
class Type; // forward
class CompilerUtils
{
public:
CompilerUtils(CompilerContext& _context): m_context(_context) {}
/// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata)
/// @param _bytes number of bytes to load
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
/// @param _fromCalldata if true, load from calldata, not from memory
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
/// Stores data from stack in memory.
/// @param _offset offset in memory
/// @param _bytes number of bytes to store
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
void copyToStackTop(unsigned _stackDepth, Type const& _type);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
template <class T>
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
private:
CompilerContext& m_context;
};
template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{
unsigned size = 0;
for (T const& variable: _variables)
size += variable->getType()->getSizeOnStack();
return size;
}
}
}

6
libsolidity/DeclarationContainer.cpp

@ -28,15 +28,15 @@ namespace dev
namespace solidity
{
bool DeclarationContainer::registerDeclaration(Declaration& _declaration)
bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update)
{
if (m_declarations.find(_declaration.getName()) != m_declarations.end())
if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end())
return false;
m_declarations[_declaration.getName()] = &_declaration;
return true;
}
Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
auto result = m_declarations.find(_name);
if (result != m_declarations.end())

15
libsolidity/DeclarationContainer.h

@ -39,18 +39,19 @@ namespace solidity
class DeclarationContainer
{
public:
explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr):
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared.
bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
bool registerDeclaration(Declaration const& _declaration, bool _update = false);
Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
private:
Declaration* m_enclosingDeclaration;
DeclarationContainer* m_enclosingContainer;
std::map<ASTString, Declaration*> m_declarations;
Declaration const* m_enclosingDeclaration;
DeclarationContainer const* m_enclosingContainer;
std::map<ASTString, Declaration const*> m_declarations;
};
}

2
libsolidity/Exceptions.h

@ -36,8 +36,8 @@ struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {};
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
}

279
libsolidity/ExpressionCompiler.cpp

@ -26,15 +26,16 @@
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
#include <libsolidity/CompilerUtils.h>
using namespace std;
namespace dev {
namespace solidity {
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression)
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize)
{
ExpressionCompiler compiler(_context);
ExpressionCompiler compiler(_context, _optimize);
_expression.accept(compiler);
}
@ -45,7 +46,7 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
compiler.appendTypeConversion(_typeOnStack, _targetType);
}
bool ExpressionCompiler::visit(Assignment& _assignment)
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
@ -67,7 +68,7 @@ bool ExpressionCompiler::visit(Assignment& _assignment)
return false;
}
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
@ -128,10 +129,10 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
}
}
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{
Expression& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression();
Expression const& leftExpression = _binaryOperation.getLeftExpression();
Expression const& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType();
Token::Value const op = _binaryOperation.getOperator();
@ -144,10 +145,24 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
cleanupNeeded = true;
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
// for commutative operators, push the literal as late as possible to allow improved optimization
//@todo this has to be extended for literal expressions
bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast<Literal const*>(&rightExpression)
&& !dynamic_cast<Literal const*>(&leftExpression));
if (swap)
{
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
}
else
{
rightExpression.accept(*this);
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
leftExpression.accept(*this);
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
}
if (Token::isCompareOp(op))
appendCompareOperatorCode(op, commonType);
else
@ -158,7 +173,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
return false;
}
bool ExpressionCompiler::visit(FunctionCall& _functionCall)
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
using Location = FunctionType::Location;
if (_functionCall.isTypeConversion())
@ -166,7 +181,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
//@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front();
Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
_functionCall.getType()->getCategory() == Type::Category::INTEGER)
@ -174,18 +189,18 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// explicit type conversion contract -> address, nothing to do.
}
else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
}
else
{
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
if (function.getLocation() == Location::INTERNAL)
switch (function.getLocation())
{
case Location::INTERNAL:
{
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
@ -201,74 +216,105 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
m_context.appendJump();
m_context << returnLabel;
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1);
m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
// @todo for now, the return value of a function is its first return value, so remove
// all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP;
CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
break;
}
else if (function.getLocation() == Location::EXTERNAL)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
else
case Location::EXTERNAL:
{
switch (function.getLocation())
unsigned dataOffset = 1; // reserve one byte for the function index
for (unsigned i = 0; i < arguments.size(); ++i)
{
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
arguments[i]->accept(*this);
Type const& type = *function.getParameterTypes()[i];
appendTypeConversion(*arguments[i]->getType(), type);
unsigned const numBytes = type.getCalldataEncodedSize();
if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
bool const leftAligned = type.getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
dataOffset += numBytes;
}
//@todo only return the first return value for now
Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr :
function.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
_functionCall.getExpression().accept(*this); // pushes addr and function index
m_context << u256(0) << eth::Instruction::MSTORE8
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
if (retSize > 0)
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
// @todo later, combine this code with external function call
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
m_context << u256(i * 32) << eth::Instruction::MSTORE;
}
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
break;
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented."));
break;
}
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
// @todo later, combine this code with external function call
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(i * 32);
}
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP;
CompilerUtils(m_context).loadFromMemory(0);
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
}
}
return false;
}
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory())
@ -289,9 +335,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::CONTRACT:
// call function
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
{
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
m_context << type.getFunctionIndex(member);
break;
}
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
@ -323,7 +371,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE);
m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
@ -332,7 +380,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
}
}
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this);
@ -340,52 +388,54 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
true);
// @todo move this once we actually use memory
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
CompilerUtils(m_context).storeInMemory(0);
CompilerUtils(m_context).storeInMemory(32);
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE);
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false;
}
void ExpressionCompiler::endVisit(Identifier& _identifier)
void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
Declaration* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration))
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS;
return;
}
if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration))
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
}
if (/*VariableDeclaration* varDef = */dynamic_cast<VariableDeclaration*>(declaration))
if (dynamic_cast<VariableDeclaration const*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
void ExpressionCompiler::endVisit(Literal& _literal)
void ExpressionCompiler::endVisit(Literal const& _literal)
{
switch (_literal.getType()->getCategory())
{
case Type::Category::INTEGER:
case Type::Category::BOOL:
case Type::Category::STRING:
m_context << _literal.getType()->literalValue(_literal);
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now."));
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
}
}
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
Token::Value const op = _binaryOperation.getOperator();
if (asserts(op == Token::OR || op == Token::AND))
@ -517,6 +567,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER)
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
else if (_typeOnStack.getCategory() == Type::Category::STRING)
{
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
}
else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types.
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));
@ -532,6 +587,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
}
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
m_stackSize(_dataType.getSizeOnStack())
{
}
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
{
switch (m_type)
@ -542,7 +604,8 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
*m_context << eth::dupInstruction(stackPos + 1);
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
case STORAGE:
@ -550,7 +613,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
*m_context << eth::Instruction::SLOAD;
if (m_stackSize == 1)
*m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_stackSize; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
break;
case MEMORY:
if (!_expression.getType()->isValueType())
@ -571,12 +644,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{
case STACK:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackPos > 16)
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
else if (stackPos > 0)
*m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
else if (stackDiff > 0)
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
retrieveValue(_expression);
break;
@ -584,9 +658,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
case LValue::STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_move)
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
*m_context << eth::Instruction::SSTORE;
// stack layout: value value ... value ref
if (!_move) // copy values
{
if (m_stackSize + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
}
if (m_stackSize > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i)
{
if (i + 1 >= m_stackSize)
*m_context << eth::Instruction::SSTORE;
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
@ -612,6 +704,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration))
{
m_type = STACK;

48
libsolidity/ExpressionCompiler.h

@ -31,40 +31,42 @@ class AssemblyItem; // forward
}
namespace solidity {
class CompilerContext; // forward
class Type; // forward
class IntegerType; // forward
// forward declarations
class CompilerContext;
class Type;
class IntegerType;
class StaticStringType;
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit.
*/
class ExpressionCompiler: private ASTVisitor
class ExpressionCompiler: private ASTConstVisitor
{
public:
/// Compile the given @a _expression into the @a _context.
static void compileExpression(CompilerContext& _context, Expression& _expression);
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
private:
ExpressionCompiler(CompilerContext& _compilerContext):
m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override;
virtual bool visit(FunctionCall& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override;
virtual bool visit(IndexAccess& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override;
virtual void endVisit(Literal& _literal) override;
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess const& _indexAccess) override;
virtual void endVisit(Identifier const& _identifier) override;
virtual void endVisit(Literal const& _literal) override;
///@{
///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation);
void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
@ -74,7 +76,7 @@ private:
/// @}
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer types.
/// higher-order bits (@see appendHighBitCleanup) when widening integer.
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary.
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
@ -92,8 +94,7 @@ private:
enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
@ -128,14 +129,13 @@ private:
/// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize;
};
bool m_optimize;
CompilerContext& m_context;
LValue m_currentLValue;
/// If a "virtual" function (i.e. a bulit-in function without jump tag) is encountered, the
/// actual function is stored here. @todo prevent assignment or store it with assignment
enum class SpecialFunction { NONE, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
SpecialFunction m_currentSpecialFunction;
};

9
libsolidity/GlobalContext.cpp

@ -68,17 +68,16 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
m_currentContract = &_contract;
}
vector<Declaration*> GlobalContext::getDeclarations() const
vector<Declaration const*> GlobalContext::getDeclarations() const
{
vector<Declaration*> declarations;
vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<Declaration> const& variable: m_magicVariables)
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
MagicVariableDeclaration*GlobalContext::getCurrentThis() const
MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
{
if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(

10
libsolidity/GlobalContext.h

@ -47,15 +47,17 @@ class GlobalContext: private boost::noncopyable
public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
std::vector<Declaration*> getDeclarations() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const;
private:
MagicVariableDeclaration* getCurrentThis() const;
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
ContractDefinition const* m_currentContract;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
};
}

340
libsolidity/InterfaceHandler.cpp

@ -0,0 +1,340 @@
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h>
namespace dev
{
namespace solidity
{
/* -- public -- */
InterfaceHandler::InterfaceHandler()
{
m_lastTag = DocTagType::NONE;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef,
DocumentationType _type)
{
switch(_type)
{
case DocumentationType::NATSPEC_USER:
return getUserDocumentation(_contractDef);
case DocumentationType::NATSPEC_DEV:
return getDevDocumentation(_contractDef);
case DocumentationType::ABI_INTERFACE:
return getABIInterface(_contractDef);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
return nullptr;
}
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef)
{
Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{
Json::Value method;
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
{
Json::Value params(Json::arrayValue);
for (ASTPointer<VariableDeclaration> const& var: _vars)
{
Json::Value input;
input["name"] = var->getName();
input["type"] = var->getType()->toString();
params.append(input);
}
return params;
};
method["name"] = f->getName();
method["inputs"] = populateParameters(f->getParameters());
method["outputs"] = populateParameters(f->getReturnParameters());
methods.append(method);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
}
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{
Json::Value user;
auto strPtr = f->getDocumentation();
if (strPtr)
{
resetUser();
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
methods[f->getName()] = user;
}
}
}
doc["methods"] = methods;
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
}
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef)
{
// LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown
Json::Value doc;
Json::Value methods(Json::objectValue);
auto contractDoc = _contractDef.getDocumentation();
if (contractDoc)
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
if (!m_title.empty())
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{
Json::Value method;
auto strPtr = f->getDocumentation();
if (strPtr)
{
resetDev();
parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_dev.empty())
method["details"] = Json::Value(m_dev);
if (!m_author.empty())
method["author"] = m_author;
Json::Value params(Json::objectValue);
for (auto const& pair: m_params)
params[pair.first] = pair.second;
if (!m_params.empty())
method["params"] = params;
if (!m_return.empty())
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
methods[f->getName()] = method;
}
}
doc["methods"] = methods;
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
}
/* -- private -- */
void InterfaceHandler::resetUser()
{
m_notice.clear();
}
void InterfaceHandler::resetDev()
{
m_dev.clear();
m_author.clear();
m_return.clear();
m_params.clear();
}
static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos,
std::string::const_iterator _end)
{
return (_nlPos == _end) ? _end : ++_nlPos;
}
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
DocTagType _tagType)
{
auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(_tagString));
m_lastTag = _tagType;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
// find param name
auto currPos = std::find(_pos, _end, ' ');
if (currPos == _end)
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end)));
auto paramName = std::string(_pos, currPos);
currPos += 1;
auto nlPos = std::find(currPos, _end, '\n');
auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DocTagType::PARAM;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
// Should never be called with an empty vector
if (asserts(!m_params.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter"));
auto pair = m_params.back();
pair.second += " ";
auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(pair.second));
m_params.at(m_params.size() - 1) = pair;
return skipLineOrEOS(nlPos, _end);
}
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag,
CommentOwner _owner)
{
// LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "")
{
if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
}
else if (_tag == "param")
return parseDocTagParam(_pos, _end);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
}
else
return appendDocTag(_pos, _end, _owner);
}
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
CommentOwner _owner)
{
switch (m_lastTag)
{
case DocTagType::DEV:
m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE:
m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN:
m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
{
m_contractAuthor += " ";
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
}
else if (_owner == CommentOwner::FUNCTION)
{
m_author += " ";
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
{
m_title += " ";
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM:
return appendDocTagParam(_pos, _end);
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
break;
}
}
static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos,
std::string::const_iterator _end)
{
auto spacePos = std::find(_pos, _end, ' ');
auto nlPos = std::find(_pos, _end, '\n');
return (spacePos < nlPos) ? spacePos : nlPos;
}
void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
{
auto currPos = _string.begin();
auto end = _string.end();
while (currPos != end)
{
auto tagPos = std::find(currPos, end, '@');
auto nlPos = std::find(currPos, end, '\n');
if (tagPos != end && tagPos < nlPos)
{
// we found a tag
auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end);
if (tagNameEndPos == end)
BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
}
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end, _owner);
else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1;
}
}
} //solidity NS
} // dev NS

123
libsolidity/InterfaceHandler.h

@ -0,0 +1,123 @@
/*
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 Lefteris <lefteris@ethdev.com>
* @date 2014
* Takes the parsed AST and produces the Natspec
* documentation and the ABI interface
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
*
* Can generally deal with JSON files
*/
#pragma once
#include <string>
#include <memory>
#include <jsonrpc/json/json.h>
namespace dev
{
namespace solidity
{
// Forward declarations
class ContractDefinition;
enum class DocumentationType: uint8_t;
enum class DocTagType: uint8_t
{
NONE = 0,
DEV,
NOTICE,
PARAM,
RETURN,
AUTHOR,
TITLE
};
enum class CommentOwner
{
CONTRACT,
FUNCTION
};
class InterfaceHandler
{
public:
InterfaceHandler();
/// Get the given type of documentation
/// @param _contractDef The contract definition
/// @param _type The type of the documentation. Can be one of the
/// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json
/// representation of provided type
std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef,
DocumentationType _type);
/// Get the ABI Interface of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition& _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition& _contractDef);
/// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition& _contractDef);
private:
void resetUser();
void resetDev();
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string& _tagString,
DocTagType _tagType);
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end);
void parseDocString(std::string const& _string, CommentOwner _owner);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
CommentOwner _owner);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end,
std::string const& _tag,
CommentOwner _owner);
Json::StyledWriter m_writer;
// internal state
DocTagType m_lastTag;
std::string m_notice;
std::string m_dev;
std::string m_return;
std::string m_contractAuthor;
std::string m_author;
std::string m_title;
std::vector<std::pair<std::string, std::string>> m_params;
};
} //solidity NS
} // dev NS

31
libsolidity/NameAndTypeResolver.cpp

@ -32,15 +32,20 @@ namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globals)
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{
for (Declaration* declaration: _globals)
for (Declaration const* declaration: _globals)
m_scopes[nullptr].registerDeclaration(*declaration);
}
void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
{
// The helper registers all declarations in m_scopes as a side-effect of its construction.
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit);
}
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
@ -65,7 +70,14 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[nullptr];
}
Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{
m_scopes[nullptr].registerDeclaration(_declaration, true);
if (asserts(_declaration.getScope() == nullptr))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Updated declaration outside global scope."));
}
Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
@ -73,7 +85,7 @@ Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaratio
return iterator->second.resolveName(_name, false);
}
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{
return m_currentScope->resolveName(_name, _recursive);
}
@ -135,7 +147,7 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration)
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{
map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded;
@ -193,15 +205,14 @@ bool ReferencesResolver::visit(Return& _return)
return true;
}
bool ReferencesResolver::visit(Mapping& _mapping)
bool ReferencesResolver::visit(Mapping&)
{
(void)_mapping;
return true;
}
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
{
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier."));
@ -211,7 +222,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
bool ReferencesResolver::visit(Identifier& _identifier)
{
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier."));

18
libsolidity/NameAndTypeResolver.h

@ -41,21 +41,25 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable
{
public:
explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals);
explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
/// Registers all declarations found in the source unit.
void registerDeclarations(SourceUnit& _sourceUnit);
/// Resolves all names and types referenced from the given contract.
void resolveNamesAndTypes(ContractDefinition& _contract);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
void updateDeclaration(Declaration const& _declaration);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract).
/// @returns a pointer to the declaration on success or nullptr on failure.
Declaration* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private:
/// Throws if @a _struct contains a recursive loop. Note that recursion via mappings is fine.
void checkForRecursion(StructDefinition const& _struct);
void reset();
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
@ -85,12 +89,12 @@ private:
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
void enterNewSubScope(Declaration& _declaration);
void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration* m_currentScope;
Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction;
};

91
libsolidity/Parser.cpp

@ -20,30 +20,27 @@
* Solidity parser.
*/
#include <vector>
#include <libdevcore/Log.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
using namespace std;
namespace dev
{
namespace solidity
{
ASTPointer<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
return parseContractDefinition();
}
/// AST node factory that also tracks the begin and end position of an AST node
/// while it is being parsed
class Parser::ASTNodeFactory
{
public:
ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {}
ASTNodeFactory(Parser const& _parser):
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocationEmpty() { m_location.end = m_location.start; }
@ -55,7 +52,7 @@ public:
{
if (m_location.end < 0)
markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
return make_shared<NodeType>(m_location, forward<Args>(_args)...);
}
private:
@ -63,6 +60,33 @@ private:
Location m_location;
};
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<ASTNode>> nodes;
while (_scanner->getCurrentToken() != Token::EOS)
{
switch (m_scanner->getCurrentToken())
{
case Token::IMPORT:
nodes.push_back(parseImportDirective());
break;
case Token::CONTRACT:
nodes.push_back(parseContractDefinition());
break;
default:
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
}
}
return nodeFactory.createNode<SourceUnit>(nodes);
}
std::shared_ptr<const string> const& Parser::getSourceName() const
{
return m_scanner->getSourceName();
}
int Parser::getPosition() const
{
return m_scanner->getCurrentLocation().start;
@ -73,15 +97,30 @@ int Parser::getEndPosition() const
return m_scanner->getCurrentLocation().end;
}
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::IMPORT);
if (m_scanner->getCurrentToken() != Token::STRING_LITERAL)
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
ASTPointer<ASTString> url = getLiteralAndAdvance();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
return nodeFactory.createNode<ImportDirective>(url);
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE);
std::vector<ASTPointer<StructDefinition>> structs;
std::vector<ASTPointer<VariableDeclaration>> stateVariables;
std::vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
bool visibilityIsPublic = true;
while (true)
{
@ -110,8 +149,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
@ -119,7 +157,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
@ -142,7 +180,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>());
returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
@ -156,7 +194,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT);
ASTPointer<ASTString> name = expectIdentifierToken();
std::vector<ASTPointer<VariableDeclaration>> members;
vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
{
@ -228,7 +266,7 @@ ASTPointer<Mapping> Parser::parseMapping()
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{
ASTNodeFactory nodeFactory(*this);
std::vector<ASTPointer<VariableDeclaration>> parameters;
vector<ASTPointer<VariableDeclaration>> parameters;
expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{
@ -249,7 +287,7 @@ ASTPointer<Block> Parser::parseBlock()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE);
std::vector<ASTPointer<Statement>> statements;
vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE)
statements.push_back(parseStatement());
nodeFactory.markEndPosition();
@ -447,7 +485,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LPAREN:
{
m_scanner->next();
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
@ -503,9 +541,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression;
}
std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
{
std::vector<ASTPointer<Expression>> arguments;
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN)
{
arguments.push_back(parseExpression());
@ -521,7 +559,7 @@ std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
void Parser::expectToken(Token::Value _value)
{
if (m_scanner->getCurrentToken() != _value)
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected token ") + std::string(Token::getName(_value))));
BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value))));
m_scanner->next();
}
@ -543,14 +581,15 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
ASTPointer<ASTString> Parser::getLiteralAndAdvance()
{
ASTPointer<ASTString> identifier = std::make_shared<ASTString>(m_scanner->getCurrentLiteral());
ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->getCurrentLiteral());
m_scanner->next();
return identifier;
}
ParserError Parser::createParserError(std::string const& _description) const
ParserError Parser::createParserError(string const& _description) const
{
return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description);
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))
<< errinfo_comment(_description);
}

4
libsolidity/Parser.h

@ -34,7 +34,8 @@ class Scanner;
class Parser
{
public:
ASTPointer<ContractDefinition> parse(std::shared_ptr<Scanner> const& _scanner);
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
std::shared_ptr<std::string const> const& getSourceName() const;
private:
class ASTNodeFactory;
@ -46,6 +47,7 @@ private:
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition();

11
libsolidity/Scanner.cpp

@ -143,17 +143,22 @@ private:
}; // end of LiteralScope class
void Scanner::reset(CharStream const& _source)
void Scanner::reset(CharStream const& _source, string const& _sourceName)
{
m_source = _source;
m_sourceName = make_shared<string const>(_sourceName);
reset();
}
void Scanner::reset()
{
m_source.reset();
m_char = m_source.get();
skipWhitespace();
scanToken();
next();
}
bool Scanner::scanHexByte(char& o_scannedByte)
{
char x = 0;

14
libsolidity/Scanner.h

@ -79,6 +79,8 @@ public:
char advanceAndGet(size_t _chars=1);
char rollback(size_t _amount);
void reset() { m_pos = 0; }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
@ -99,11 +101,12 @@ class Scanner
friend class LiteralScope;
public:
Scanner() { reset(CharStream()); }
explicit Scanner(CharStream const& _source) { reset(_source); }
explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); }
/// Resets the scanner as if newly constructed with _input as input.
void reset(CharStream const& _source);
/// Resets the scanner as if newly constructed with _source and _sourceName as input.
void reset(CharStream const& _source, std::string const& _sourceName);
/// Resets scanner to the start of input.
void reset();
/// Returns the next token and advances input
Token::Value next();
@ -139,6 +142,8 @@ public:
std::string const& peekLiteral() const { return m_nextToken.literal; }
///@}
std::shared_ptr<std::string const> const& getSourceName() const { return m_sourceName; }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
@ -203,6 +208,7 @@ private:
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
CharStream m_source;
std::shared_ptr<std::string const> m_sourceName;
/// one character look-ahead, equals 0 at end of input
char m_char;

41
libsolidity/SourceReferenceFormatter.cpp

@ -21,6 +21,7 @@
*/
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
@ -38,7 +39,6 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
int startLine;
int startColumn;
tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
_stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n";
int endLine;
int endColumn;
tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
@ -58,37 +58,28 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
<< "Spanning multiple lines.\n";
}
void SourceReferenceFormatter::printSourcePosition(ostream& _stream,
int _position,
const Scanner& _scanner)
{
int line;
int column;
tie(line, column) = _scanner.translatePositionToLineColumn(_position);
_stream << "at line " << (line + 1) << ", column " << (column + 1) << endl
<< _scanner.getLineAtPosition(_position) << endl
<< string(column, ' ') << "^" << endl;
}
void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
Exception const& _exception,
string const& _name,
Scanner const& _scanner)
CompilerStack const& _compiler)
{
_stream << _name;
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description;
Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
Scanner const* scanner;
if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception))
if (location)
{
_stream << " ";
printSourcePosition(_stream, *position, _scanner);
}
if (Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception))
{
_stream << " ";
printSourceLocation(_stream, *location, _scanner);
scanner = &_compiler.getScanner(*location->sourceName);
int startLine;
int startColumn;
tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start);
_stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
}
_stream << _name;
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description << endl;
if (location)
printSourceLocation(_stream, *location, *scanner);
}
}

4
libsolidity/SourceReferenceFormatter.h

@ -34,14 +34,14 @@ namespace solidity
{
class Scanner; // forward
class CompilerStack; // forward
struct SourceReferenceFormatter
{
public:
static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner);
static void printSourcePosition(std::ostream& _stream, int _position, Scanner const& _scanner);
static void printExceptionInformation(std::ostream& _stream, Exception const& _exception,
std::string const& _name, Scanner const& _scanner);
std::string const& _name, CompilerStack const& _compiler);
};
}

35
libsolidity/Token.h

@ -269,6 +269,39 @@ namespace solidity
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(STRING0, "string0", 0) \
K(STRING1, "string1", 0) \
K(STRING2, "string2", 0) \
K(STRING3, "string3", 0) \
K(STRING4, "string4", 0) \
K(STRING5, "string5", 0) \
K(STRING6, "string6", 0) \
K(STRING7, "string7", 0) \
K(STRING8, "string8", 0) \
K(STRING9, "string9", 0) \
K(STRING10, "string10", 0) \
K(STRING11, "string11", 0) \
K(STRING12, "string12", 0) \
K(STRING13, "string13", 0) \
K(STRING14, "string14", 0) \
K(STRING15, "string15", 0) \
K(STRING16, "string16", 0) \
K(STRING17, "string17", 0) \
K(STRING18, "string18", 0) \
K(STRING19, "string19", 0) \
K(STRING20, "string20", 0) \
K(STRING21, "string21", 0) \
K(STRING22, "string22", 0) \
K(STRING23, "string23", 0) \
K(STRING24, "string24", 0) \
K(STRING25, "string25", 0) \
K(STRING26, "string26", 0) \
K(STRING27, "string27", 0) \
K(STRING28, "string28", 0) \
K(STRING29, "string29", 0) \
K(STRING30, "string30", 0) \
K(STRING31, "string31", 0) \
K(STRING32, "string32", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
@ -317,6 +350,8 @@ public:
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
op == ADD || op == MUL || op == EQ || op == NE; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }

110
libsolidity/Types.cpp

@ -32,7 +32,7 @@ namespace dev
namespace solidity
{
shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
{
if (asserts(Token::isElementaryTypeName(_typeToken)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
@ -44,33 +44,35 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
if (bytes == 0)
bytes = 32;
int modifier = offset / 33;
return make_shared<IntegerType>(bytes * 8,
return make_shared<IntegerType const>(bytes * 8,
modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH);
}
else if (_typeToken == Token::ADDRESS)
return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL)
return make_shared<BoolType>();
return make_shared<BoolType const>();
else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32)
return make_shared<StaticStringType const>(int(_typeToken) - int(Token::STRING0));
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
}
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
return make_shared<StructType>(*structDef);
return make_shared<StructType const>(*structDef);
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
return make_shared<FunctionType>(*function);
return make_shared<FunctionType const>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract);
return shared_ptr<Type>();
return make_shared<ContractType const>(*contract);
return shared_ptr<Type const>();
}
shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName)
{
shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
if (!keyType)
@ -78,28 +80,29 @@ shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
shared_ptr<Type const> valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType);
return make_shared<MappingType const>(keyType, valueType);
}
shared_ptr<Type> Type::forLiteral(Literal const& _literal)
shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
{
switch (_literal.getToken())
{
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
return make_shared<BoolType>();
return make_shared<BoolType const>();
case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL:
return shared_ptr<Type>(); // @todo add string literals
//@todo put larger strings into dynamic strings
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
default:
return shared_ptr<Type>();
return shared_ptr<Type const>();
}
}
const MemberList Type::EmptyMemberList = MemberList();
shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _literal)
shared_ptr<IntegerType const> IntegerType::smallestTypeForLiteral(string const& _literal)
{
bigint value(_literal);
bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-');
@ -108,8 +111,8 @@ shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _liter
value = ((-value) - 1) << 1;
unsigned bytes = max(bytesRequired(value), 1u);
if (bytes > 32)
return shared_ptr<IntegerType>();
return make_shared<IntegerType>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
return shared_ptr<IntegerType const>();
return make_shared<IntegerType const>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
}
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
@ -140,7 +143,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.getCategory() == getCategory();
return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
}
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList =
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
TypePointers(), FunctionType::Location::SEND)}});
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
{
if (_literal.length() <= 32)
return make_shared<StaticStringType>(_literal.length());
return shared_ptr<StaticStringType>();
}
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
{
if (asserts(m_bytes >= 0 && m_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " +
dev::toString(m_bytes)));
}
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() != getCategory())
return false;
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
return convertTo.m_bytes >= m_bytes;
}
bool StaticStringType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
return other.m_bytes == m_bytes;
}
u256 StaticStringType::literalValue(const Literal& _literal) const
{
u256 value = 0;
for (char c: _literal.getValue())
value = (value << 8) | byte(c);
return value << ((32 - _literal.getValue().length()) * 8);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address
@ -247,6 +288,31 @@ string ContractType::toString() const
return "contract " + m_contract.getName();
}
MemberList const& ContractType::getMembers() const
{
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
members[function->getName()] = make_shared<FunctionType>(*function, false);
m_members.reset(new MemberList(members));
}
return *m_members;
}
unsigned ContractType::getFunctionIndex(string const& _functionName) const
{
unsigned index = 0;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
{
if (function->getName() == _functionName)
return index;
++index;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested."));
}
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -302,7 +368,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
FunctionType::FunctionType(FunctionDefinition const& _function)
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
{
TypePointers params;
TypePointers retParams;
@ -314,7 +380,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function)
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
m_location = Location::INTERNAL;
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
}
bool FunctionType::operator==(Type const& _other) const
@ -323,6 +389,8 @@ bool FunctionType::operator==(Type const& _other) const
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if (m_location != other.m_location)
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;

55
libsolidity/Types.h

@ -36,7 +36,7 @@ namespace dev
namespace solidity
{
// @todo realMxN, string<N>
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward
using TypePointer = std::shared_ptr<Type const>;
@ -80,15 +80,15 @@ public:
///@{
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type> fromFunction(FunctionDefinition const& _function);
static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
/// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type.
static std::shared_ptr<Type> forLiteral(Literal const& _literal);
static std::shared_ptr<Type const> forLiteral(Literal const& _literal);
virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
@ -148,7 +148,7 @@ public:
/// @returns the smallest integer type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal);
static std::shared_ptr<IntegerType const> smallestTypeForLiteral(std::string const& _literal);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
@ -178,12 +178,42 @@ private:
static const MemberList AddressMemberList;
};
/**
* String type with fixed length, up to 32 bytes.
*/
class StaticStringType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRING; }
/// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
StaticStringType(int _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBytes() const { return m_bytes; }
private:
int m_bytes;
};
/**
* The boolean type.
*/
class BoolType: public Type
{
public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override
@ -214,10 +244,17 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override;
virtual MemberList const& getMembers() const override;
unsigned getFunctionIndex(std::string const& _functionName) const;
private:
ContractDefinition const& m_contract;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
/**
@ -263,7 +300,7 @@ public:
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function);
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
@ -279,7 +316,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
Location getLocation() const { return m_location; }
Location const& getLocation() const { return m_location; }
private:
TypePointers m_parameterTypes;

2
libweb3jsonrpc/WebThreeStubServer.cpp

@ -510,7 +510,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code)
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner());
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str();
}
catch (...)

4
libwebthree/WebThree.cpp

@ -57,11 +57,11 @@ WebThreeDirect::~WebThreeDirect()
// eth::Client (owned by us via a unique_ptr) uses eth::EthereumHost (via a weak_ptr).
// Really need to work out a clean way of organising ownership and guaranteeing startup/shutdown is perfect.
// Have to call quit here to get the Host to kill its io_service otherwise we end up with left-over reads,
// Have to call stop here to get the Host to kill its io_service otherwise we end up with left-over reads,
// still referencing Sessions getting deleted *after* m_ethereum is reset, causing bad things to happen, since
// the guarantee is that m_ethereum is only reset *after* all sessions have ended (sessions are allowed to
// use bits of data owned by m_ethereum).
m_net.quit();
m_net.stop();
m_ethereum.reset();
}

5
libwebthree/WebThree.h

@ -14,7 +14,7 @@
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Client.h
/** @file WebThree.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
@ -92,9 +92,6 @@ public:
/// Connect to a particular peer.
void connect(std::string const& _seedHost, unsigned short _port = 30303);
/// Is the network subsystem up?
bool haveNetwork() { return peerCount() != 0; }
/// Save peers
dev::bytes saveNodes();

14
libwhisper/Common.cpp

@ -54,10 +54,16 @@ bool TopicFilter::matches(Envelope const& _e) const
{
for (TopicMask const& t: m_topicMasks)
{
if (_e.topics().size() == t.size())
for (unsigned i = 0; i < t.size(); ++i)
if (((t[i].first ^ _e.topics()[i]) & t[i].second) != 0)
goto NEXT_TOPICMASK;
for (unsigned i = 0; i < t.size(); ++i)
{
for (auto et: _e.topics())
if (((t[i].first ^ et) & t[i].second) == 0)
goto NEXT_TOPICPART;
// failed to match topicmask against any topics: move on to next mask
goto NEXT_TOPICMASK;
NEXT_TOPICPART:;
}
// all topicmasks matched.
return true;
NEXT_TOPICMASK:;
}

9
libwhisper/Message.cpp

@ -89,6 +89,15 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
return ret;
}
Envelope::Envelope(RLP const& _m)
{
m_expiry = _m[0].toInt<unsigned>();
m_ttl = _m[1].toInt<unsigned>();
m_topic = _m[2].toVector<FixedHash<4>>();
m_data = _m[3].toBytes();
m_nonce = _m[4].toInt<u256>();
}
Message Envelope::open(Secret const& _s) const
{
return Message(*this, _s);

9
libwhisper/Message.h

@ -51,14 +51,7 @@ class Envelope
public:
Envelope() {}
Envelope(RLP const& _m)
{
m_expiry = _m[0].toInt<unsigned>();
m_ttl = _m[1].toInt<unsigned>();
m_topic = _m[2].toVector<FixedHash<4>>();
m_data = _m[3].toBytes();
m_nonce = _m[4].toInt<u256>();
}
Envelope(RLP const& _m);
operator bool() const { return !!m_expiry; }

2
libwhisper/WhisperHost.cpp

@ -68,7 +68,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
return;
UpgradeGuard ll(l);
m_messages[h] = _m;
m_expiryQueue[_m.expiry()] = h;
m_expiryQueue.insert(make_pair(_m.expiry(), h));
}
// if (_p)

2
libwhisper/WhisperHost.h

@ -78,7 +78,7 @@ private:
mutable dev::SharedMutex x_messages;
std::map<h256, Envelope> m_messages;
std::map<unsigned, h256> m_expiryQueue;
std::multimap<unsigned, h256> m_expiryQueue;
mutable dev::Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;

26
libwhisper/WhisperPeer.cpp

@ -82,21 +82,21 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
void WhisperPeer::sendMessages()
{
if (m_unseen.size())
RLPStream amalg;
unsigned msgCount = 0;
{
RLPStream amalg;
unsigned msgCount;
Guard l(x_unseen);
msgCount = m_unseen.size();
while (m_unseen.size())
{
Guard l(x_unseen);
msgCount = m_unseen.size();
while (m_unseen.size())
{
auto p = *m_unseen.begin();
m_unseen.erase(m_unseen.begin());
host()->streamMessage(p.second, amalg);
}
auto p = *m_unseen.begin();
m_unseen.erase(m_unseen.begin());
host()->streamMessage(p.second, amalg);
}
}
if (msgCount)
{
RLPStream s;
prep(s, MessagesPacket, msgCount).appendRaw(amalg.out(), msgCount);
sealAndSend(s);
@ -106,5 +106,5 @@ void WhisperPeer::sendMessages()
void WhisperPeer::noteNewMessage(h256 _h, Message const& _m)
{
Guard l(x_unseen);
m_unseen[rating(_m)] = _h;
m_unseen.insert(make_pair(rating(_m), _h));
}

2
libwhisper/WhisperPeer.h

@ -67,7 +67,7 @@ private:
void noteNewMessage(h256 _h, Message const& _m);
mutable dev::Mutex x_unseen;
std::map<unsigned, h256> m_unseen; ///< Rated according to what they want.
std::multimap<unsigned, h256> m_unseen; ///< Rated according to what they want.
std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now();
};

2
mix/ConstantCompilationModel.cpp

@ -46,7 +46,7 @@ CompilerResult ConstantCompilationModel::compile(QString _code)
catch (dev::Exception const& _exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner());
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler);
res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped();
res.hexCode = "";

2
solc/CMakeLists.txt

@ -8,6 +8,8 @@ set(EXECUTABLE solc)
add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} boost_filesystem)
target_link_libraries(${EXECUTABLE} boost_program_options)
target_link_libraries(${EXECUTABLE} solidity)
install( TARGETS ${EXECUTABLE} DESTINATION bin )

383
solc/CommandLineInterface.cpp

@ -0,0 +1,383 @@
/*
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 Lefteris <lefteris@ethdev.com>
* @date 2014
* Solidity command line interface.
*/
#include "CommandLineInterface.h"
#include <string>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include "BuildInfo.h"
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
namespace po = boost::program_options;
namespace dev
{
namespace solidity
{
static void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
static inline bool argToStdout(po::variables_map const& _args, const char* _name)
{
return _args.count(_name) && _args[_name].as<OutputType>() != OutputType::FILE;
}
static bool needStdout(po::variables_map const& _args)
{
return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") ||
argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary");
}
static inline bool outputToFile(OutputType type)
{
return type == OutputType::FILE || type == OutputType::BOTH;
}
static inline bool outputToStdout(OutputType type)
{
return type == OutputType::STDOUT || type == OutputType::BOTH;
}
static std::istream& operator>>(std::istream& _in, OutputType& io_output)
{
std::string token;
_in >> token;
if (token == "stdout")
io_output = OutputType::STDOUT;
else if (token == "file")
io_output = OutputType::FILE;
else if (token == "both")
io_output = OutputType::BOTH;
else
throw boost::program_options::invalid_option_value(token);
return _in;
}
void CommandLineInterface::handleBinary(string const& _contract)
{
auto choice = m_args["binary"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Binary: " << endl;
cout << toHex(m_compiler.getBytecode(_contract)) << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".binary");
outFile << toHex(m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleOpcode(string const& _contract)
{
// TODO: Figure out why the wrong operator << (from boost) is used here
auto choice = m_args["opcode"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Opcodes: " << endl;
dev::operator<<(cout, m_compiler.getBytecode(_contract));
cout << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".opcode");
dev::operator<<(outFile, m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleBytecode(string const& _contract)
{
if (m_args.count("opcodes"))
handleOpcode(_contract);
if (m_args.count("binary"))
handleBinary(_contract);
}
void CommandLineInterface::handleJson(DocumentationType _type,
string const& _contract)
{
std::string argName;
std::string suffix;
std::string title;
switch(_type)
{
case DocumentationType::ABI_INTERFACE:
argName = "abi";
suffix = ".abi";
title = "Contract ABI";
break;
case DocumentationType::NATSPEC_USER:
argName = "natspec-user";
suffix = ".docuser";
title = "User Documentation";
break;
case DocumentationType::NATSPEC_DEV:
argName = "natspec-dev";
suffix = ".docdev";
title = "Developer Documentation";
break;
default:
// should never happen
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type"));
}
if (m_args.count(argName))
{
auto choice = m_args[argName].as<OutputType>();
if (outputToStdout(choice))
{
cout << title << endl;
cout << m_compiler.getJsonDocumentation(_contract, _type);
}
if (outputToFile(choice))
{
ofstream outFile(_contract + suffix);
outFile << m_compiler.getJsonDocumentation(_contract, _type);
outFile.close();
}
}
}
bool CommandLineInterface::parseArguments(int argc, char** argv)
{
#define OUTPUT_TYPE_STR "Legal values:\n" \
"\tstdout: Print it to standard output\n" \
"\tfile: Print it to a file with same name\n" \
"\tboth: Print both to a file and the stdout\n"
// Declare the supported options.
po::options_description desc("Allowed options");
desc.add_options()
("help", "Show help message and exit")
("version", "Show version and exit")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size")
("input-file", po::value<vector<string>>(), "input file")
("ast", po::value<OutputType>(),
"Request to output the AST of the contract. " OUTPUT_TYPE_STR)
("asm", po::value<OutputType>(),
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR)
("opcodes", po::value<OutputType>(),
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR)
("binary", po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
("abi", po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
("natspec-user", po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
("natspec-dev", po::value<OutputType>(),
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR);
#undef OUTPUT_TYPE_STR
// All positional options should be interpreted as input files
po::positional_options_description p;
p.add("input-file", -1);
// parse the compiler arguments
try
{
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args);
}
catch (po::error const& exception)
{
cout << exception.what() << endl;
return false;
}
po::notify(m_args);
if (m_args.count("help"))
{
cout << desc;
return false;
}
if (m_args.count("version"))
{
version();
return false;
}
return true;
}
bool CommandLineInterface::processInput()
{
if (!m_args.count("input-file"))
{
string s;
while (!cin.eof())
{
getline(cin, s);
m_sourceCodes["<stdin>"].append(s);
}
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
{
auto path = boost::filesystem::path(infile);
if (!boost::filesystem::exists(path))
{
cout << "Skipping non existant input file \"" << infile << "\"" << endl;
continue;
}
if (!boost::filesystem::is_regular_file(path))
{
cout << "\"" << infile << "\" is not a valid file. Skipping" << endl;
continue;
}
m_sourceCodes[infile] = asString(dev::contents(infile));
}
try
{
for (auto const& sourceCode: m_sourceCodes)
m_compiler.addSource(sourceCode.first, sourceCode.second);
// TODO: Perhaps we should not compile unless requested
m_compiler.compile(m_args["optimize"].as<bool>());
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler);
return false;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler);
return false;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler);
return false;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler);
return false;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler);
return false;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return false;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return false;
}
return true;
}
void CommandLineInterface::actOnInput()
{
// do we need AST output?
if (m_args.count("ast"))
{
auto choice = m_args["ast"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Syntax trees:" << endl << endl;
for (auto const& sourceCode: m_sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(cout);
}
}
if (outputToFile(choice))
{
for (auto const& sourceCode: m_sourceCodes)
{
boost::filesystem::path p(sourceCode.first);
ofstream outFile(p.stem().string() + ".ast");
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(outFile);
outFile.close();
}
}
}
vector<string> contracts = m_compiler.getContractNames();
for (string const& contract: contracts)
{
if (needStdout(m_args))
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count("asm"))
{
auto choice = m_args["asm"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
m_compiler.streamAssembly(cout, contract);
}
if (outputToFile(choice))
{
ofstream outFile(contract + ".evm");
m_compiler.streamAssembly(outFile, contract);
outFile.close();
}
}
handleBytecode(contract);
handleJson(DocumentationType::ABI_INTERFACE, contract);
handleJson(DocumentationType::NATSPEC_DEV, contract);
handleJson(DocumentationType::NATSPEC_USER, contract);
} // end of contracts iteration
}
}
}

71
solc/CommandLineInterface.h

@ -0,0 +1,71 @@
/*
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 Lefteris <lefteris@ethdev.com>
* @date 2014
* Solidity command line interface.
*/
#pragma once
#include <boost/program_options.hpp>
#include <libsolidity/CompilerStack.h>
namespace dev
{
namespace solidity
{
//forward declaration
enum class DocumentationType: uint8_t;
enum class OutputType: uint8_t
{
STDOUT,
FILE,
BOTH
};
class CommandLineInterface
{
public:
CommandLineInterface() {}
/// Parse command line arguments and return false if we should not continue
bool parseArguments(int argc, char** argv);
/// Parse the files and create source code objects
bool processInput();
/// Perform actions on the input depending on provided compiler arguments
void actOnInput();
private:
void handleBinary(std::string const& _contract);
void handleOpcode(std::string const& _contract);
void handleBytecode(std::string const& _contract);
void handleJson(DocumentationType _type,
std::string const& _contract);
/// Compiler arguments variable map
boost::program_options::variables_map m_args;
/// map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes;
/// Solidity compiler stack
dev::solidity::CompilerStack m_compiler;
};
}
}

120
solc/main.cpp

@ -20,122 +20,16 @@
* Solidity commandline compiler.
*/
#include <string>
#include <iostream>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
using namespace dev;
using namespace solidity;
void help()
{
cout << "Usage solc [OPTIONS] <file>" << endl
<< "Options:" << endl
<< " -o,--optimize Optimize the bytecode for size." << endl
<< " -h,--help Show this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl;
exit(0);
}
void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
#include "CommandLineInterface.h"
int main(int argc, char** argv)
{
string infile;
bool optimize = false;
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
if (arg == "-o" || arg == "--optimize")
optimize = true;
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
else
infile = argv[i];
}
string sourceCode;
if (infile.empty())
{
string s;
while (!cin.eof())
{
getline(cin, s);
sourceCode.append(s);
}
}
else
sourceCode = asString(dev::contents(infile));
CompilerStack compiler;
try
{
compiler.compile(sourceCode, optimize);
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler.getScanner());
return -1;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler.getScanner());
return -1;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler.getScanner());
return -1;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler.getScanner());
return -1;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler.getScanner());
return -1;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return -1;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return -1;
}
cout << "Syntax tree for the contract:" << endl;
ASTPrinter printer(compiler.getAST(), sourceCode);
printer.print(cout);
cout << "EVM assembly:" << endl;
compiler.streamAssembly(cout);
cout << "Opcodes:" << endl;
cout << eth::disassemble(compiler.getBytecode()) << endl;
cout << "Binary: " << toHex(compiler.getBytecode()) << endl;
cout << "Interface specification: " << compiler.getInterface() << endl;
dev::solidity::CommandLineInterface cli;
if (!cli.parseArguments(argc, argv))
return 1;
if (!cli.processInput())
return 1;
cli.actOnInput();
return 0;
}

20
test/TestHelper.cpp

@ -72,7 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o
if (!isFiller)
{
importState(_o["post"].get_obj(), m_statePost);
m_environment.sub.logs = importLog(_o["logs"].get_obj());
m_environment.sub.logs = importLog(_o["logs"].get_array());
}
}
@ -259,16 +259,17 @@ bytes importCode(json_spirit::mObject& _o)
return code;
}
LogEntries importLog(json_spirit::mObject& _o)
LogEntries importLog(json_spirit::mArray& _a)
{
LogEntries logEntries;
for (auto const& l: _o)
for (auto const& l: _a)
{
json_spirit::mObject o = l.second.get_obj();
json_spirit::mObject o = l.get_obj();
// cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert(o.count("address") > 0);
assert(o.count("topics") > 0);
assert(o.count("data") > 0);
assert(o.count("bloom") > 0);
LogEntry log;
log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array())
@ -279,9 +280,9 @@ LogEntries importLog(json_spirit::mObject& _o)
return logEntries;
}
json_spirit::mObject exportLog(eth::LogEntries _logs)
json_spirit::mArray exportLog(eth::LogEntries _logs)
{
json_spirit::mObject ret;
json_spirit::mArray ret;
if (_logs.size() == 0) return ret;
for (LogEntry const& l: _logs)
{
@ -292,7 +293,8 @@ json_spirit::mObject exportLog(eth::LogEntries _logs)
topics.push_back(toString(t));
o["topics"] = topics;
o["data"] = "0x" + toHex(l.data);
ret[toString(l.bloom())] = o;
o["bloom"] = toString(l.bloom());
ret.push_back(o);
}
return ret;
}
@ -327,6 +329,10 @@ void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore,
BOOST_CHECK_MESSAGE(expectedStoreValue == resultStoreValue, _expectedAddr << ": store[" << expectedStoreKey << "] = " << resultStoreValue << ", expected " << expectedStoreValue);
}
}
for (auto&& resultStorePair : _resultStore)
if (!_expectedStore.count(resultStorePair.first))
BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first);
}
void checkLog(LogEntries _resultLogs, LogEntries _expectedLogs)

4
test/TestHelper.h

@ -68,8 +68,8 @@ u256 toInt(json_spirit::mValue const& _v);
byte toByte(json_spirit::mValue const& _v);
bytes importCode(json_spirit::mObject& _o);
bytes importData(json_spirit::mObject& _o);
eth::LogEntries importLog(json_spirit::mObject& _o);
json_spirit::mObject exportLog(eth::LogEntries _logs);
eth::LogEntries importLog(json_spirit::mArray& _o);
json_spirit::mArray exportLog(eth::LogEntries _logs);
void checkOutput(bytes const& _output, json_spirit::mObject& _o);
void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr);
void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);

10
test/createRandomTest.cpp

@ -32,7 +32,7 @@
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libevmcore/Instruction.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include "vm.h"
using namespace std;
@ -142,14 +142,14 @@ void doMyTests(json_spirit::mValue& v)
}
bytes output;
eth::VM vm(fev.gas);
auto vm = eth::VMFactory::create(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
}
catch (eth::VMException const& _e)
{
@ -189,7 +189,7 @@ void doMyTests(json_spirit::mValue& v)
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(test::exportLog(fev.sub.logs));
o["logs"] = test::exportLog(fev.sub.logs);
}
}
}

8
test/crypto.cpp

@ -44,6 +44,14 @@ static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1());
static CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> s_params(s_curveOID);
static CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP>::EllipticCurve s_curve(s_params.GetCurve());
BOOST_AUTO_TEST_CASE(cryptopp_patch)
{
KeyPair k = KeyPair::create();
bytes io_text;
s_secp256k1.decrypt(k.sec(), io_text);
BOOST_REQUIRE_EQUAL(io_text.size(), 0);
}
BOOST_AUTO_TEST_CASE(verify_secert)
{
h256 empty;

2
test/genesis.cpp

@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(genesis_tests)
BOOST_CHECK_EQUAL(BlockChain::genesis().stateRoot, h256(o["genesis_state_root"].get_str()));
BOOST_CHECK_EQUAL(toHex(BlockChain::createGenesisBlock()), toHex(fromHex(o["genesis_rlp_hex"].get_str())));
BOOST_CHECK_EQUAL(sha3(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str()));
BOOST_CHECK_EQUAL(BlockInfo::headerHash(BlockChain::createGenesisBlock()), h256(o["genesis_hash"].get_str()));
}
BOOST_AUTO_TEST_SUITE_END()

4
test/jsonrpc.cpp

@ -19,7 +19,7 @@
* @date 2014
*/
#if ETH_JSONRPC
#if ETH_JSONRPC && 0
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
@ -43,7 +43,7 @@ using namespace dev;
using namespace dev::eth;
namespace js = json_spirit;
WebThreeDirect *web3;
WebThreeDirect* web3;
unique_ptr<WebThreeStubServer> jsonrpcServer;
unique_ptr<WebThreeStubClient> jsonrpcClient;

43
test/solidityCompiler.cpp

@ -46,16 +46,23 @@ namespace
bytes compileContract(const string& _sourceCode)
{
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
Compiler compiler;
compiler.compileContract(*contract, {});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
Compiler compiler;
compiler.compileContract(*contract, {});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();
}
BOOST_FAIL("No contract found in source.");
return bytes();
}
/// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation.
@ -80,7 +87,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
"}\n";
bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 42;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2,
@ -100,8 +107,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 70;
unsigned boilerplateSize = 83;
unsigned shift = 68;
unsigned boilerplateSize = 81;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize return variable d
byte(Instruction::DUP3),
@ -118,8 +125,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
byte(Instruction::JUMP), // end of f
byte(Instruction::JUMPDEST), // beginning of g
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), // initialized e and h
byte(Instruction::PUSH1), byte(0x29 + shift), // ret address
byte(Instruction::PUSH1), 0x0, // initialized e and h
byte(Instruction::PUSH1), byte(0x2a + shift), // ret address
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
@ -151,8 +158,8 @@ BOOST_AUTO_TEST_CASE(ifStatement)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 29;
unsigned boilerplateSize = 42;
unsigned shift = 27;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
@ -193,8 +200,8 @@ BOOST_AUTO_TEST_CASE(loops)
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 29;
unsigned boilerplateSize = 42;
unsigned shift = 27;
unsigned boilerplateSize = 40;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1,

343
test/solidityEndToEndTest.cpp

@ -24,132 +24,18 @@
#include <string>
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
#include <libdevcrypto/SHA3.h>
#include <test/solidityExecutionFramework.h>
using namespace std;
namespace dev
{
/// Provides additional overloads for toBigEndian to encode arguments and return values.
inline bytes toBigEndian(byte _value) { return bytes({_value}); }
inline bytes toBigEndian(bool _value) { return bytes({byte(_value)}); }
namespace solidity
{
namespace test
{
class ExecutionFramework
{
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0)
{
bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode);
sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false, _value);
return m_output;
}
template <class... Args>
bytes const& callContractFunction(byte _index, Args const&... _arguments)
{
return callContractFunction(_index, argsToBigEndian(_arguments...));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCpp(byte _index, CppFunction const& _cppFunction, Args const&... _arguments)
{
bytes solidityResult = callContractFunction(_index, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(byte _index, CppFunction const& _cppFunction,
u256 const& _rangeStart, u256 const& _rangeEnd)
{
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{
bytes solidityResult = callContractFunction(_index, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) +
"\nArgument: " + toHex(toBigEndian(argument)));
}
}
private:
template <class FirstArg, class... Args>
bytes argsToBigEndian(FirstArg const& _firstArg, Args const&... _followingArgs) const
{
return toBigEndian(_firstArg) + argsToBigEndian(_followingArgs...);
}
bytes argsToBigEndian() const { return bytes(); }
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename enable_if<is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
_cppFunction(_arguments...);
return bytes();
}
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)
-> typename enable_if<!is_void<decltype(_cppFunction(_arguments...))>::value, bytes>::type
{
return toBigEndian(_cppFunction(_arguments...));
}
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
eth::Executive executive(m_state);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp();
try
{
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
executive.setup(&transactionRLP);
}
catch (...) {}
if (_isCreation)
{
BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address()));
m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
}
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address()));
}
BOOST_REQUIRE(executive.go());
executive.finalize();
m_output = executive.out().toVector();
}
protected:
Address m_contractAddress;
eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 1000000;
bytes m_output;
};
BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework)
BOOST_AUTO_TEST_CASE(smoke_test)
@ -477,6 +363,48 @@ BOOST_AUTO_TEST_CASE(small_signed_types)
testSolidityAgainstCpp(0, small_signed_types_cpp);
}
BOOST_AUTO_TEST_CASE(strings)
{
char const* sourceCode = "contract test {\n"
" function fixed() returns(string32 ret) {\n"
" return \"abc\\x00\\xff__\";\n"
" }\n"
" function pipeThrough(string2 small, bool one) returns(string16 large, bool oneRet) {\n"
" oneRet = one;\n"
" large = small;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
bytes expectation(32, 0);
expectation[0] = byte('a');
expectation[1] = byte('b');
expectation[2] = byte('c');
expectation[3] = byte(0);
expectation[4] = byte(0xff);
expectation[5] = byte('_');
expectation[6] = byte('_');
BOOST_CHECK(callContractFunction(0, bytes()) == expectation);
expectation = bytes(17, 0);
expectation[0] = 0;
expectation[1] = 2;
expectation[16] = 1;
BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation);
}
BOOST_AUTO_TEST_CASE(empty_string_on_stack)
{
char const* sourceCode = "contract test {\n"
" function run(string0 empty, uint8 inp) returns(uint16 a, string0 b, string4 c) {\n"
" var x = \"abc\";\n"
" var y = \"\";\n"
" var z = inp;\n"
" a = z; b = y; c = x;"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes({0x02})) == bytes({0x00, 0x02, 'a', 'b', 'c', 0x00}));
}
BOOST_AUTO_TEST_CASE(state_smoke_test)
{
char const* sourceCode = "contract test {\n"
@ -898,6 +826,191 @@ BOOST_AUTO_TEST_CASE(ecrecover)
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls)
{
char const* sourceCode = R"(
contract Helper {
function multiply(uint a, uint b) returns (uint c) {
return a * b;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint c) {
return h.multiply(a, b);
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters)
{
char const* sourceCode = R"(
contract Helper {
function sel(uint a, bool select, uint b) returns (uint c) {
if (select) return a; else return b;
}
}
contract Main {
Helper h;
function callHelper(uint a, bool select, uint b) returns (uint c) {
return h.sel(a, select, b) * 3;
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3));
BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this)
{
char const* sourceCode = R"(
contract Helper {
function getAddress() returns (address addr) {
return address(this);
}
}
contract Main {
Helper h;
function callHelper() returns (address addr) {
return h.getAddress();
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress));
}
BOOST_AUTO_TEST_CASE(calls_to_this)
{
char const* sourceCode = R"(
contract Helper {
function invoke(uint a, uint b) returns (uint c) {
return this.multiply(a, b, 10);
}
function multiply(uint a, uint b, uint8 c) returns (uint ret) {
return a * b + c;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint ret) {
return h.invoke(a, b);
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
{
// note that a reference to another contract's function occupies two stack slots,
// so this tests correct stack slot allocation
char const* sourceCode = R"(
contract Helper {
function multiply(uint a, uint b) returns (uint c) {
return a * b;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint c) {
var fu = h.multiply;
var y = 9;
var ret = fu(a, b);
return ret + y;
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9));
}
BOOST_AUTO_TEST_CASE(strings_in_calls)
{
char const* sourceCode = R"(
contract Helper {
function invoke(string3 x, bool stop) returns (string4 ret) {
return x;
}
}
contract Main {
Helper h;
function callHelper(string2 x, bool stop) returns (string5 ret) {
return h.invoke(x, stop);
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
}
BOOST_AUTO_TEST_SUITE_END()
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save