Browse Source

Merge remote-tracking branch 'origin/vm' into develop-evmcc

Conflicts:
	libethereum/Executive.cpp
	libethereum/Executive.h
	libethereum/State.cpp
	libevm/VM.cpp
	libevm/VM.h
	libevm/VMFace.h
	test/createRandomTest.cpp
	test/vm.cpp
	windows/LibEthereum.vcxproj.filters
cl-refactor
Paweł Bylica 10 years ago
parent
commit
799c8f95de
  1. 2
      alethzero/MainWin.cpp
  2. 1
      alethzero/MainWin.h
  3. 6
      libdevcrypto/CryptoPP.cpp
  4. 2
      libethereum/Client.cpp
  5. 18
      libethereum/Executive.cpp
  6. 14
      libethereum/Executive.h
  7. 23
      libethereum/State.cpp
  8. 11
      libethereum/State.h
  9. 28
      libethereum/VMFactory.cpp
  10. 28
      libethereum/VMFactory.h
  11. 2
      libevm/ExtVMFace.h
  12. 4
      libevm/VM.cpp
  13. 86
      libevm/VM.h
  14. 4
      libevm/VMFace.h
  15. 45
      libevm/VMFactory.cpp
  16. 27
      libevm/VMFactory.h
  17. 2
      libp2p/Common.h
  18. 526
      libp2p/Host.cpp
  19. 45
      libp2p/Host.h
  20. 187
      libp2p/Network.cpp
  21. 63
      libp2p/Network.h
  22. 6
      libp2p/Session.cpp
  23. 282
      libsolidity/AST.cpp
  24. 160
      libsolidity/AST.h
  25. 2
      libsolidity/ASTForward.h
  26. 131
      libsolidity/ASTPrinter.cpp
  27. 129
      libsolidity/ASTPrinter.h
  28. 76
      libsolidity/ASTVisitor.h
  29. 493
      libsolidity/AST_accept.h
  30. 10
      libsolidity/BaseTypes.h
  31. 67
      libsolidity/Compiler.cpp
  32. 20
      libsolidity/Compiler.h
  33. 30
      libsolidity/CompilerContext.cpp
  34. 13
      libsolidity/CompilerContext.h
  35. 186
      libsolidity/CompilerStack.cpp
  36. 79
      libsolidity/CompilerStack.h
  37. 71
      libsolidity/CompilerUtils.cpp
  38. 63
      libsolidity/CompilerUtils.h
  39. 6
      libsolidity/DeclarationContainer.cpp
  40. 15
      libsolidity/DeclarationContainer.h
  41. 1
      libsolidity/Exceptions.h
  42. 245
      libsolidity/ExpressionCompiler.cpp
  43. 38
      libsolidity/ExpressionCompiler.h
  44. 9
      libsolidity/GlobalContext.cpp
  45. 10
      libsolidity/GlobalContext.h
  46. 52
      libsolidity/InterfaceHandler.cpp
  47. 28
      libsolidity/InterfaceHandler.h
  48. 31
      libsolidity/NameAndTypeResolver.cpp
  49. 18
      libsolidity/NameAndTypeResolver.h
  50. 86
      libsolidity/Parser.cpp
  51. 4
      libsolidity/Parser.h
  52. 11
      libsolidity/Scanner.cpp
  53. 14
      libsolidity/Scanner.h
  54. 41
      libsolidity/SourceReferenceFormatter.cpp
  55. 4
      libsolidity/SourceReferenceFormatter.h
  56. 69
      libsolidity/Types.cpp
  57. 23
      libsolidity/Types.h
  58. 2
      libweb3jsonrpc/WebThreeStubServer.cpp
  59. 4
      libwebthree/WebThree.cpp
  60. 5
      libwebthree/WebThree.h
  61. 9
      libwhisper/Message.cpp
  62. 9
      libwhisper/Message.h
  63. 2
      libwhisper/WhisperHost.cpp
  64. 2
      libwhisper/WhisperHost.h
  65. 26
      libwhisper/WhisperPeer.cpp
  66. 2
      libwhisper/WhisperPeer.h
  67. 2
      mix/ConstantCompilationModel.cpp
  68. 2
      solc/CMakeLists.txt
  69. 368
      solc/CommandLineInterface.cpp
  70. 71
      solc/CommandLineInterface.h
  71. 122
      solc/main.cpp
  72. 16
      test/TestHelper.cpp
  73. 4
      test/TestHelper.h
  74. 15
      test/createRandomTest.cpp
  75. 8
      test/crypto.cpp
  76. 29
      test/solidityCompiler.cpp
  77. 170
      test/solidityEndToEndTest.cpp
  78. 47
      test/solidityExpressionCompiler.cpp
  79. 2
      test/solidityJSONInterfaceTest.cpp
  80. 8
      test/solidityNameAndTypeResolution.cpp
  81. 4
      test/solidityNatspecJSON.cpp
  82. 55
      test/solidityParser.cpp
  83. 1851
      test/stLogTestsFiller.json
  84. 35
      test/stPreCompiledContractsFiller.json
  85. 80
      test/stSystemOperationsTestFiller.json
  86. 5
      test/state.cpp
  87. 86
      test/vm.cpp
  88. 2
      test/vm.h
  89. 140
      test/vmEnvironmentalInfoTestFiller.json
  90. 28
      test/vmLogTestFiller.json
  91. 9
      windows/LibEthereum.vcxproj.filters

2
alethzero/MainWin.cpp

@ -1626,7 +1626,7 @@ void Main::on_data_textChanged()
catch (dev::Exception const& exception) catch (dev::Exception const& exception)
{ {
ostringstream error; 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>"; solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
} }
catch (...) catch (...)

1
alethzero/MainWin.h

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

6
libdevcrypto/CryptoPP.cpp

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

2
libethereum/Client.cpp

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

18
libethereum/Executive.cpp

@ -21,6 +21,7 @@
#include <boost/timer.hpp> #include <boost/timer.hpp>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libevm/VMFactory.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include "Interface.h" #include "Interface.h"
#include "Executive.h" #include "Executive.h"
@ -32,13 +33,6 @@ using namespace dev::eth;
#define ETH_VMTRACE 1 #define ETH_VMTRACE 1
Executive::~Executive()
{
// TODO: Make safe.
delete m_ext;
delete m_vm;
}
u256 Executive::gasUsed() const u256 Executive::gasUsed() const
{ {
return m_t.gas() - m_endGas; return m_t.gas() - m_endGas;
@ -112,9 +106,9 @@ bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _valu
if (m_s.addressHasCode(_receiveAddress)) if (m_s.addressHasCode(_receiveAddress))
{ {
m_vm = VMFactory::create(VMFactory::Interpreter, _gas).release(); m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_receiveAddress); bytes const& c = m_s.code(_receiveAddress);
m_ext = new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms); m_ext.reset(new ExtVM(m_s, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_ms));
} }
else else
m_endGas = _gas; m_endGas = _gas;
@ -131,8 +125,8 @@ 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); m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress) + _endowment, Account::ContractConception);
// Execute _init. // Execute _init.
m_vm = VMFactory::create(VMFactory::Interpreter, _gas).release(); m_vm = VMFactory::create(_gas);
m_ext = new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms); m_ext.reset(new ExtVM(m_s, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_ms));
return _init.empty(); return _init.empty();
} }
@ -141,7 +135,7 @@ 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, void* voidVM, void const* voidExt)
{ {
ExtVM const& ext = *(ExtVM const*)voidExt; ExtVM const& ext = *(ExtVM const*)voidExt;
VM& vm = *(VM*)voidVM; // TODO: Ok for now, because only interpeter/VM supports OnOp callback, but safer solution would be nice VM& vm = *(VM*)voidVM;
ostringstream o; ostringstream o;
o << endl << " STACK" << endl; o << endl << " STACK" << endl;

14
libethereum/Executive.h

@ -25,17 +25,17 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libethcore/CommonEth.h> #include <libethcore/CommonEth.h>
#include <libevm/VMFace.h>
#include "Transaction.h" #include "Transaction.h"
#include "Manifest.h" #include "ExtVM.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
class VMFace;
class ExtVM;
class State; class State;
struct Manifest;
struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; };
@ -43,7 +43,9 @@ class Executive
{ {
public: public:
Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {} Executive(State& _s, Manifest* o_ms = nullptr): m_s(_s), m_ms(o_ms) {}
~Executive(); ~Executive() = default;
Executive(Executive const&) = delete;
void operator=(Executive) = delete;
bool setup(bytesConstRef _transaction); bool setup(bytesConstRef _transaction);
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
@ -68,8 +70,8 @@ public:
private: private:
State& m_s; State& m_s;
ExtVM* m_ext = nullptr; // TODO: make safe. std::unique_ptr<ExtVM> m_ext;
VMFace* m_vm = nullptr; std::unique_ptr<VMFace> m_vm;
Manifest* m_ms = nullptr; Manifest* m_ms = nullptr;
bytesConstRef m_out; bytesConstRef m_out;
Address m_newAddress; Address m_newAddress;

23
libethereum/State.cpp

@ -29,10 +29,11 @@
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libevm/VM.h> #include <libevm/VMFactory.h>
#include "BlockChain.h" #include "BlockChain.h"
#include "Defaults.h" #include "Defaults.h"
#include "ExtVM.h" #include "ExtVM.h"
#include "Executive.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -171,8 +172,7 @@ State::State(State const& _s):
m_previousBlock(_s.m_previousBlock), m_previousBlock(_s.m_previousBlock),
m_currentBlock(_s.m_currentBlock), m_currentBlock(_s.m_currentBlock),
m_ourAddress(_s.m_ourAddress), m_ourAddress(_s.m_ourAddress),
m_blockReward(_s.m_blockReward), m_blockReward(_s.m_blockReward)
m_vmKind(_s.m_vmKind)
{ {
paranoia("after state cloning (copy cons).", true); paranoia("after state cloning (copy cons).", true);
} }
@ -206,7 +206,6 @@ State& State::operator=(State const& _s)
m_ourAddress = _s.m_ourAddress; m_ourAddress = _s.m_ourAddress;
m_blockReward = _s.m_blockReward; m_blockReward = _s.m_blockReward;
m_lastTx = _s.m_lastTx; m_lastTx = _s.m_lastTx;
m_vmKind = _s.m_vmKind;
paranoia("after state cloning (assignment op)", true); paranoia("after state cloning (assignment op)", true);
return *this; return *this;
} }
@ -1103,7 +1102,7 @@ bool State::isTrieGood(bool _enforceRefs, bool _requireNoLeftOvers) const
return false; return false;
} }
} }
catch (InvalidTrie) catch (InvalidTrie const&)
{ {
cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]"; cwarn << "BAD TRIE" << (e ? "[enforced" : "[unenforced") << "refs]";
cnote << m_db.keys(); cnote << m_db.keys();
@ -1215,20 +1214,19 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
} }
else if (addressHasCode(_codeAddress)) else if (addressHasCode(_codeAddress))
{ {
auto vmObj = VMFactory::create(getVMKind(), *_gas); auto vm = VMFactory::create(*_gas);
auto& vm = *vmObj;
ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level); ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_codeAddress), o_ms, _level);
bool revert = false; bool revert = false;
try try
{ {
auto out = vm.go(evm, _onOp); auto out = vm->go(evm, _onOp);
memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size()));
if (o_sub) if (o_sub)
*o_sub += evm.sub; *o_sub += evm.sub;
if (o_ms) if (o_ms)
o_ms->output = out.toBytes(); o_ms->output = out.toBytes();
*_gas = vm.gas(); *_gas = vm->gas();
} }
catch (VMException const& _e) catch (VMException const& _e)
{ {
@ -1275,20 +1273,19 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception); m_cache[newAddress] = Account(balance(newAddress) + _endowment, Account::ContractConception);
// Execute init code. // Execute init code.
auto vmObj = VMFactory::create(getVMKind(), *_gas); auto vm = VMFactory::create(*_gas);
auto& vm = *vmObj;
ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level);
bool revert = false; bool revert = false;
bytesConstRef out; bytesConstRef out;
try try
{ {
out = vm.go(evm, _onOp); out = vm->go(evm, _onOp);
if (o_ms) if (o_ms)
o_ms->output = out.toBytes(); o_ms->output = out.toBytes();
if (o_sub) if (o_sub)
*o_sub += evm.sub; *o_sub += evm.sub;
*_gas = vm.gas(); *_gas = vm->gas();
} }
catch (VMException const& _e) catch (VMException const& _e)
{ {

11
libethereum/State.h

@ -30,7 +30,6 @@
#include <libethcore/Exceptions.h> #include <libethcore/Exceptions.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethereum/VMFactory.h>
#include <libevm/FeeStructure.h> #include <libevm/FeeStructure.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include <libevm/VMFace.h> #include <libevm/VMFace.h>
@ -38,9 +37,7 @@
#include "Account.h" #include "Account.h"
#include "Transaction.h" #include "Transaction.h"
#include "TransactionReceipt.h" #include "TransactionReceipt.h"
#include "Executive.h"
#include "AccountDiff.h" #include "AccountDiff.h"
#include "VMFactory.h"
namespace dev namespace dev
{ {
@ -262,12 +259,6 @@ public:
/// the block since all state changes are ultimately reversed. /// the block since all state changes are ultimately reversed.
void cleanup(bool _fullCommit); void cleanup(bool _fullCommit);
/// Sets VM kind to be used by the state
void setVMKind(VMFactory::Kind _kind) { m_vmKind = _kind; }
/// Get the kind of VM used by the state
VMFactory::Kind getVMKind() const { return m_vmKind; }
private: private:
/// Undo the changes to the state for committing to mine. /// Undo the changes to the state for committing to mine.
void uncommitToMine(); void uncommitToMine();
@ -335,8 +326,6 @@ private:
u256 m_blockReward; u256 m_blockReward;
VMFactory::Kind m_vmKind = VMFactory::Interpreter; ///< The kind of VM used by the state
static std::string c_defaultPath; static std::string c_defaultPath;
static const std::map<unsigned, PrecompiledAddress> c_precompiled; static const std::map<unsigned, PrecompiledAddress> c_precompiled;

28
libethereum/VMFactory.cpp

@ -1,28 +0,0 @@
#include <libevm/VM.h>
#if ETH_EVMJIT
#include<evmjit/libevmjit-cpp/JitVM.h>
#endif
#include "VMFactory.h"
namespace dev
{
namespace eth
{
std::unique_ptr<VMFace> VMFactory::create(VMFactory::Kind _kind, u256 _gas)
{
#if ETH_EVMJIT
auto vm = _kind == Kind::JIT ? static_cast<VMFace*>(new JitVM)
: static_cast<VMFace*>(new VM);
#else
VMFace* vm = new VM;
#endif
vm->reset(_gas);
return std::unique_ptr<VMFace>(vm);
}
}
}

28
libethereum/VMFactory.h

@ -1,28 +0,0 @@
#pragma once
#include <libevm/VMFace.h>
namespace dev
{
namespace eth
{
/**
*/
class VMFactory
{
public:
enum Kind: bool {
Interpreter,
#if ETH_EVMJIT
JIT
#endif
};
static std::unique_ptr<VMFace> create(Kind, u256 _gas = 0);
};
}
}

2
libevm/ExtVMFace.h

@ -150,7 +150,7 @@ public:
BlockInfo previousBlock; ///< The previous block's information. BlockInfo previousBlock; ///< The previous block's information.
BlockInfo currentBlock; ///< The current block's information. BlockInfo currentBlock; ///< The current block's information.
SubState sub; ///< Sub-band VM state (suicides, refund counter, logs). 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/VM.cpp

@ -35,7 +35,7 @@ void VM::reset(u256 _gas) noexcept
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
if (auto defaultExt = dynamic_cast<ExtVM*>(&_ext)) if (auto defaultExt = dynamic_cast<ExtVM*>(&_ext))
return go<ExtVM>(*defaultExt, _onOp, _steps); return goImpl<ExtVM>(*defaultExt, _onOp, _steps);
else else
return go<ExtVMFace>(_ext, _onOp, _steps); return goImpl<ExtVMFace>(_ext, _onOp, _steps);
} }

86
libevm/VM.h

@ -27,17 +27,14 @@
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include "VMFace.h"
#include "FeeStructure.h" #include "FeeStructure.h"
#include "ExtVMFace.h" #include "VMFace.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
class VMFactory;
// Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // 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. // Currently we just pull out the right (low-order in BE) 160-bits.
inline Address asAddress(u256 _item) inline Address asAddress(u256 _item)
@ -52,7 +49,7 @@ inline u256 fromAddress(Address _a)
/** /**
*/ */
class VM : public VMFace class VM: public VMFace
{ {
public: public:
virtual void reset(u256 _gas = 0) noexcept override final; virtual void reset(u256 _gas = 0) noexcept override final;
@ -61,17 +58,20 @@ public:
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 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); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; } u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; } bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; } u256s const& stack() const { return m_stack; }
private: private:
friend VMFactory; friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas = 0): VMFace(_gas) {} explicit VM(u256 _gas = 0): VMFace(_gas) {}
template <class Ext> template <class Ext>
bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps); bytesConstRef goImpl(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1);
u256 m_curPC = 0; u256 m_curPC = 0;
bytes m_temp; bytes m_temp;
@ -83,7 +83,7 @@ private:
} }
// INLINE: // INLINE:
template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) template <class Ext> dev::bytesConstRef dev::eth::VM::goImpl(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; auto memNeed = [](dev::u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
@ -518,12 +518,12 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break; break;
case Instruction::CALLDATALOAD: case Instruction::CALLDATALOAD:
{ {
if ((unsigned)m_stack.back() + 31 < _ext.data.size()) if ((unsigned)m_stack.back() + (uint64_t)31 < _ext.data.size())
m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back()); m_stack.back() = (u256)*(h256 const*)(_ext.data.data() + (unsigned)m_stack.back());
else else
{ {
h256 r; h256 r;
for (unsigned i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + 32, j = 0; i < e; ++i, ++j) for (uint64_t i = (unsigned)m_stack.back(), e = (unsigned)m_stack.back() + (uint64_t)32, j = 0; i < e; ++i, ++j)
r[j] = i < _ext.data.size() ? _ext.data[i] : 0; r[j] = i < _ext.data.size() ? _ext.data[i] : 0;
m_stack.back() = (u256)r; m_stack.back() = (u256)r;
} }
@ -532,51 +532,49 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::CALLDATASIZE: case Instruction::CALLDATASIZE:
m_stack.push_back(_ext.data.size()); m_stack.push_back(_ext.data.size());
break; break;
case Instruction::CALLDATACOPY:
{
unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back();
u256 cf = m_stack.back();
m_stack.pop_back();
unsigned l = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned el = cf + l > (u256)_ext.data.size() ? (u256)_ext.data.size() < cf ? 0 : _ext.data.size() - (unsigned)cf : l;
memcpy(m_temp.data() + mf, _ext.data.data() + (unsigned)cf, el);
memset(m_temp.data() + mf + el, 0, l - el);
break;
}
case Instruction::CODESIZE: case Instruction::CODESIZE:
m_stack.push_back(_ext.code.size()); m_stack.push_back(_ext.code.size());
break; break;
case Instruction::CODECOPY:
{
unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back();
u256 cf = (u256)m_stack.back();
m_stack.pop_back();
unsigned l = (unsigned)m_stack.back();
m_stack.pop_back();
unsigned el = cf + l > (u256)_ext.code.size() ? (u256)_ext.code.size() < cf ? 0 : _ext.code.size() - (unsigned)cf : l;
memcpy(m_temp.data() + mf, _ext.code.data() + (unsigned)cf, el);
memset(m_temp.data() + mf + el, 0, l - el);
break;
}
case Instruction::EXTCODESIZE: case Instruction::EXTCODESIZE:
m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size(); m_stack.back() = _ext.codeAt(asAddress(m_stack.back())).size();
break; break;
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::EXTCODECOPY: case Instruction::EXTCODECOPY:
{ {
Address a = asAddress(m_stack.back()); Address a;
m_stack.pop_back(); if (inst == Instruction::EXTCODECOPY)
unsigned mf = (unsigned)m_stack.back(); {
a = asAddress(m_stack.back());
m_stack.pop_back();
}
unsigned offset = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
u256 cf = m_stack.back(); u256 index = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned l = (unsigned)m_stack.back(); unsigned size = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned el = cf + l > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - (unsigned)cf : l; unsigned sizeToBeCopied;
memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + (unsigned)cf, el); switch(inst)
memset(m_temp.data() + mf + el, 0, l - el); {
case Instruction::CALLDATACOPY:
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:
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:
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;
}
memset(m_temp.data() + offset + sizeToBeCopied, 0, size - sizeToBeCopied);
break; break;
} }
case Instruction::GASPRICE: case Instruction::GASPRICE:

4
libevm/VMFace.h

@ -32,7 +32,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {}; struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {}; struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {}; struct OutOfGas: virtual VMException {};
struct StackTooSmall: virtual public VMException {}; struct StackTooSmall: virtual VMException {};
/** /**
*/ */
@ -47,7 +47,7 @@ public:
VMFace(VMFace const&) = delete; VMFace(VMFace const&) = delete;
void operator=(VMFace const&) = delete; void operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept; virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; }
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;

45
libevm/VMFactory.cpp

@ -0,0 +1,45 @@
/*
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));
}
}
}

27
libevm/VMFace.cpp → libevm/VMFactory.h

@ -14,14 +14,31 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#include "VMFace.h" #include "VMFace.h"
#include "VM.h"
using namespace dev; namespace dev
using namespace dev::eth; {
namespace eth
{
void VMFace::reset(u256 _gas) noexcept enum class VMKind: bool
{ {
m_gas = _gas; Interpreter,
JIT
};
class VMFactory
{
public:
VMFactory() = delete;
static std::unique_ptr<VMFace> create(u256 _gas);
static void setKind(VMKind _kind);
};
}
} }

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 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 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 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 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 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; }; 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/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file Host.cpp /** @file Host.cpp
* @authors: * @author Alex Leverington <nessence@gmail.com>
* Gav Wood <i@gavwood.com> * @author Gav Wood <i@gavwood.com>
* Eric Lombrozo <elombrozo@gmail.com> (Windows version of populateAddresses())
* @date 2014 * @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 <set>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
@ -43,166 +32,18 @@
#include "Common.h" #include "Common.h"
#include "Capability.h" #include "Capability.h"
#include "UPnP.h" #include "UPnP.h"
#include "Host.h"
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; 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): Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool _start):
Worker("p2p", 0), Worker("p2p", 0),
m_clientVersion(_clientVersion), m_clientVersion(_clientVersion),
m_netPrefs(_n), m_netPrefs(_n),
m_ifAddresses(getInterfaceAddresses()), m_ifAddresses(Network::getInterfaceAddresses()),
m_ioService(new ba::io_service(2)), m_ioService(2),
m_acceptor(new bi::tcp::acceptor(*m_ioService)), m_acceptorV4(m_ioService),
m_socket(new bi::tcp::socket(*m_ioService)),
m_key(KeyPair::create()) m_key(KeyPair::create())
{ {
for (auto address: m_ifAddresses) for (auto address: m_ifAddresses)
@ -216,7 +57,7 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool
Host::~Host() Host::~Host()
{ {
quit(); stop();
} }
void Host::start() void Host::start()
@ -226,30 +67,78 @@ void Host::start()
void Host::stop() 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); 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; m_run = false;
} }
// we know shutdown is complete when m_timer is reset // wait for m_timer to reset (indicating network scheduler has stopped)
while (m_timer) while (!!m_timer)
this_thread::sleep_for(chrono::milliseconds(50)); this_thread::sleep_for(chrono::milliseconds(50));
// stop worker thread
stopWorking(); stopWorking();
} }
void Host::quit() void Host::doneWorking()
{ {
// called to force io_service to kill any remaining tasks it might have - // reset ioservice (allows manually polling network, below)
// such tasks may involve socket reads from Capabilities that maintain references m_ioService.reset();
// to resources we're about to free.
if (isWorking()) // shutdown acceptor
stop(); m_acceptorV4.cancel();
m_acceptor.reset(); if (m_acceptorV4.is_open())
m_socket.reset(); 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_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 unsigned Host::protocolVersion() const
@ -407,7 +296,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp)
if (_upnp) if (_upnp)
{ {
bi::address upnpifaddr; 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 (!upnpep.address().is_unspecified() && !upnpifaddr.is_unspecified())
{ {
if (!m_peerAddresses.count(upnpep.address())) 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); 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 assert(m_listenPort > 0);
if (!m_ioService || m_listenPort < 1)
return; if (m_run && !m_accepting)
if (!m_accepting)
{ {
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")"; clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_public << ")";
m_accepting = true; 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) if (!ec)
{ {
try try
@ -452,8 +341,9 @@ void Host::ensureAccepting()
} catch (...){} } catch (...){}
bi::address remoteAddress = m_socket->remote_endpoint().address(); bi::address remoteAddress = m_socket->remote_endpoint().address();
// Port defaults to 0 - we let the hello tell us which port the peer listens to // 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(); p->start();
success = true;
} }
catch (Exception const& _e) catch (Exception const& _e)
{ {
@ -464,9 +354,17 @@ void Host::ensureAccepting()
clog(NetWarn) << "ERROR: " << _e.what(); 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; m_accepting = false;
if (ec.value() < 1) if (ec.value() < 1)
ensureAccepting(); runAcceptor();
}); });
} }
} }
@ -480,17 +378,16 @@ string Host::pocHost()
void Host::connect(std::string const& _addr, unsigned short _port) noexcept 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_run)
if (!m_ioService)
return; return;
for (int i = 0; i < 2; ++i) for (auto first: {true, false})
{ {
try 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()); connect(r.resolve({_addr, toString(_port)})->endpoint());
} }
else else
@ -512,12 +409,11 @@ void Host::connect(std::string const& _addr, unsigned short _port) noexcept
void Host::connect(bi::tcp::endpoint const& _ep) 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_run)
if (!m_ioService)
return; return;
clog(NetConnect) << "Attempting single-shot connection to " << _ep; 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) s->async_connect(_ep, [=](boost::system::error_code const& ec)
{ {
if (ec) if (ec)
@ -534,8 +430,7 @@ void Host::connect(bi::tcp::endpoint const& _ep)
void Host::connect(std::shared_ptr<Node> const& _n) 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_run)
if (!m_ioService)
return; return;
// prevent concurrently connecting to a node; todo: better abstraction // 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->lastAttempted = std::chrono::system_clock::now();
_n->failedAttempts++; _n->failedAttempts++;
m_ready -= _n->index; m_ready -= _n->index;
bi::tcp::socket* s = new bi::tcp::socket(*m_ioService); bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_n->address, [=](boost::system::error_code const& ec)
{ auto n = node(_n->id);
if (ec) if (n)
{ s->async_connect(_n->address, [=](boost::system::error_code const& 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; if (ec)
_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. clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")";
p->start(); _n->lastDisconnect = TCPError;
} _n->lastAttempted = std::chrono::system_clock::now();
delete s; m_ready += _n->index;
Guard l(x_pendingNodeConns); }
m_pendingNodeConns.erase(nptr); 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 bool Host::havePeer(NodeId _id) const
@ -680,8 +580,7 @@ void Host::prunePeers()
PeerInfos Host::peers(bool _updatePing) const 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_run)
if (!m_ioService)
return PeerInfos(); return PeerInfos();
RecursiveGuard l(x_peers); RecursiveGuard l(x_peers);
@ -698,152 +597,95 @@ PeerInfos Host::peers(bool _updatePing) const
return ret; return ret;
} }
void Host::run(boost::system::error_code const& error) void Host::run(boost::system::error_code const&)
{ {
m_lastTick += c_timerInterval; if (!m_run)
if (error || !m_ioService)
{ {
// 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(); m_timer.reset();
return; return;
} }
// network running m_lastTick += c_timerInterval;
if (m_run) if (m_lastTick >= c_timerInterval * 10)
{ {
if (m_lastTick >= c_timerInterval * 10) growPeers();
{ prunePeers();
growPeers(); m_lastTick = 0;
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;
} }
// network stopping if (m_hadNewNodes)
if (!m_run)
{ {
// close acceptor for (auto p: m_peers)
if (m_acceptor->is_open()) if (auto pp = p.second.lock())
{ pp->serviceNodesRequest();
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();
// m_run is false, so we're stopping; kill timer m_hadNewNodes = false;
m_lastTick = 0; }
// causes parent thread's stop() to continue which calls stopWorking() if (chrono::steady_clock::now() - m_lastPing > chrono::seconds(30)) // ping every 30s.
m_timer.reset(); {
for (auto p: m_peers)
// stop ioservice (stops blocking worker thread, allowing thread to join) if (auto pp = p.second.lock())
if (!!m_ioService) if (chrono::steady_clock::now() - pp->m_lastReceived > chrono::seconds(60))
m_ioService->stop(); pp->disconnect(PingTimeout);
return; 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() void Host::startedWorking()
{ {
if (!m_timer) asserts(!m_timer);
{ {
// no timer means this is first run and network must be started // prevent m_run from being set to true at same time as set to false by stop()
// (run once when host worker thread calls startedWorking()) // 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);
// prevent m_run from being set to true at same time as set to false by stop() // create deadline timer
// don't release mutex until m_timer is set so in case stop() is called at same m_timer.reset(new boost::asio::deadline_timer(m_ioService));
// time, stop will wait on m_timer and graceful network shutdown. m_run = true;
Guard l(x_runTimer); }
// reset io service and create deadline timer
m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); // try to open acceptor (todo: ipv6)
m_run = true; m_listenPort = Network::listen4(m_acceptorV4, m_netPrefs.listenPort);
}
m_ioService->reset(); // start capability threads
for (auto const& h: m_capabilities)
// try to open acceptor (todo: ipv6) h.second->onStarting();
m_listenPort = listen4(m_acceptor.get(), m_netPrefs.listenPort);
// determine public IP, but only if we're able to listen for connections
// start capability threads // todo: GUI when listen is unavailable in UI
for (auto const& h: m_capabilities) if (m_listenPort)
h.second->onStarting(); {
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp);
// 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);
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()); run(boost::system::error_code());
} }
void Host::doWork() void Host::doWork()
{ {
// no ioService means we've had quit() called - bomb out - we're not allowed in here. if (m_run)
if (asserts(!!m_ioService)) m_ioService.run();
return;
m_ioService->run();
} }
void Host::pingAll() void Host::pingAll()

45
libp2p/Host.h

@ -14,7 +14,8 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. 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> * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2014
*/ */
@ -34,9 +35,10 @@
#include <libdevcore/RangeMask.h> #include <libdevcore/RangeMask.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
#include "HostCapability.h" #include "HostCapability.h"
#include "Network.h"
#include "Common.h" #include "Common.h"
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bi = boost::asio::ip; namespace bi = ba::ip;
namespace dev namespace dev
{ {
@ -101,16 +103,6 @@ struct Node
using Nodes = std::vector<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 * @brief The Host class
* Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * 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 Session;
friend class HostCapabilityFace; friend class HostCapabilityFace;
friend struct Node; 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: public:
/// Start server, listening for connections on the given port. /// Start server, listening for connections on the given port.
@ -187,14 +168,12 @@ public:
void start(); void start();
/// Stop network. @threadsafe /// Stop network. @threadsafe
/// Resets acceptor, socket, and IO service. Called by deallocator.
void stop(); void stop();
/// @returns if network is running. /// @returns if network is running.
bool isStarted() const { return m_run; } 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(); } NodeId id() const { return m_key.pub(); }
void registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps); void registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps);
@ -205,7 +184,8 @@ private:
/// Populate m_peerAddresses with available public addresses. /// Populate m_peerAddresses with available public addresses.
void determinePublic(std::string const& _publicAddress, bool _upnp); void determinePublic(std::string const& _publicAddress, bool _upnp);
void ensureAccepting(); /// Called only from startedWorking().
void runAcceptor();
void seal(bytes& _b); void seal(bytes& _b);
@ -214,11 +194,14 @@ private:
/// Called by Worker. Not thread-safe; to be called only by worker. /// Called by Worker. Not thread-safe; to be called only by worker.
virtual void startedWorking(); 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. 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(); 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()); std::shared_ptr<Node> noteNode(NodeId _id, bi::tcp::endpoint _a, Origin _o, bool _ready, NodeId _oldId = NodeId());
Nodes potentialPeers(RangeMask<unsigned> const& _known); 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. 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. ba::io_service m_ioService; ///< IOService for network stuff.
std::unique_ptr<bi::tcp::acceptor> m_acceptor; ///< Listening acceptor. bi::tcp::acceptor m_acceptorV4; ///< Listening acceptor.
std::unique_ptr<bi::tcp::socket> m_socket; ///< Listening socket. 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. 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 try
{ {
if (m_socket.is_open()) if (m_socket.is_open())
{
boost::system::error_code ec;
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
m_socket.close(); m_socket.close();
}
} }
catch (...){} catch (...){}
} }
@ -479,6 +483,8 @@ void Session::drop(DisconnectReason _reason)
try try
{ {
clogS(NetConnect) << "Closing " << m_socket.remote_endpoint() << "(" << reasonOf(_reason) << ")"; 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(); m_socket.close();
} }
catch (...) {} catch (...) {}

282
libsolidity/AST.cpp

@ -25,6 +25,7 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h>
using namespace std; using namespace std;
@ -33,38 +34,34 @@ namespace dev
namespace solidity namespace solidity
{ {
void ContractDefinition::accept(ASTVisitor& _visitor) TypeError ASTNode::createTypeError(string const& _description) const
{ {
if (_visitor.visit(*this)) return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
} }
void StructDefinition::accept(ASTVisitor& _visitor) vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{ {
if (_visitor.visit(*this)) vector<FunctionDefinition const*> exportedFunctions;
listAccept(m_members, _visitor); for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
_visitor.endVisit(*this); 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() sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
{ return exportedFunctions;
checkMemberTypes();
checkRecursion();
} }
void StructDefinition::checkMemberTypes() void StructDefinition::checkMemberTypes() const
{ {
for (ASTPointer<VariableDeclaration> const& member: getMembers()) for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored()) if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
} }
void StructDefinition::checkRecursion() void StructDefinition::checkRecursion() const
{ {
set<StructDefinition const*> definitionsSeen; set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this}; vector<StructDefinition const*> queue = {this};
@ -79,239 +76,12 @@ void StructDefinition::checkRecursion()
for (ASTPointer<VariableDeclaration> const& member: def->getMembers()) for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT) 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())); 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() void FunctionDefinition::checkTypeRequirements()
{ {
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters()) for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
@ -519,7 +289,7 @@ void Identifier::checkTypeRequirements()
if (asserts(m_referencedDeclaration)) if (asserts(m_referencedDeclaration))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved.")); 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)
{ {
if (!variable->getType()) if (!variable->getType())
@ -529,29 +299,29 @@ void Identifier::checkTypeRequirements()
return; return;
} }
//@todo can we unify these with TypeName::toType()? //@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) if (structDef)
{ {
// note that we do not have a struct type here // 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; return;
} }
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
if (functionDef) if (functionDef)
{ {
// a function reference is not a TypeType, because calling a TypeType converts to the type. // 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 // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion. // conversion.
m_type = make_shared<FunctionType>(*functionDef); m_type = make_shared<FunctionType const>(*functionDef);
return; return;
} }
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef) if (contractDef)
{ {
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef)); m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
return; return;
} }
MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration); MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
if (magicVariable) if (magicVariable)
{ {
m_type = magicVariable->getType(); m_type = magicVariable->getType();
@ -562,7 +332,7 @@ void Identifier::checkTypeRequirements()
void ElementaryTypeNameExpression::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() void Literal::checkTypeRequirements()

160
libsolidity/AST.h

@ -39,6 +39,7 @@ namespace solidity
{ {
class ASTVisitor; class ASTVisitor;
class ASTConstVisitor;
/** /**
@ -54,12 +55,19 @@ public:
virtual ~ASTNode() {} virtual ~ASTNode() {}
virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0;
template <class T> template <class T>
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor)
{ {
for (ASTPointer<T>& element: _list) for (ASTPointer<T>& element: _list)
element->accept(_visitor); 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. /// Returns the source code location of this node.
Location const& getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
@ -79,6 +87,44 @@ private:
Location m_location; 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). * Abstract AST class for a declaration (contract, function, struct, variable).
*/ */
@ -86,18 +132,18 @@ class Declaration: public ASTNode
{ {
public: public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): 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. /// @returns the declared name.
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; } Declaration const* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; } void setScope(Declaration const* _scope) { m_scope = _scope; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Declaration* m_scope; Declaration const* m_scope;
}; };
/** /**
@ -120,6 +166,7 @@ public:
{} {}
virtual void accept(ASTVisitor& _visitor) override; 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<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
@ -142,16 +189,17 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _members): std::vector<ASTPointer<VariableDeclaration>> const& _members):
Declaration(_location, _name), m_members(_members) {} Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; } std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
/// Checks that the members do not include any recursive structs and have valid types /// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions). /// (e.g. no functions).
void checkValidityOfMembers(); void checkValidityOfMembers() const;
private: private:
void checkMemberTypes(); void checkMemberTypes() const;
void checkRecursion(); void checkRecursion() const;
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
}; };
@ -168,6 +216,7 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _parameters): std::vector<ASTPointer<VariableDeclaration>> const& _parameters):
ASTNode(_location), m_parameters(_parameters) {} ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; } std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; }
@ -194,14 +243,15 @@ public:
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; } bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); } 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(); } std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } 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. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
@ -234,15 +284,16 @@ public:
ASTPointer<ASTString> const& _name): ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {} Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override; 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 /// 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. /// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _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: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -261,6 +312,8 @@ public:
Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {} Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); } << 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; } std::shared_ptr<Type const> const& getType() const { return m_type; }
@ -279,11 +332,12 @@ class TypeName: public ASTNode
public: public:
explicit TypeName(Location const& _location): ASTNode(_location) {} explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; 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 /// 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. /// 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. /// 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 +353,8 @@ public:
if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; 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; } Token::Value getTypeName() const { return m_type; }
@ -314,18 +369,19 @@ class UserDefinedTypeName: public TypeName
{ {
public: public:
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name): 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 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; } 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; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Declaration* m_referencedDeclaration; Declaration const* m_referencedDeclaration;
}; };
/** /**
@ -338,7 +394,8 @@ public:
ASTPointer<TypeName> const& _valueType): ASTPointer<TypeName> const& _valueType):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override; 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; } ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; } TypeName const& getValueType() const { return *m_valueType; }
@ -361,7 +418,6 @@ class Statement: public ASTNode
{ {
public: public:
explicit Statement(Location const& _location): ASTNode(_location) {} 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. /// 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 /// This includes checking that operators are applicable to their arguments but also that
@ -378,6 +434,7 @@ public:
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements): Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements):
Statement(_location), m_statements(_statements) {} Statement(_location), m_statements(_statements) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
@ -397,12 +454,13 @@ public:
Statement(_location), Statement(_location),
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; } Expression const& getCondition() const { return *m_condition; }
Statement& getTrueStatement() const { return *m_trueBody; } Statement const& getTrueStatement() const { return *m_trueBody; }
/// @returns the "else" part of the if statement or nullptr if there is no "else" part. /// @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: private:
ASTPointer<Expression> m_condition; ASTPointer<Expression> m_condition;
@ -411,13 +469,12 @@ private:
}; };
/** /**
* Statement in which a break statement is legal. * Statement in which a break statement is legal (abstract class).
*/ */
class BreakableStatement: public Statement class BreakableStatement: public Statement
{ {
public: public:
BreakableStatement(Location const& _location): Statement(_location) {} BreakableStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
}; };
class WhileStatement: public BreakableStatement class WhileStatement: public BreakableStatement
@ -427,10 +484,11 @@ public:
ASTPointer<Statement> const& _body): ASTPointer<Statement> const& _body):
BreakableStatement(_location), m_condition(_condition), m_body(_body) {} BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; } Expression const& getCondition() const { return *m_condition; }
Statement& getBody() const { return *m_body; } Statement const& getBody() const { return *m_body; }
private: private:
ASTPointer<Expression> m_condition; ASTPointer<Expression> m_condition;
@ -442,6 +500,7 @@ class Continue: public Statement
public: public:
Continue(Location const& _location): Statement(_location) {} Continue(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {} virtual void checkTypeRequirements() override {}
}; };
@ -450,6 +509,7 @@ class Break: public Statement
public: public:
Break(Location const& _location): Statement(_location) {} Break(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {} virtual void checkTypeRequirements() override {}
}; };
@ -457,8 +517,9 @@ class Return: public Statement
{ {
public: public:
Return(Location const& _location, ASTPointer<Expression> _expression): 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
@ -468,7 +529,7 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
return *m_returnParameters; return *m_returnParameters;
} }
Expression* getExpression() const { return m_expression.get(); } Expression const* getExpression() const { return m_expression.get(); }
private: private:
ASTPointer<Expression> m_expression; ///< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
@ -489,10 +550,11 @@ public:
ASTPointer<Expression> _value): ASTPointer<Expression> _value):
Statement(_location), m_variable(_variable), m_value(_value) {} Statement(_location), m_variable(_variable), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; } VariableDeclaration const& getDeclaration() const { return *m_variable; }
Expression* getExpression() const { return m_value.get(); } Expression const* getExpression() const { return m_value.get(); }
private: private:
ASTPointer<VariableDeclaration> m_variable; ASTPointer<VariableDeclaration> m_variable;
@ -508,9 +570,10 @@ public:
ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression): ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {} Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
private: private:
ASTPointer<Expression> m_expression; ASTPointer<Expression> m_expression;
@ -572,11 +635,12 @@ public:
if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() 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; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
Expression& getRightHandSide() const { return *m_rightHandSide; } Expression const& getRightHandSide() const { return *m_rightHandSide; }
private: private:
ASTPointer<Expression> m_leftHandSide; ASTPointer<Expression> m_leftHandSide;
@ -599,11 +663,12 @@ public:
if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; } bool isPrefixOperation() const { return m_isPrefix; }
Expression& getSubExpression() const { return *m_subExpression; } Expression const& getSubExpression() const { return *m_subExpression; }
private: private:
Token::Value m_operator; Token::Value m_operator;
@ -625,10 +690,11 @@ public:
if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getLeftExpression() const { return *m_left; } Expression const& getLeftExpression() const { return *m_left; }
Expression& getRightExpression() const { return *m_right; } Expression const& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
Type const& getCommonType() const { return *m_commonType; } Type const& getCommonType() const { return *m_commonType; }
@ -652,10 +718,11 @@ public:
std::vector<ASTPointer<Expression>> const& _arguments): std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; } 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 /// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call. /// or constructor call.
@ -676,7 +743,8 @@ public:
ASTPointer<ASTString> const& _memberName): ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {} Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override; 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; } ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
@ -695,10 +763,11 @@ public:
ASTPointer<Expression> const& _index): ASTPointer<Expression> const& _index):
Expression(_location), m_base(_base), m_index(_index) {} Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getBaseExpression() const { return *m_base; } Expression const& getBaseExpression() const { return *m_base; }
Expression& getIndexExpression() const { return *m_index; } Expression const& getIndexExpression() const { return *m_index; }
private: private:
ASTPointer<Expression> m_base; ASTPointer<Expression> m_base;
@ -722,20 +791,21 @@ class Identifier: public PrimaryExpression
{ {
public: public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
/// Declaration the name refers to. /// Declaration the name refers to.
Declaration* m_referencedDeclaration; Declaration const* m_referencedDeclaration;
}; };
/** /**
@ -752,6 +822,7 @@ public:
if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getTypeToken() const { return m_typeToken; } Token::Value getTypeToken() const { return m_typeToken; }
@ -769,6 +840,7 @@ public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value): Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {} PrimaryExpression(_location), m_token(_token), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; } Token::Value getToken() const { return m_token; }

2
libsolidity/ASTForward.h

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

131
libsolidity/ASTPrinter.cpp

@ -30,7 +30,7 @@ namespace dev
namespace solidity 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) 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() + "\""); writeLine("ContractDefinition \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(StructDefinition& _node) bool ASTPrinter::visit(StructDefinition const& _node)
{ {
writeLine("StructDefinition \"" + _node.getName() + "\""); writeLine("StructDefinition \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ParameterList& _node) bool ASTPrinter::visit(ParameterList const& _node)
{ {
writeLine("ParameterList"); writeLine("ParameterList");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(FunctionDefinition& _node) bool ASTPrinter::visit(FunctionDefinition const& _node)
{ {
writeLine("FunctionDefinition \"" + _node.getName() + "\"" + writeLine("FunctionDefinition \"" + _node.getName() + "\"" +
(_node.isPublic() ? " - public" : "") + (_node.isPublic() ? " - public" : "") +
@ -73,112 +80,112 @@ bool ASTPrinter::visit(FunctionDefinition& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(VariableDeclaration& _node) bool ASTPrinter::visit(VariableDeclaration const& _node)
{ {
writeLine("VariableDeclaration \"" + _node.getName() + "\""); writeLine("VariableDeclaration \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(TypeName& _node) bool ASTPrinter::visit(TypeName const& _node)
{ {
writeLine("TypeName"); writeLine("TypeName");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ElementaryTypeName& _node) bool ASTPrinter::visit(ElementaryTypeName const& _node)
{ {
writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName())); writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(UserDefinedTypeName& _node) bool ASTPrinter::visit(UserDefinedTypeName const& _node)
{ {
writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); writeLine("UserDefinedTypeName \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Mapping& _node) bool ASTPrinter::visit(Mapping const& _node)
{ {
writeLine("Mapping"); writeLine("Mapping");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Statement& _node) bool ASTPrinter::visit(Statement const& _node)
{ {
writeLine("Statement"); writeLine("Statement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Block& _node) bool ASTPrinter::visit(Block const& _node)
{ {
writeLine("Block"); writeLine("Block");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(IfStatement& _node) bool ASTPrinter::visit(IfStatement const& _node)
{ {
writeLine("IfStatement"); writeLine("IfStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(BreakableStatement& _node) bool ASTPrinter::visit(BreakableStatement const& _node)
{ {
writeLine("BreakableStatement"); writeLine("BreakableStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(WhileStatement& _node) bool ASTPrinter::visit(WhileStatement const& _node)
{ {
writeLine("WhileStatement"); writeLine("WhileStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Continue& _node) bool ASTPrinter::visit(Continue const& _node)
{ {
writeLine("Continue"); writeLine("Continue");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Break& _node) bool ASTPrinter::visit(Break const& _node)
{ {
writeLine("Break"); writeLine("Break");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Return& _node) bool ASTPrinter::visit(Return const& _node)
{ {
writeLine("Return"); writeLine("Return");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(VariableDefinition& _node) bool ASTPrinter::visit(VariableDefinition const& _node)
{ {
writeLine("VariableDefinition"); writeLine("VariableDefinition");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ExpressionStatement& _node) bool ASTPrinter::visit(ExpressionStatement const& _node)
{ {
writeLine("ExpressionStatement"); writeLine("ExpressionStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Expression& _node) bool ASTPrinter::visit(Expression const& _node)
{ {
writeLine("Expression"); writeLine("Expression");
printType(_node); printType(_node);
@ -186,7 +193,7 @@ bool ASTPrinter::visit(Expression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Assignment& _node) bool ASTPrinter::visit(Assignment const& _node)
{ {
writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node); printType(_node);
@ -194,7 +201,7 @@ bool ASTPrinter::visit(Assignment& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(UnaryOperation& _node) bool ASTPrinter::visit(UnaryOperation const& _node)
{ {
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator())); ") " + Token::toString(_node.getOperator()));
@ -203,7 +210,7 @@ bool ASTPrinter::visit(UnaryOperation& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(BinaryOperation& _node) bool ASTPrinter::visit(BinaryOperation const& _node)
{ {
writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node); printType(_node);
@ -211,7 +218,7 @@ bool ASTPrinter::visit(BinaryOperation& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(FunctionCall& _node) bool ASTPrinter::visit(FunctionCall const& _node)
{ {
writeLine("FunctionCall"); writeLine("FunctionCall");
printType(_node); printType(_node);
@ -219,7 +226,7 @@ bool ASTPrinter::visit(FunctionCall& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(MemberAccess& _node) bool ASTPrinter::visit(MemberAccess const& _node)
{ {
writeLine("MemberAccess to member " + _node.getMemberName()); writeLine("MemberAccess to member " + _node.getMemberName());
printType(_node); printType(_node);
@ -227,7 +234,7 @@ bool ASTPrinter::visit(MemberAccess& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(IndexAccess& _node) bool ASTPrinter::visit(IndexAccess const& _node)
{ {
writeLine("IndexAccess"); writeLine("IndexAccess");
printType(_node); printType(_node);
@ -235,7 +242,7 @@ bool ASTPrinter::visit(IndexAccess& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(PrimaryExpression& _node) bool ASTPrinter::visit(PrimaryExpression const& _node)
{ {
writeLine("PrimaryExpression"); writeLine("PrimaryExpression");
printType(_node); printType(_node);
@ -243,7 +250,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(Identifier const& _node)
{ {
writeLine(string("Identifier ") + _node.getName()); writeLine(string("Identifier ") + _node.getName());
printType(_node); printType(_node);
@ -251,7 +258,7 @@ bool ASTPrinter::visit(Identifier& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
{ {
writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node); printType(_node);
@ -259,7 +266,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Literal& _node) bool ASTPrinter::visit(Literal const& _node)
{ {
char const* tokenString = Token::toString(_node.getToken()); char const* tokenString = Token::toString(_node.getToken());
if (!tokenString) if (!tokenString)
@ -270,157 +277,157 @@ bool ASTPrinter::visit(Literal& _node)
return goDeeper(); return goDeeper();
} }
void ASTPrinter::endVisit(ASTNode&) void ASTPrinter::endVisit(ImportDirective const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ContractDefinition&) void ASTPrinter::endVisit(ContractDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(StructDefinition&) void ASTPrinter::endVisit(StructDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ParameterList&) void ASTPrinter::endVisit(ParameterList const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(FunctionDefinition&) void ASTPrinter::endVisit(FunctionDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(VariableDeclaration&) void ASTPrinter::endVisit(VariableDeclaration const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(TypeName&) void ASTPrinter::endVisit(TypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ElementaryTypeName&) void ASTPrinter::endVisit(ElementaryTypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(UserDefinedTypeName&) void ASTPrinter::endVisit(UserDefinedTypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Mapping&) void ASTPrinter::endVisit(Mapping const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Statement&) void ASTPrinter::endVisit(Statement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Block&) void ASTPrinter::endVisit(Block const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(IfStatement&) void ASTPrinter::endVisit(IfStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(BreakableStatement&) void ASTPrinter::endVisit(BreakableStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(WhileStatement&) void ASTPrinter::endVisit(WhileStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Continue&) void ASTPrinter::endVisit(Continue const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Break&) void ASTPrinter::endVisit(Break const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Return&) void ASTPrinter::endVisit(Return const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(VariableDefinition&) void ASTPrinter::endVisit(VariableDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ExpressionStatement&) void ASTPrinter::endVisit(ExpressionStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Expression&) void ASTPrinter::endVisit(Expression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Assignment&) void ASTPrinter::endVisit(Assignment const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(UnaryOperation&) void ASTPrinter::endVisit(UnaryOperation const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(BinaryOperation&) void ASTPrinter::endVisit(BinaryOperation const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(FunctionCall&) void ASTPrinter::endVisit(FunctionCall const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(MemberAccess&) void ASTPrinter::endVisit(MemberAccess const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(IndexAccess&) void ASTPrinter::endVisit(IndexAccess const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(PrimaryExpression&) void ASTPrinter::endVisit(PrimaryExpression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Identifier&) void ASTPrinter::endVisit(Identifier const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ElementaryTypeNameExpression&) void ASTPrinter::endVisit(ElementaryTypeNameExpression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Literal&) void ASTPrinter::endVisit(Literal const&)
{ {
m_indentation--; 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. * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
*/ */
class ASTPrinter: public ASTVisitor class ASTPrinter: public ASTConstVisitor
{ {
public: public:
/// Create a printer for the given abstract syntax tree. If the source is specified, /// 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. /// 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. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);
bool visit(ContractDefinition& _node) override; bool visit(ImportDirective const& _node) override;
bool visit(StructDefinition& _node) override; bool visit(ContractDefinition const& _node) override;
bool visit(ParameterList& _node) override; bool visit(StructDefinition const& _node) override;
bool visit(FunctionDefinition& _node) override; bool visit(ParameterList const& _node) override;
bool visit(VariableDeclaration& _node) override; bool visit(FunctionDefinition const& _node) override;
bool visit(TypeName& _node) override; bool visit(VariableDeclaration const& _node) override;
bool visit(ElementaryTypeName& _node) override; bool visit(TypeName const& _node) override;
bool visit(UserDefinedTypeName& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(Mapping& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Statement& _node) override; bool visit(Mapping const& _node) override;
bool visit(Block& _node) override; bool visit(Statement const& _node) override;
bool visit(IfStatement& _node) override; bool visit(Block const& _node) override;
bool visit(BreakableStatement& _node) override; bool visit(IfStatement const& _node) override;
bool visit(WhileStatement& _node) override; bool visit(BreakableStatement const& _node) override;
bool visit(Continue& _node) override; bool visit(WhileStatement const& _node) override;
bool visit(Break& _node) override; bool visit(Continue const& _node) override;
bool visit(Return& _node) override; bool visit(Break const& _node) override;
bool visit(VariableDefinition& _node) override; bool visit(Return const& _node) override;
bool visit(ExpressionStatement& _node) override; bool visit(VariableDefinition const& _node) override;
bool visit(Expression& _node) override; bool visit(ExpressionStatement const& _node) override;
bool visit(Assignment& _node) override; bool visit(Expression const& _node) override;
bool visit(UnaryOperation& _node) override; bool visit(Assignment const& _node) override;
bool visit(BinaryOperation& _node) override; bool visit(UnaryOperation const& _node) override;
bool visit(FunctionCall& _node) override; bool visit(BinaryOperation const& _node) override;
bool visit(MemberAccess& _node) override; bool visit(FunctionCall const& _node) override;
bool visit(IndexAccess& _node) override; bool visit(MemberAccess const& _node) override;
bool visit(PrimaryExpression& _node) override; bool visit(IndexAccess const& _node) override;
bool visit(Identifier& _node) override; bool visit(PrimaryExpression const& _node) override;
bool visit(ElementaryTypeNameExpression& _node) override; bool visit(Identifier const& _node) override;
bool visit(Literal& _node) override; bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
void endVisit(ASTNode& _node) override; void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition&) override; void endVisit(ContractDefinition const&) override;
void endVisit(StructDefinition&) override; void endVisit(StructDefinition const&) override;
void endVisit(ParameterList&) override; void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition&) override; void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration&) override; void endVisit(VariableDeclaration const&) override;
void endVisit(TypeName&) override; void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping&) override; void endVisit(Mapping const&) override;
void endVisit(Statement&) override; void endVisit(Statement const&) override;
void endVisit(Block&) override; void endVisit(Block const&) override;
void endVisit(IfStatement&) override; void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement&) override; void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement&) override; void endVisit(WhileStatement const&) override;
void endVisit(Continue&) override; void endVisit(Continue const&) override;
void endVisit(Break&) override; void endVisit(Break const&) override;
void endVisit(Return&) override; void endVisit(Return const&) override;
void endVisit(VariableDefinition&) override; void endVisit(VariableDefinition const&) override;
void endVisit(ExpressionStatement&) override; void endVisit(ExpressionStatement const&) override;
void endVisit(Expression&) override; void endVisit(Expression const&) override;
void endVisit(Assignment&) override; void endVisit(Assignment const&) override;
void endVisit(UnaryOperation&) override; void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation&) override; void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall&) override; void endVisit(FunctionCall const&) override;
void endVisit(MemberAccess&) override; void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess&) override; void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression&) override; void endVisit(PrimaryExpression const&) override;
void endVisit(Identifier&) override; void endVisit(Identifier const&) override;
void endVisit(ElementaryTypeNameExpression&) override; void endVisit(ElementaryTypeNameExpression const&) override;
void endVisit(Literal&) override; void endVisit(Literal const&) override;
private: private:
void printSourcePart(ASTNode const& _node); void printSourcePart(ASTNode const& _node);
@ -114,7 +115,7 @@ private:
int m_indentation; int m_indentation;
std::string m_source; std::string m_source;
ASTNode* m_ast; ASTNode const* m_ast;
std::ostream* m_ostream; std::ostream* m_ostream;
}; };

76
libsolidity/ASTVisitor.h

@ -42,6 +42,8 @@ class ASTVisitor
{ {
public: public:
virtual bool visit(ASTNode&) { return true; } 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(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; } virtual bool visit(StructDefinition&) { return true; }
virtual bool visit(ParameterList&) { return true; } virtual bool visit(ParameterList&) { return true; }
@ -74,6 +76,8 @@ public:
virtual bool visit(Literal&) { return true; } virtual bool visit(Literal&) { return true; }
virtual void endVisit(ASTNode&) { } virtual void endVisit(ASTNode&) { }
virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { } virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(StructDefinition&) { } virtual void endVisit(StructDefinition&) { }
virtual void endVisit(ParameterList&) { } virtual void endVisit(ParameterList&) { }
@ -106,5 +110,77 @@ public:
virtual void endVisit(Literal&) { } 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 #pragma once
#include <memory>
#include <string>
#include <ostream> #include <ostream>
namespace dev namespace dev
@ -35,19 +37,19 @@ namespace solidity
*/ */
struct Location 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) { } Location(): start(-1), end(-1) { }
bool IsValid() const { return start >= 0 && end >= start; }
int start; int start;
int end; int end;
std::shared_ptr<std::string const> sourceName;
}; };
/// Stream output for Location (used e.g. in boost exceptions). /// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location) inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{ {
return _out << "[" << _location.start << "," << _location.end << ")"; return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
} }
} }

67
libsolidity/Compiler.cpp

@ -26,13 +26,14 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
namespace dev { namespace dev {
namespace solidity { 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 m_context = CompilerContext(); // clear it just in case
@ -135,7 +136,7 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{ {
unsigned const numBytes = var->getType()->getCalldataEncodedSize(); unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation()) << errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
@ -154,18 +155,20 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
//@todo this can be also done more efficiently //@todo this can be also done more efficiently
unsigned dataOffset = 0; unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters(); vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i) for (unsigned i = 0; i < parameters.size(); ++i)
{ {
Type const& paramType = *parameters[i]->getType(); Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize(); unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported.")); << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
m_context << eth::dupInstruction(parameters.size() - i); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
if (numBytes != 32) if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes; dataOffset += numBytes;
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
@ -179,7 +182,7 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable); 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 //@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
@ -195,15 +198,12 @@ bool Compiler::visit(FunctionDefinition& _function)
// stack upon entry: [return address] [arg0] [arg1] ... [argn] // stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned const numArguments = _function.getParameters().size(); for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters() + _function.getReturnParameters())
m_context.addVariable(*variable); m_context.addVariable(*variable);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables()) for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addVariable(*localVariable); m_context.addAndInitializeVariable(*localVariable);
m_context.initializeLocalVariables(numReturnValues + numLocalVariables);
_function.getBody().accept(*this); _function.getBody().accept(*this);
@ -215,12 +215,16 @@ bool Compiler::visit(FunctionDefinition& _function)
// Note that the fact that the return arguments are of increasing index is vital for this // Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work. // 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; vector<int> stackLayout;
stackLayout.push_back(numReturnValues); // target of return address stackLayout.push_back(returnValuesSize); // target of return address
stackLayout += vector<int>(numArguments, -1); // discard all arguments stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < numReturnValues; ++i) for (unsigned i = 0; i < returnValuesSize; ++i)
stackLayout.push_back(i); stackLayout.push_back(i);
stackLayout += vector<int>(numLocalVariables, -1); stackLayout += vector<int>(localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1)) while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
@ -240,7 +244,7 @@ bool Compiler::visit(FunctionDefinition& _function)
return false; return false;
} }
bool Compiler::visit(IfStatement& _ifStatement) bool Compiler::visit(IfStatement const& _ifStatement)
{ {
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition()); ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump(); eth::AssemblyItem trueTag = m_context.appendConditionalJump();
@ -253,7 +257,7 @@ bool Compiler::visit(IfStatement& _ifStatement)
return false; return false;
} }
bool Compiler::visit(WhileStatement& _whileStatement) bool Compiler::visit(WhileStatement const& _whileStatement)
{ {
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
@ -275,58 +279,53 @@ bool Compiler::visit(WhileStatement& _whileStatement)
return false; return false;
} }
bool Compiler::visit(Continue&) bool Compiler::visit(Continue const&)
{ {
if (!m_continueTags.empty()) if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back()); m_context.appendJumpTo(m_continueTags.back());
return false; return false;
} }
bool Compiler::visit(Break&) bool Compiler::visit(Break const&)
{ {
if (!m_breakTags.empty()) if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back()); m_context.appendJumpTo(m_breakTags.back());
return false; 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 //@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); ExpressionCompiler::compileExpression(m_context, *expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable)); CompilerUtils(m_context).moveToStackVariable(firstVariable);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
m_context.appendJumpTo(m_returnTag); m_context.appendJumpTo(m_returnTag);
return false; 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); ExpressionCompiler::compileExpression(m_context, *expression);
ExpressionCompiler::appendTypeConversion(m_context, ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(), *expression->getType(),
*_variableDefinition.getDeclaration().getType()); *_variableDefinition.getDeclaration().getType());
unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration()); CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
return false; return false;
} }
bool Compiler::visit(ExpressionStatement& _expressionStatement) bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{ {
Expression& expression = _expressionStatement.getExpression(); Expression const& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression); ExpressionCompiler::compileExpression(m_context, expression);
// Type::Category category = expression.getType()->getCategory(); CompilerUtils(m_context).popStackElement(*expression.getType());
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
return false; return false;
} }

20
libsolidity/Compiler.h

@ -27,12 +27,12 @@
namespace dev { namespace dev {
namespace solidity { namespace solidity {
class Compiler: private ASTVisitor class Compiler: private ASTConstVisitor
{ {
public: public:
Compiler(): m_returnTag(m_context.newTag()) {} Compiler(): m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals); void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); } bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
@ -48,14 +48,14 @@ private:
void registerStateVariables(ContractDefinition const& _contract); void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(FunctionDefinition& _function) override; virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement& _ifStatement) override; virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement& _whileStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(Continue& _continue) override; virtual bool visit(Continue const& _continue) override;
virtual bool visit(Break& _break) override; virtual bool visit(Break const& _break) override;
virtual bool visit(Return& _return) override; virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition& _variableDefinition) override; virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement& _expressionStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override;
CompilerContext m_context; CompilerContext m_context;

30
libsolidity/CompilerContext.cpp

@ -41,20 +41,30 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize(); 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); *this << u256(0);
for (unsigned i = 1; i < _numVariables; ++i) m_asm.adjustDeposit(-size);
*this << eth::Instruction::DUP1; }
m_asm.adjustDeposit(-_numVariables);
} void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
} }
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const 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 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 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())) if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); 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 unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const

13
libsolidity/CompilerContext.h

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

186
libsolidity/CompilerStack.cpp

@ -36,22 +36,43 @@ namespace dev
namespace solidity namespace solidity
{ {
CompilerStack::CompilerStack(): m_interfaceHandler(make_shared<InterfaceHandler>()) {} 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) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
m_scanner = make_shared<Scanner>(CharStream(_sourceCode)); addSource("", _sourceCode);
} }
void CompilerStack::parse() void CompilerStack::parse()
{ {
if (!m_scanner) for (auto& sourcePair: m_sources)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); {
m_contractASTNode = Parser().parse(m_scanner); sourcePair.second.scanner->reset();
sourcePair.second.ast = Parser().parse(sourcePair.second.scanner);
}
resolveImports();
m_globalContext = make_shared<GlobalContext>(); m_globalContext = make_shared<GlobalContext>();
m_globalContext->setCurrentContract(*m_contractASTNode); NameAndTypeResolver resolver(m_globalContext->getDeclarations());
NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); 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; m_parseSuccessful = true;
} }
@ -61,54 +82,90 @@ void CompilerStack::parse(string const& _sourceCode)
parse(); parse();
} }
bytes const& CompilerStack::compile(bool _optimize) vector<string> CompilerStack::getContractNames() const
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear(); vector<string> contractNames;
m_compiler = make_shared<Compiler>(); for (auto const& contract: m_contracts)
m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); contractNames.push_back(contract.first);
return m_bytecode = m_compiler->getAssembledBytecode(_optimize); 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>();
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(_optimize);
compiledContract.compiler = move(compiler);
}
} }
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{ {
parse(_sourceCode); parse(_sourceCode);
return compile(_optimize); compile(_optimize);
return getBytecode();
} }
void CompilerStack::streamAssembly(ostream& _outStream) bytes const& CompilerStack::getBytecode(string const& _contractName) const
{ {
if (!m_compiler || m_bytecode.empty()) return getContract(_contractName).bytecode;
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
m_compiler->streamAssembly(_outStream);
} }
std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type) void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{
getContract(_contractName).compiler->streamAssembly(_outStream);
}
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) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
auto createDocIfNotThere = [this, _type](std::unique_ptr<string>& _doc) Contract const& contract = getContract(_contractName);
{
if (!_doc)
_doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type);
};
std::unique_ptr<string const>* doc;
switch (_type) switch (_type)
{ {
case NATSPEC_USER: case DocumentationType::NATSPEC_USER:
createDocIfNotThere(m_userDocumentation); doc = &contract.userDocumentation;
return *m_userDocumentation; break;
case NATSPEC_DEV: case DocumentationType::NATSPEC_DEV:
createDocIfNotThere(m_devDocumentation); doc = &contract.devDocumentation;
return *m_devDocumentation; break;
case ABI_INTERFACE: case DocumentationType::ABI_INTERFACE:
createDocIfNotThere(m_interface); doc = &contract.interface;
return *m_interface; break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
} }
if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
return *(*doc);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); 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) bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
@ -117,7 +174,70 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize); 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>()) {}
} }
} }

79
libsolidity/CompilerStack.h

@ -25,6 +25,7 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <memory> #include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
namespace dev { namespace dev {
@ -33,11 +34,12 @@ namespace solidity {
// forward declarations // forward declarations
class Scanner; class Scanner;
class ContractDefinition; class ContractDefinition;
class SourceUnit;
class Compiler; class Compiler;
class GlobalContext; class GlobalContext;
class InterfaceHandler; class InterfaceHandler;
enum DocumentationType: unsigned short enum class DocumentationType: uint8_t
{ {
NATSPEC_USER = 1, NATSPEC_USER = 1,
NATSPEC_DEV, NATSPEC_DEV,
@ -49,52 +51,85 @@ enum DocumentationType: unsigned short
* It holds state and can be used to either step through the compilation stages (and abort e.g. * 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. * before compilation to bytecode) or run the whole compilation in one call.
*/ */
class CompilerStack class CompilerStack: boost::noncopyable
{ {
public: public:
CompilerStack(); CompilerStack(): m_parseSuccessful(false) {}
void reset() { *this = CompilerStack(); }
/// 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); void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse(); void parse();
/// Sets the given source code as the only source unit and parses it.
void parse(std::string const& _sourceCode); void parse(std::string const& _sourceCode);
/// Compiles the contract that was previously parsed. /// Returns a list of the contract names in the sources.
bytes const& compile(bool _optimize = false); 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. /// Parses and compiles the given source code.
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); 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. /// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation. /// 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. /// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile. /// 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. /// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get. /// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c documentation_type /// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(enum DocumentationType type); std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Returns the previously used scanner, useful for counting lines during error reporting. /// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner() const { return *m_scanner; } Scanner const& getScanner(std::string const& _sourceName = "") const;
ContractDefinition& getAST() const { return *m_contractASTNode; } SourceUnit const& getAST(std::string const& _sourceName = "") const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// 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. /// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
private: private:
std::shared_ptr<Scanner> m_scanner; /**
std::shared_ptr<GlobalContext> m_globalContext; * Information pertaining to one source unit, filled gradually during parsing and compilation.
std::shared_ptr<ContractDefinition> m_contractASTNode; */
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; bool m_parseSuccessful;
std::unique_ptr<std::string> m_interface; std::map<std::string const, Source> m_sources;
std::unique_ptr<std::string> m_userDocumentation; std::shared_ptr<GlobalContext> m_globalContext;
std::unique_ptr<std::string> m_devDocumentation; std::vector<Source const*> m_sourceOrder;
std::shared_ptr<Compiler> m_compiler; std::map<std::string const, Contract> m_contracts;
std::shared_ptr<InterfaceHandler> m_interfaceHandler;
bytes m_bytecode;
}; };
} }

71
libsolidity/CompilerUtils.cpp

@ -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 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::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;
}
}
}

63
libsolidity/CompilerUtils.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/>.
*/
/**
* @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) {}
/// 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 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; return false;
m_declarations[_declaration.getName()] = &_declaration; m_declarations[_declaration.getName()] = &_declaration;
return true; 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); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())

15
libsolidity/DeclarationContainer.h

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

1
libsolidity/Exceptions.h

@ -38,7 +38,6 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: 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; typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
} }

245
libsolidity/ExpressionCompiler.cpp

@ -26,13 +26,14 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
namespace dev { namespace dev {
namespace solidity { namespace solidity {
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression) void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression)
{ {
ExpressionCompiler compiler(_context); ExpressionCompiler compiler(_context);
_expression.accept(compiler); _expression.accept(compiler);
@ -45,7 +46,7 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
compiler.appendTypeConversion(_typeOnStack, _targetType); compiler.appendTypeConversion(_typeOnStack, _targetType);
} }
bool ExpressionCompiler::visit(Assignment& _assignment) bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
_assignment.getRightHandSide().accept(*this); _assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
@ -67,7 +68,7 @@ bool ExpressionCompiler::visit(Assignment& _assignment)
return false; 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: //@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 // 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 const& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression(); Expression const& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType(); Type const& commonType = _binaryOperation.getCommonType();
Token::Value const op = _binaryOperation.getOperator(); Token::Value const op = _binaryOperation.getOperator();
@ -158,7 +159,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
return false; return false;
} }
bool ExpressionCompiler::visit(FunctionCall& _functionCall) bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
using Location = FunctionType::Location; using Location = FunctionType::Location;
if (_functionCall.isTypeConversion()) if (_functionCall.isTypeConversion())
@ -166,7 +167,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
//@todo struct construction //@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1)) if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front(); Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this); firstArgument.accept(*this);
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
_functionCall.getType()->getCategory() == Type::Category::INTEGER) _functionCall.getType()->getCategory() == Type::Category::INTEGER)
@ -174,18 +175,18 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// explicit type conversion contract -> address, nothing to do. // explicit type conversion contract -> address, nothing to do.
} }
else else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
} }
else else
{ {
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()); 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())) if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
if (function.getLocation() == Location::INTERNAL) switch (function.getLocation())
{
case Location::INTERNAL:
{ {
// Calling convention: Caller pushes return address and arguments // Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values // Callee removes them and pushes return values
@ -201,74 +202,104 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
m_context.appendJump(); m_context.appendJump();
m_context << returnLabel; m_context << returnLabel;
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label // 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 // @todo for now, the return value of a function is its first return value, so remove
// all others // all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) 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) case Location::EXTERNAL:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
else
{ {
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:
{ {
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1}, arguments[i]->accept(*this);
{Location::SHA256, 2}, Type const& type = *function.getParameterTypes()[i];
{Location::RIPEMD160, 3}}; appendTypeConversion(*arguments[i]->getType(), type);
u256 contractAddress = contractAddresses.find(function.getLocation())->second; unsigned const numBytes = type.getCalldataEncodedSize();
// @todo later, combine this code with external function call if (numBytes == 0 || numBytes > 32)
for (unsigned i = 0; i < arguments.size(); ++i) BOOST_THROW_EXCEPTION(CompilerError()
{ << errinfo_sourceLocation(arguments[i]->getLocation())
arguments[i]->accept(*this); << errinfo_comment("Type " + type.toString() + " not yet supported."));
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); if (numBytes != 32)
// @todo move this once we actually use memory m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(i * 32) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
} dataOffset += numBytes;
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;
} }
default: //@todo only return the first return value for now
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); unsigned retSize = function.getReturnParameterTypes().empty() ? 0
: function.getReturnParameterTypes().front()->getCalldataEncodedSize();
// 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 == 32)
m_context << u256(0) << eth::Instruction::MLOAD;
else if (retSize > 0)
m_context << (u256(1) << ((32 - retSize) * 8))
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
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
m_context << u256(0) << eth::Instruction::MSTORE << 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
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;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
} }
} }
return false; return false;
} }
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
ASTString const& member = _memberAccess.getMemberName(); ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory()) switch (_memberAccess.getExpression().getType()->getCategory())
@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break; break;
case Type::Category::CONTRACT: 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; break;
}
case Type::Category::MAGIC: case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member // we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase") if (member == "coinbase")
@ -323,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{ {
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType()); StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; 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); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; break;
} }
@ -332,7 +365,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
} }
} }
bool ExpressionCompiler::visit(IndexAccess& _indexAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this); _indexAccess.getIndexExpression().accept(*this);
@ -343,36 +376,36 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
m_context << u256(64) << u256(0) << eth::Instruction::SHA3; 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); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false; return false;
} }
void ExpressionCompiler::endVisit(Identifier& _identifier) void ExpressionCompiler::endVisit(Identifier const& _identifier)
{ {
Declaration* declaration = _identifier.getReferencedDeclaration(); Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration)) if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{ {
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
return; return;
} }
if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration)) if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{ {
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return; 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); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return; return;
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); 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()) switch (_literal.getType()->getCategory())
{ {
@ -385,7 +418,7 @@ void ExpressionCompiler::endVisit(Literal& _literal)
} }
} }
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{ {
Token::Value const op = _binaryOperation.getOperator(); Token::Value const op = _binaryOperation.getOperator();
if (asserts(op == Token::OR || op == Token::AND)) if (asserts(op == Token::OR || op == Token::AND))
@ -532,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; 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 void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
{ {
switch (m_type) switch (m_type)
@ -542,7 +582,8 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << 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; break;
} }
case STORAGE: case STORAGE:
@ -550,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *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; break;
case MEMORY: case MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -571,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{ {
case STACK: case STACK:
{ {
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
if (stackPos > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackPos > 0) else if (stackDiff > 0)
*m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression);
break; break;
@ -584,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
case LValue::STORAGE: case LValue::STORAGE:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_move) // stack layout: value value ... value ref
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; if (!_move) // copy values
*m_context << eth::Instruction::SSTORE; {
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; break;
case LValue::MEMORY: case LValue::MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -612,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{ {
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration)) if (m_context->isLocalVariable(&_declaration))
{ {
m_type = STACK; m_type = STACK;

38
libsolidity/ExpressionCompiler.h

@ -31,20 +31,21 @@ class AssemblyItem; // forward
} }
namespace solidity { namespace solidity {
class CompilerContext; // forward // forward declarations
class Type; // forward class CompilerContext;
class IntegerType; // forward class Type;
class IntegerType;
/** /**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream * 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 * of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit. * unit.
*/ */
class ExpressionCompiler: private ASTVisitor class ExpressionCompiler: private ASTConstVisitor
{ {
public: public:
/// Compile the given @a _expression into the @a _context. /// Compile the given @a _expression into the @a _context.
static void compileExpression(CompilerContext& _context, Expression& _expression); static void compileExpression(CompilerContext& _context, Expression const& _expression);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. /// 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); static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
@ -53,18 +54,18 @@ private:
ExpressionCompiler(CompilerContext& _compilerContext): ExpressionCompiler(CompilerContext& _compilerContext):
m_context(_compilerContext), m_currentLValue(m_context) {} m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment& _assignment) override; virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override; virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override; virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall& _functionCall) override; virtual bool visit(FunctionCall const& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override; virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess& _indexAccess) override; virtual bool visit(IndexAccess const& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override; virtual void endVisit(Identifier const& _identifier) override;
virtual void endVisit(Literal& _literal) override; virtual void endVisit(Literal const& _literal) override;
///@{ ///@{
///@name Append code for various operator types ///@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 appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
@ -92,8 +93,7 @@ private:
enum LValueType { NONE, STACK, MEMORY, STORAGE }; enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0): LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
@ -128,14 +128,12 @@ private:
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset; unsigned m_baseStackOffset;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize;
}; };
CompilerContext& m_context; CompilerContext& m_context;
LValue m_currentLValue; 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; 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); 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(variable.get());
declarations.push_back(getCurrentThis());
return declarations; return declarations;
} }
MagicVariableDeclaration*GlobalContext::getCurrentThis() const MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
{ {
if (!m_thisPointer[m_currentContract]) if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(

10
libsolidity/GlobalContext.h

@ -47,15 +47,17 @@ class GlobalContext: private boost::noncopyable
public: public:
GlobalContext(); GlobalContext();
void setCurrentContract(ContractDefinition const& _contract); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const; 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: private:
MagicVariableDeclaration* getCurrentThis() const; std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
ContractDefinition const* m_currentContract; 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;
}; };
} }

52
libsolidity/InterfaceHandler.cpp

@ -12,19 +12,19 @@ namespace solidity
InterfaceHandler::InterfaceHandler() InterfaceHandler::InterfaceHandler()
{ {
m_lastTag = DOCTAG_NONE; m_lastTag = DocTagType::NONE;
} }
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef,
enum DocumentationType _type) DocumentationType _type)
{ {
switch(_type) switch(_type)
{ {
case NATSPEC_USER: case DocumentationType::NATSPEC_USER:
return getUserDocumentation(_contractDef); return getUserDocumentation(_contractDef);
case NATSPEC_DEV: case DocumentationType::NATSPEC_DEV:
return getDevDocumentation(_contractDef); return getDevDocumentation(_contractDef);
case ABI_INTERFACE: case DocumentationType::ABI_INTERFACE:
return getABIInterface(_contractDef); return getABIInterface(_contractDef);
} }
@ -32,11 +32,11 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<
return nullptr; return nullptr;
} }
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef)
{ {
Json::Value methods(Json::arrayValue); Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
Json::Value inputs(Json::arrayValue); Json::Value inputs(Json::arrayValue);
@ -63,12 +63,12 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<C
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods))); return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
} }
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef)
{ {
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value user; Json::Value user;
auto strPtr = f->getDocumentation(); auto strPtr = f->getDocumentation();
@ -88,14 +88,14 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
} }
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef)
{ {
// LTODO: Somewhere in this function warnings for mismatch of param names // LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown // should be thrown
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
auto strPtr = f->getDocumentation(); auto strPtr = f->getDocumentation();
@ -146,7 +146,7 @@ static inline std::string::const_iterator skipLineOrEOS(std::string::const_itera
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string& _tagString, std::string& _tagString,
enum DocTagType _tagType) DocTagType _tagType)
{ {
auto nlPos = std::find(_pos, _end, '\n'); auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(_tagString)); std::copy(_pos, nlPos, back_inserter(_tagString));
@ -170,7 +170,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons
auto paramDesc = std::string(currPos, nlPos); auto paramDesc = std::string(currPos, nlPos);
m_params.push_back(std::make_pair(paramName, paramDesc)); m_params.push_back(std::make_pair(paramName, paramDesc));
m_lastTag = DOCTAG_PARAM; m_lastTag = DocTagType::PARAM;
return skipLineOrEOS(nlPos, _end); return skipLineOrEOS(nlPos, _end);
} }
@ -197,14 +197,14 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
{ {
// LTODO: need to check for @(start of a tag) between here and the end of line // LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases // for all cases
if (m_lastTag == DOCTAG_NONE || _tag != "") if (m_lastTag == DocTagType::NONE || _tag != "")
{ {
if (_tag == "dev") if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
else if (_tag == "notice") else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
else if (_tag == "return") else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
else if (_tag == "param") else if (_tag == "param")
return parseDocTagParam(_pos, _end); return parseDocTagParam(_pos, _end);
else else
@ -222,16 +222,16 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
{ {
switch (m_lastTag) switch (m_lastTag)
{ {
case DOCTAG_DEV: case DocTagType::DEV:
m_dev += " "; m_dev += " ";
return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DOCTAG_NOTICE: case DocTagType::NOTICE:
m_notice += " "; m_notice += " ";
return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DOCTAG_RETURN: case DocTagType::RETURN:
m_return += " "; m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DOCTAG_PARAM: case DocTagType::PARAM:
return appendDocTagParam(_pos, _end); return appendDocTagParam(_pos, _end);
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type"));
@ -267,7 +267,7 @@ void InterfaceHandler::parseDocString(std::string const& _string)
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos));
} }
else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end); currPos = appendDocTag(currPos + 1, end);
else if (currPos != end) // skip the line if a newline was found else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1; currPos = nlPos + 1;

28
libsolidity/InterfaceHandler.h

@ -37,15 +37,15 @@ namespace solidity
// Forward declarations // Forward declarations
class ContractDefinition; class ContractDefinition;
enum DocumentationType: unsigned short; enum class DocumentationType: uint8_t;
enum DocTagType enum class DocTagType: uint8_t
{ {
DOCTAG_NONE = 0, NONE = 0,
DOCTAG_DEV, DEV,
DOCTAG_NOTICE, NOTICE,
DOCTAG_PARAM, PARAM,
DOCTAG_RETURN RETURN
}; };
class InterfaceHandler class InterfaceHandler
@ -59,23 +59,23 @@ public:
/// types provided by @c DocumentationType /// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of provided type /// representation of provided type
std::unique_ptr<std::string> getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef,
enum DocumentationType _type); DocumentationType _type);
/// Get the ABI Interface of the contract /// Get the ABI Interface of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface /// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getABIInterface(ContractDefinition& _contractDef);
/// Get the User documentation of the contract /// Get the User documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's user documentation /// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getUserDocumentation(ContractDefinition& _contractDef);
/// Get the Developer's documentation of the contract /// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation /// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getDevDocumentation(ContractDefinition& _contractDef);
private: private:
void resetUser(); void resetUser();
@ -84,7 +84,7 @@ private:
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string& _tagString, std::string& _tagString,
enum DocTagType _tagType); DocTagType _tagType);
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end); std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
@ -99,7 +99,7 @@ private:
Json::StyledWriter m_writer; Json::StyledWriter m_writer;
// internal state // internal state
enum DocTagType m_lastTag; DocTagType m_lastTag;
std::string m_notice; std::string m_notice;
std::string m_dev; std::string m_dev;
std::string m_return; std::string m_return;

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); 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) void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{ {
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract]; m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr); ReferencesResolver resolver(*structDef, *this, nullptr);
@ -65,7 +70,14 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[nullptr]; 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); auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes)) if (iterator == end(m_scopes))
@ -73,7 +85,7 @@ Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaratio
return iterator->second.resolveName(_name, false); 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); return m_currentScope->resolveName(_name, _recursive);
} }
@ -135,7 +147,7 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true; return true;
} }
void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{ {
map<ASTNode const*, DeclarationContainer>::iterator iter; map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded; bool newlyAdded;
@ -193,15 +205,14 @@ bool ReferencesResolver::visit(Return& _return)
return true; return true;
} }
bool ReferencesResolver::visit(Mapping& _mapping) bool ReferencesResolver::visit(Mapping&)
{ {
(void)_mapping;
return true; return true;
} }
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
{ {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));
@ -211,7 +222,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
bool ReferencesResolver::visit(Identifier& _identifier) bool ReferencesResolver::visit(Identifier& _identifier)
{ {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));

18
libsolidity/NameAndTypeResolver.h

@ -41,21 +41,25 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable class NameAndTypeResolver: private boost::noncopyable
{ {
public: 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); 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, /// 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). /// 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. /// @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 /// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase. /// resolving phase.
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private: private:
/// Throws if @a _struct contains a recursive loop. Note that recursion via mappings is fine.
void checkForRecursion(StructDefinition const& _struct);
void reset(); void reset();
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
@ -85,12 +89,12 @@ private:
void endVisit(VariableDefinition& _variableDefinition); void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration); bool visit(VariableDeclaration& _declaration);
void enterNewSubScope(Declaration& _declaration); void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope(); void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope); void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode const*, DeclarationContainer>& m_scopes; std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration* m_currentScope; Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction; FunctionDefinition* m_currentFunction;
}; };

86
libsolidity/Parser.cpp

@ -20,30 +20,27 @@
* Solidity parser. * Solidity parser.
*/ */
#include <vector>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libsolidity/BaseTypes.h> #include <libsolidity/BaseTypes.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity 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 /// AST node factory that also tracks the begin and end position of an AST node
/// while it is being parsed /// while it is being parsed
class Parser::ASTNodeFactory class Parser::ASTNodeFactory
{ {
public: 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 markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty() { m_location.end = m_location.start; }
@ -55,7 +52,7 @@ public:
{ {
if (m_location.end < 0) if (m_location.end < 0)
markEndPosition(); markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); return make_shared<NodeType>(m_location, forward<Args>(_args)...);
} }
private: private:
@ -63,6 +60,33 @@ private:
Location m_location; 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 int Parser::getPosition() const
{ {
return m_scanner->getCurrentLocation().start; return m_scanner->getCurrentLocation().start;
@ -73,15 +97,27 @@ int Parser::getEndPosition() const
return m_scanner->getCurrentLocation().end; 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() ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT); expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
std::vector<ASTPointer<StructDefinition>> structs; vector<ASTPointer<StructDefinition>> structs;
std::vector<ASTPointer<VariableDeclaration>> stateVariables; vector<ASTPointer<VariableDeclaration>> stateVariables;
std::vector<ASTPointer<FunctionDefinition>> functions; vector<ASTPointer<FunctionDefinition>> functions;
bool visibilityIsPublic = true; bool visibilityIsPublic = true;
while (true) while (true)
{ {
@ -110,7 +146,6 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
} }
@ -119,7 +154,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "") if (m_scanner->getCurrentCommentLiteral() != "")
docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION); expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
@ -142,7 +177,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
// create an empty parameter list at a zero-length location // create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty(); nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>()); returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
} }
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
@ -156,7 +191,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT); expectToken(Token::STRUCT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
std::vector<ASTPointer<VariableDeclaration>> members; vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBRACE)
{ {
@ -228,7 +263,7 @@ ASTPointer<Mapping> Parser::parseMapping()
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
std::vector<ASTPointer<VariableDeclaration>> parameters; vector<ASTPointer<VariableDeclaration>> parameters;
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{ {
@ -249,7 +284,7 @@ ASTPointer<Block> Parser::parseBlock()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
std::vector<ASTPointer<Statement>> statements; vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBRACE)
statements.push_back(parseStatement()); statements.push_back(parseStatement());
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -447,7 +482,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LPAREN: case Token::LPAREN:
{ {
m_scanner->next(); m_scanner->next();
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
@ -503,9 +538,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression; 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) if (m_scanner->getCurrentToken() != Token::RPAREN)
{ {
arguments.push_back(parseExpression()); arguments.push_back(parseExpression());
@ -521,7 +556,7 @@ std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)
{ {
if (m_scanner->getCurrentToken() != _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(); m_scanner->next();
} }
@ -543,14 +578,15 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
ASTPointer<ASTString> Parser::getLiteralAndAdvance() 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(); m_scanner->next();
return identifier; 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 class Parser
{ {
public: 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: private:
class ASTNodeFactory; class ASTNodeFactory;
@ -46,6 +47,7 @@ private:
///@{ ///@{
///@name Parsing functions for the AST nodes ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();

11
libsolidity/Scanner.cpp

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

14
libsolidity/Scanner.h

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

41
libsolidity/SourceReferenceFormatter.cpp

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

69
libsolidity/Types.cpp

@ -32,7 +32,7 @@ namespace dev
namespace solidity namespace solidity
{ {
shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
{ {
if (asserts(Token::isElementaryTypeName(_typeToken))) if (asserts(Token::isElementaryTypeName(_typeToken)))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
@ -44,33 +44,33 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
if (bytes == 0) if (bytes == 0)
bytes = 32; bytes = 32;
int modifier = offset / 33; int modifier = offset / 33;
return make_shared<IntegerType>(bytes * 8, return make_shared<IntegerType const>(bytes * 8,
modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH); IntegerType::Modifier::HASH);
} }
else if (_typeToken == Token::ADDRESS) 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) else if (_typeToken == Token::BOOL)
return make_shared<BoolType>(); return make_shared<BoolType const>();
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); 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(); Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) 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)) 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)) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract); return make_shared<ContractType const>(*contract);
return shared_ptr<Type>(); 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(); shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
if (!keyType) if (!keyType)
@ -78,28 +78,28 @@ shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
shared_ptr<Type const> valueType = _typeName.getValueType().toType(); shared_ptr<Type const> valueType = _typeName.getValueType().toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); 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()) switch (_literal.getToken())
{ {
case Token::TRUE_LITERAL: case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL: case Token::FALSE_LITERAL:
return make_shared<BoolType>(); return make_shared<BoolType const>();
case Token::NUMBER: case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue()); return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL: case Token::STRING_LITERAL:
return shared_ptr<Type>(); // @todo add string literals return shared_ptr<Type const>(); // @todo add string literals
default: default:
return shared_ptr<Type>(); return shared_ptr<Type const>();
} }
} }
const MemberList Type::EmptyMemberList = MemberList(); 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); bigint value(_literal);
bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-'); bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-');
@ -108,8 +108,8 @@ shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _liter
value = ((-value) - 1) << 1; value = ((-value) - 1) << 1;
unsigned bytes = max(bytesRequired(value), 1u); unsigned bytes = max(bytesRequired(value), 1u);
if (bytes > 32) if (bytes > 32)
return shared_ptr<IntegerType>(); return shared_ptr<IntegerType const>();
return make_shared<IntegerType>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); return make_shared<IntegerType const>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
} }
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
@ -140,7 +140,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(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 bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@ -247,6 +247,31 @@ string ContractType::toString() const
return "contract " + m_contract.getName(); 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 bool StructType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -302,7 +327,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); 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 params;
TypePointers retParams; TypePointers retParams;
@ -314,7 +339,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function)
retParams.push_back(var->getType()); retParams.push_back(var->getType());
swap(params, m_parameterTypes); swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes); swap(retParams, m_returnParameterTypes);
m_location = Location::INTERNAL; m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
} }
bool FunctionType::operator==(Type const& _other) const bool FunctionType::operator==(Type const& _other) const
@ -323,6 +348,8 @@ bool FunctionType::operator==(Type const& _other) const
return false; return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other); FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if (m_location != other.m_location)
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() || if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false; return false;

23
libsolidity/Types.h

@ -80,15 +80,15 @@ public:
///@{ ///@{
///@name Factory functions ///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type. /// 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 const> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type> fromFunction(FunctionDefinition const& _function); 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 /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type. /// 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 Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } 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 /// @returns the smallest integer type for the given literal or an empty pointer
/// if no type fits. /// 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); explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
@ -214,10 +214,17 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString() const override;
virtual MemberList const& getMembers() const override;
unsigned getFunctionIndex(std::string const& _functionName) const;
private: private:
ContractDefinition const& m_contract; 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 +270,7 @@ public:
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 }; enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; } 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, FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL): Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
@ -279,7 +286,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
Location getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
private: private:
TypePointers m_parameterTypes; 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) catch (dev::Exception const& exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str(); cwarn << "Solidity compilation error: " << error.str();
} }
catch (...) 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). // 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. // 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 // 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 // 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). // use bits of data owned by m_ethereum).
m_net.quit(); m_net.stop();
m_ethereum.reset(); m_ethereum.reset();
} }

5
libwebthree/WebThree.h

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

9
libwhisper/Message.cpp

@ -89,6 +89,15 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
return ret; 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 Message Envelope::open(Secret const& _s) const
{ {
return Message(*this, _s); return Message(*this, _s);

9
libwhisper/Message.h

@ -51,14 +51,7 @@ class Envelope
public: public:
Envelope() {} Envelope() {}
Envelope(RLP const& _m) 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>();
}
operator bool() const { return !!m_expiry; } operator bool() const { return !!m_expiry; }

2
libwhisper/WhisperHost.cpp

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

2
libwhisper/WhisperHost.h

@ -78,7 +78,7 @@ private:
mutable dev::SharedMutex x_messages; mutable dev::SharedMutex x_messages;
std::map<h256, Envelope> m_messages; std::map<h256, Envelope> m_messages;
std::map<unsigned, h256> m_expiryQueue; std::multimap<unsigned, h256> m_expiryQueue;
mutable dev::Mutex m_filterLock; mutable dev::Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters; 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() void WhisperPeer::sendMessages()
{ {
if (m_unseen.size()) RLPStream amalg;
unsigned msgCount = 0;
{ {
RLPStream amalg; Guard l(x_unseen);
unsigned msgCount; msgCount = m_unseen.size();
while (m_unseen.size())
{ {
Guard l(x_unseen); auto p = *m_unseen.begin();
msgCount = m_unseen.size(); m_unseen.erase(m_unseen.begin());
while (m_unseen.size()) 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; RLPStream s;
prep(s, MessagesPacket, msgCount).appendRaw(amalg.out(), msgCount); prep(s, MessagesPacket, msgCount).appendRaw(amalg.out(), msgCount);
sealAndSend(s); sealAndSend(s);
@ -106,5 +106,5 @@ void WhisperPeer::sendMessages()
void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) void WhisperPeer::noteNewMessage(h256 _h, Message const& _m)
{ {
Guard l(x_unseen); 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); void noteNewMessage(h256 _h, Message const& _m);
mutable dev::Mutex x_unseen; 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(); 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) catch (dev::Exception const& _exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner()); solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler);
res.success = false; res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); res.comment = QString::fromStdString(error.str()).toHtmlEscaped();
res.hexCode = ""; res.hexCode = "";

2
solc/CMakeLists.txt

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

368
solc/CommandLineInterface.cpp

@ -0,0 +1,368 @@
/*
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>>())
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;
};
}
}

122
solc/main.cpp

@ -20,124 +20,16 @@
* Solidity commandline compiler. * Solidity commandline compiler.
*/ */
#include <string> #include "CommandLineInterface.h"
#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);
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
string infile; dev::solidity::CommandLineInterface cli;
bool optimize = false; if (!cli.parseArguments(argc, argv))
for (int i = 1; i < argc; ++i) return 1;
{ if (!cli.processInput())
string arg = argv[i]; return 1;
if (arg == "-o" || arg == "--optimize") cli.actOnInput();
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.getJsonDocumentation(ABI_INTERFACE) << endl;
cout << "Natspec user documentation: " << compiler.getJsonDocumentation(NATSPEC_USER) << endl;
cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(NATSPEC_DEV) << endl;
return 0; return 0;
} }

16
test/TestHelper.cpp

@ -72,7 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o
if (!isFiller) if (!isFiller)
{ {
importState(_o["post"].get_obj(), m_statePost); 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; return code;
} }
LogEntries importLog(json_spirit::mObject& _o) LogEntries importLog(json_spirit::mArray& _a)
{ {
LogEntries logEntries; 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) // cant use BOOST_REQUIRE, because this function is used outside boost test (createRandomTest)
assert(o.count("address") > 0); assert(o.count("address") > 0);
assert(o.count("topics") > 0); assert(o.count("topics") > 0);
assert(o.count("data") > 0); assert(o.count("data") > 0);
assert(o.count("bloom") > 0);
LogEntry log; LogEntry log;
log.address = Address(o["address"].get_str()); log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array()) for (auto const& t: o["topics"].get_array())
@ -279,9 +280,9 @@ LogEntries importLog(json_spirit::mObject& _o)
return logEntries; 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; if (_logs.size() == 0) return ret;
for (LogEntry const& l: _logs) for (LogEntry const& l: _logs)
{ {
@ -292,7 +293,8 @@ json_spirit::mObject exportLog(eth::LogEntries _logs)
topics.push_back(toString(t)); topics.push_back(toString(t));
o["topics"] = topics; o["topics"] = topics;
o["data"] = "0x" + toHex(l.data); o["data"] = "0x" + toHex(l.data);
ret[toString(l.bloom())] = o; o["bloom"] = toString(l.bloom());
ret.push_back(o);
} }
return ret; return ret;
} }

4
test/TestHelper.h

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

15
test/createRandomTest.cpp

@ -31,9 +31,8 @@
#include <json_spirit/json_spirit_writer_template.h> #include <json_spirit/json_spirit_writer_template.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libethereum/VMFactory.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevm/VM.h> #include <libevm/VMFactory.h>
#include "vm.h" #include "vm.h"
using namespace std; using namespace std;
@ -129,9 +128,6 @@ void doMyTests(json_spirit::mValue& v)
assert(o.count("pre") > 0); assert(o.count("pre") > 0);
assert(o.count("exec") > 0); assert(o.count("exec") > 0);
auto vmObj = eth::VMFactory::create(eth::VMFactory::Interpreter);
auto& vm = *vmObj;
dev::test::FakeExtVM fev; dev::test::FakeExtVM fev;
fev.importEnv(o["env"].get_obj()); fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj()); fev.importState(o["pre"].get_obj());
@ -145,14 +141,15 @@ void doMyTests(json_spirit::mValue& v)
fev.code = fev.thisTxCode; fev.code = fev.thisTxCode;
} }
vm.reset(fev.gas);
bytes output; bytes output;
auto vm = eth::VMFactory::create(fev.gas);
u256 gas; u256 gas;
bool vmExceptionOccured = false; bool vmExceptionOccured = false;
try try
{ {
output = vm.go(fev, fev.simpleTrace()).toBytes(); output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas(); gas = vm->gas();
} }
catch (eth::VMException const& _e) catch (eth::VMException const& _e)
{ {
@ -192,7 +189,7 @@ void doMyTests(json_spirit::mValue& v)
o["callcreates"] = fev.exportCallCreates(); o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output); o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas); 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> s_params(s_curveOID);
static CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP>::EllipticCurve s_curve(s_params.GetCurve()); 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) BOOST_AUTO_TEST_CASE(verify_secert)
{ {
h256 empty; h256 empty;

29
test/solidityCompiler.cpp

@ -46,16 +46,23 @@ namespace
bytes compileContract(const string& _sourceCode) bytes compileContract(const string& _sourceCode)
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract; ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
Compiler compiler; if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
compiler.compileContract(*contract, {}); {
// debug BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode(); 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. /// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation.
@ -118,8 +125,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
byte(Instruction::JUMP), // end of f byte(Instruction::JUMP), // end of f
byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::JUMPDEST), // beginning of g
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), // initialized e and h byte(Instruction::PUSH1), 0x0, // initialized e and h
byte(Instruction::PUSH1), byte(0x29 + shift), // ret address byte(Instruction::PUSH1), byte(0x2a + shift), // ret address
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), 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), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),

170
test/solidityEndToEndTest.cpp

@ -47,9 +47,11 @@ class ExecutionFramework
public: public:
ExecutionFramework() { g_logVerbosity = 0; } ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0, string const& _contractName = "")
{ {
bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); dev::solidity::CompilerStack compiler;
compiler.compile(_sourceCode);
bytes code = compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
@ -115,6 +117,7 @@ private:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{ {
m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state); eth::Executive executive(m_state);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) 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()); : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
@ -127,7 +130,7 @@ private:
catch (...) {} catch (...) {}
if (_isCreation) if (_isCreation)
{ {
BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender));
m_contractAddress = executive.newAddress(); m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress); BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
@ -135,14 +138,16 @@ private:
else else
{ {
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
} }
BOOST_REQUIRE(executive.go()); BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender);
executive.finalize(); executive.finalize();
m_output = executive.out().toVector(); m_output = executive.out().toVector();
} }
protected: protected:
Address m_sender;
Address m_contractAddress; Address m_contractAddress;
eth::State m_state; eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo; u256 const m_gasPrice = 100 * eth::szabo;
@ -898,6 +903,163 @@ BOOST_AUTO_TEST_CASE(ecrecover)
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); 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_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

47
test/solidityExpressionCompiler.cpp

@ -86,27 +86,34 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
vector<vector<string>> _localVariables = {}) vector<vector<string>> _localVariables = {})
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract; ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); resolver.registerDeclarations(*sourceUnit);
FirstExpressionExtractor extractor(*contract); for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
BOOST_REQUIRE(extractor.getExpression() != nullptr); if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
CompilerContext context; BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
for (vector<string> const& function: _functions) FirstExpressionExtractor extractor(*contract);
context.addFunction(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver))); BOOST_REQUIRE(extractor.getExpression() != nullptr);
for (vector<string> const& variable: _localVariables)
context.addVariable(dynamic_cast<VariableDeclaration const&>(resolveDeclaration(variable, resolver))); CompilerContext context;
for (vector<string> const& function: _functions)
ExpressionCompiler::compileExpression(context, *extractor.getExpression()); context.addFunction(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
for (vector<string> const& variable: _localVariables)
for (vector<string> const& function: _functions) context.addVariable(dynamic_cast<VariableDeclaration const&>(resolveDeclaration(variable, resolver)));
context << context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
bytes instructions = context.getAssembledBytecode(); ExpressionCompiler::compileExpression(context, *extractor.getExpression());
// debug
// cout << eth::disassemble(instructions) << endl; for (vector<string> const& function: _functions)
return instructions; context << context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
bytes instructions = context.getAssembledBytecode();
// debug
// cout << eth::disassemble(instructions) << endl;
return instructions;
}
BOOST_FAIL("No contract found in source.");
return bytes();
} }
} // end anonymous namespace } // end anonymous namespace

2
test/solidityJSONInterfaceTest.cpp

@ -50,7 +50,7 @@ public:
msg += *extra; msg += *extra;
BOOST_FAIL(msg); BOOST_FAIL(msg);
} }
std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(ABI_INTERFACE); std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation("", DocumentationType::ABI_INTERFACE);
Json::Value generatedInterface; Json::Value generatedInterface;
m_reader.parse(generatedInterfaceString, generatedInterface); m_reader.parse(generatedInterfaceString, generatedInterface);
Json::Value expectedInterface; Json::Value expectedInterface;

8
test/solidityNameAndTypeResolution.cpp

@ -41,10 +41,12 @@ namespace
void parseTextAndResolveNames(std::string const& _source) void parseTextAndResolveNames(std::string const& _source)
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract = parser.parse( ASTPointer<SourceUnit> sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source)));
std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
resolver.resolveNamesAndTypes(*contract); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
resolver.resolveNamesAndTypes(*contract);
} }
} }

4
test/solidityNatspecJSON.cpp

@ -55,9 +55,9 @@ public:
} }
if (_userDocumentation) if (_userDocumentation)
generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_USER); generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_USER);
else else
generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_DEV); generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_DEV);
Json::Value generatedDocumentation; Json::Value generatedDocumentation;
m_reader.parse(generatedDocumentationString, generatedDocumentation); m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation; Json::Value expectedDocumentation;

55
test/solidityParser.cpp

@ -21,13 +21,15 @@
*/ */
#include <string> #include <string>
#include <memory>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -40,7 +42,12 @@ namespace
ASTPointer<ContractDefinition> parseText(std::string const& _source) ASTPointer<ContractDefinition> parseText(std::string const& _source)
{ {
Parser parser; Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source))); ASTPointer<SourceUnit> sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source)));
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ASTPointer<ContractDefinition> contract = dynamic_pointer_cast<ContractDefinition>(node))
return contract;
BOOST_FAIL("No contract found in source.");
return ASTPointer<ContractDefinition>();
} }
} }
@ -380,6 +387,50 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
} }
BOOST_AUTO_TEST_CASE(import_directive)
{
char const* text = "import \"abc\";\n"
"contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multiple_contracts)
{
char const* text = "contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"contract test2 {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)
{
char const* text = "import \"abc\";\n"
"contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"import \"def\";\n"
"contract test2 {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"import \"ghi\";\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

1851
test/stLogTestsFiller.json

File diff suppressed because it is too large

35
test/stPreCompiledContractsFiller.json

@ -33,6 +33,41 @@
} }
}, },
"CallEcrecover0_overlappingInputOutput": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20000000",
"nonce" : 0,
"code": "{ (MSTORE 0 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c) (MSTORE 32 28) (MSTORE 64 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f) (MSTORE 96 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549) [[ 2 ]] (CALL 1000 1 0 0 128 64 32) [[ 0 ]] (MOD (MLOAD 64) (EXP 2 160)) [[ 1 ]] (EQ (ORIGIN) (SLOAD 0)) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "365224",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"CallEcrecover0_completeReturnValue": { "CallEcrecover0_completeReturnValue": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

80
test/stSystemOperationsTestFiller.json

@ -634,6 +634,86 @@
} }
}, },
"CallRecursiveBombLog": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20000000",
"nonce" : 0,
"code" : "{ (CALL 100000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }",
"storage": {}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) [[ 0 ]] (+ (SLOAD 0) 1) [[ 1 ]] (CALL (- (GAS) 224) (ADDRESS) 0 0 0 0 0) } ",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "1000000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"CallRecursiveBombLog2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "10000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "20000000",
"nonce" : 0,
"code" : "{ (CALL 100000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 23 0 0 0 0) }",
"storage": {}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 (GAS)) (LOG0 0 32) [[ 0 ]] (+ (SLOAD 0) 1) [[ 1 ]] (CALL (- (GAS) 224) (ADDRESS) 0 0 0 0 0) } ",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "1000000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"CallRecursiveBomb1": { "CallRecursiveBomb1": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

5
test/state.cpp

@ -125,6 +125,11 @@ BOOST_AUTO_TEST_CASE(stPreCompiledContracts)
dev::test::executeTests("stPreCompiledContracts", "/StateTests", dev::test::doStateTests); dev::test::executeTests("stPreCompiledContracts", "/StateTests", dev::test::doStateTests);
} }
BOOST_AUTO_TEST_CASE(stLogTests)
{
dev::test::executeTests("stLogTests", "/StateTests", dev::test::doStateTests);
}
BOOST_AUTO_TEST_CASE(stSpecialTest) BOOST_AUTO_TEST_CASE(stSpecialTest)
{ {
dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests); dev::test::executeTests("stSpecialTest", "/StateTests", dev::test::doStateTests);

86
test/vm.cpp

@ -22,8 +22,10 @@
#include <chrono> #include <chrono>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libethereum/VMFactory.h> #include <libethereum/Executive.h>
#include <libevm/VMFactory.h>
#include "vm.h" #include "vm.h"
using namespace std; using namespace std;
using namespace json_spirit; using namespace json_spirit;
using namespace dev; using namespace dev;
@ -121,41 +123,6 @@ void FakeExtVM::importEnv(mObject& _o)
currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str()); currentBlock.coinbaseAddress = Address(_o["currentCoinbase"].get_str());
} }
mObject FakeExtVM::exportLog()
{
mObject ret;
for (LogEntry const& l: sub.logs)
{
mObject o;
o["address"] = toString(l.address);
mArray topics;
for (auto const& t: l.topics)
topics.push_back(toString(t));
o["topics"] = topics;
o["data"] = "0x" + toHex(l.data);
ret[toString(l.bloom())] = o;
}
return ret;
}
void FakeExtVM::importLog(mObject& _o)
{
for (auto const& l: _o)
{
mObject o = l.second.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);
LogEntry log;
log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array())
log.topics.push_back(h256(t.get_str()));
log.data = importData(o);
sub.logs.push_back(log);
}
}
mObject FakeExtVM::exportState() mObject FakeExtVM::exportState()
{ {
mObject ret; mObject ret;
@ -320,14 +287,16 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
auto argc = boost::unit_test::framework::master_test_suite().argc; auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv; auto argv = boost::unit_test::framework::master_test_suite().argv;
auto useJit = false;
for (auto i = 0; i < argc && !useJit; ++i) for (auto i = 0; i < argc; ++i)
useJit |= std::string(argv[i]) == "--jit"; {
#if ETH_EVMJIT if (std::string(argv[i]) == "--jit")
auto vmKind = useJit ? VMFactory::JIT : VMFactory::Interpreter; {
#else VMFactory::setKind(VMKind::JIT);
auto vmKind == VMFactory::Interpreter; break;
#endif }
}
dev::test::FakeExtVM fev; dev::test::FakeExtVM fev;
fev.importEnv(o["env"].get_obj()); fev.importEnv(o["env"].get_obj());
@ -343,14 +312,13 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
fev.code = fev.thisTxCode; fev.code = fev.thisTxCode;
} }
auto vm = VMFactory::create(vmKind, fev.gas);
bytes output; bytes output;
auto startTime = std::chrono::high_resolution_clock::now();
u256 gas; u256 gas;
bool vmExceptionOccured = false; bool vmExceptionOccured = false;
auto startTime = std::chrono::high_resolution_clock::now();
try try
{ {
auto vm = eth::VMFactory::create(fev.gas);
output = vm->go(fev, fev.simpleTrace()).toBytes(); output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas(); gas = vm->gas();
} }
@ -410,7 +378,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates(); o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output); o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas); fev.push(o, "gas", gas);
o["logs"] = mValue(exportLog(fev.sub.logs)); o["logs"] = exportLog(fev.sub.logs);
} }
} }
else else
@ -428,7 +396,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
dev::test::FakeExtVM test; dev::test::FakeExtVM test;
test.importState(o["post"].get_obj()); test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array()); test.importCallCreates(o["callcreates"].get_array());
test.sub.logs = importLog(o["logs"].get_obj()); test.sub.logs = importLog(o["logs"].get_array());
checkOutput(output, o); checkOutput(output, o);
@ -514,6 +482,26 @@ BOOST_AUTO_TEST_CASE(vmLogTest)
dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests); dev::test::executeTests("vmLogTest", "/VMTests", dev::test::doVMTests);
} }
BOOST_AUTO_TEST_CASE(vmPerformanceTest)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--performance")
dev::test::executeTests("vmPerformanceTest", "/VMTests", dev::test::doVMTests);
}
}
BOOST_AUTO_TEST_CASE(vmArithPerformanceTest)
{
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i)
{
string arg = boost::unit_test::framework::master_test_suite().argv[i];
if (arg == "--performance")
dev::test::executeTests("vmArithPerformanceTest", "/VMTests", dev::test::doVMTests);
}
}
BOOST_AUTO_TEST_CASE(vmRandom) BOOST_AUTO_TEST_CASE(vmRandom)
{ {
string testPath = getTestPath(); string testPath = getTestPath();

2
test/vm.h

@ -72,8 +72,6 @@ public:
void importExec(json_spirit::mObject& _o); void importExec(json_spirit::mObject& _o);
json_spirit::mArray exportCallCreates(); json_spirit::mArray exportCallCreates();
void importCallCreates(json_spirit::mArray& _callcreates); void importCallCreates(json_spirit::mArray& _callcreates);
json_spirit::mObject exportLog();
void importLog(json_spirit::mObject& _o);
eth::OnOpFunc simpleTrace(); eth::OnOpFunc simpleTrace();

140
test/vmEnvironmentalInfoTestFiller.json

@ -338,6 +338,33 @@
} }
}, },
"calldataloadSizeTooHigh": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (CALLDATALOAD 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa)}",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "0x123456789abcdef0000000000000000000000000000000000000000000000000024",
"gasPrice" : "1000000000",
"gas" : "100000000000"
}
},
"calldatasize0": { "calldatasize0": {
"env" : { "env" : {
@ -451,6 +478,62 @@
} }
}, },
"calldatacopy_DataIndexTooHigh": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 0xff ) [[ 0 ]] @0}",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "0x1234567890abcdef01234567890abcdef",
"gasPrice" : "1000000000",
"gas" : "100000000000"
}
},
"calldatacopy_DataIndexTooHigh2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (CALLDATACOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 9 ) [[ 0 ]] @0}",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "0x1234567890abcdef01234567890abcdef",
"gasPrice" : "1000000000",
"gas" : "100000000000"
}
},
"calldatacopy1": { "calldatacopy1": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
@ -535,6 +618,34 @@
} }
}, },
"codecopy_DataIndexTooHigh": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (CODECOPY 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "0x1234567890abcdef01234567890abcdef",
"gasPrice" : "1000000000",
"gas" : "100000000000"
}
},
"codecopy0": { "codecopy0": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
@ -686,6 +797,35 @@
} }
}, },
"extcodecopy_DataIndexTooHigh": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (EXTCODECOPY (ADDRESS) 0 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa 8 ) [[ 0 ]] @0}",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "0x1234567890abcdef01234567890abcdef",
"gasPrice" : "1000000000",
"gas" : "100000000000"
}
},
"extcodecopy0": { "extcodecopy0": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

28
test/vmLogTestFiller.json

@ -55,6 +55,34 @@
} }
}, },
"log_2logs": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (LOG0 0 32) (LOG0 2 16) }",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"log0_nonEmptyMem_logMemSize1": { "log0_nonEmptyMem_logMemSize1": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

9
windows/LibEthereum.vcxproj.filters

@ -196,14 +196,8 @@
<ClCompile Include="..\libevmcore\Assembly.cpp"> <ClCompile Include="..\libevmcore\Assembly.cpp">
<Filter>libevmcore</Filter> <Filter>libevmcore</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\libevm\VMFace.cpp">
<Filter>libevm</Filter>
</ClCompile>
<ClCompile Include="..\libdevcrypto\AES.cpp" /> <ClCompile Include="..\libdevcrypto\AES.cpp" />
<ClCompile Include="..\libdevcrypto\ECDHE.cpp" /> <ClCompile Include="..\libdevcrypto\ECDHE.cpp" />
<ClCompile Include="..\libethereum\VMFactory.cpp">
<Filter>libethereum</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="stdafx.h"> <ClInclude Include="stdafx.h">
@ -443,9 +437,6 @@
<ClInclude Include="..\libevm\VMFace.h"> <ClInclude Include="..\libevm\VMFace.h">
<Filter>libevm</Filter> <Filter>libevm</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\libethereum\VMFactory.h">
<Filter>libethereum</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="Windows"> <Filter Include="Windows">

Loading…
Cancel
Save