Browse Source

Merge branch 'develop' into network

cl-refactor
subtly 10 years ago
parent
commit
957e7537e5
  1. 2
      CMakeLists.txt
  2. 11
      alethzero/MainWin.cpp
  3. 18
      eth/main.cpp
  4. 2
      libdevcore/Common.cpp
  5. 18
      libethcore/BlockInfo.cpp
  6. 4
      libethcore/BlockInfo.h
  7. 4
      libethcore/CommonEth.cpp
  8. 21
      libethereum/BlockChain.cpp
  9. 12
      libethereum/BlockChain.h
  10. 22
      libethereum/BlockDetails.h
  11. 14
      libethereum/Executive.cpp
  12. 2
      libethereum/Interface.h
  13. 2
      libethereum/MessageFilter.cpp
  14. 3
      libethereum/State.cpp
  15. 6
      libevm/ExtVMFace.h
  16. 6
      libevm/FeeStructure.cpp
  17. 6
      libevm/FeeStructure.h
  18. 28
      libevm/VM.h
  19. 117
      libevmcore/Assembly.cpp
  20. 3
      libevmcore/Assembly.h
  21. 2
      libevmcore/Instruction.h
  22. 6
      libp2p/Host.cpp
  23. 12
      libsolidity/AST.cpp
  24. 42
      libsolidity/AST.h
  25. 2
      libsolidity/Compiler.cpp
  26. 10
      libsolidity/DeclarationContainer.cpp
  27. 10
      libsolidity/DeclarationContainer.h
  28. 2
      libsolidity/ExpressionCompiler.cpp
  29. 2
      libsolidity/GlobalContext.cpp
  30. 19
      libsolidity/NameAndTypeResolver.cpp
  31. 14
      libsolidity/NameAndTypeResolver.h
  32. 7
      libsolidity/Parser.cpp
  33. 288
      libsolidity/Scanner.cpp
  34. 32
      libsolidity/Scanner.h
  35. 14
      libsolidity/Token.h
  36. 4
      libsolidity/Types.cpp
  37. 1
      libweb3jsonrpc/WebThreeStubServer.cpp
  38. 6
      libwhisper/WhisperHost.cpp
  39. 9
      libwhisper/WhisperHost.h
  40. 12
      libwhisper/WhisperPeer.cpp
  41. 2
      libwhisper/WhisperPeer.h
  42. 1
      mix/.gitignore
  43. 36
      mix/ApplicationCtx.cpp
  44. 55
      mix/ApplicationCtx.h
  45. 94
      mix/CMakeLists.txt
  46. 89
      mix/CodeEditorExtensionManager.cpp
  47. 61
      mix/CodeEditorExtensionManager.h
  48. 97
      mix/ConstantCompilationCtrl.cpp
  49. 55
      mix/ConstantCompilationCtrl.h
  50. 61
      mix/ConstantCompilationModel.cpp
  51. 51
      mix/ConstantCompilationModel.h
  52. 44
      mix/Extension.cpp
  53. 48
      mix/Extension.h
  54. 45
      mix/MixApplication.cpp
  55. 46
      mix/MixApplication.h
  56. 40
      mix/main.cpp
  57. 8
      mix/qml.qrc
  58. 35
      mix/qml/BasicContent.qml
  59. 54
      mix/qml/MainContent.qml
  60. 23
      mix/qml/TabStyle.qml
  61. 24
      mix/qml/main.qml
  62. 24
      neth/main.cpp
  63. 42
      test/TestHelper.cpp
  64. 2
      test/TestHelper.h
  65. 17
      test/createRandomTest.cpp
  66. 30
      test/solidityNameAndTypeResolution.cpp
  67. 161
      test/solidityParser.cpp
  68. 37
      test/stExampleFiller.json
  69. 12
      test/stSystemOperationsTestFiller.json
  70. 3
      test/state.cpp
  71. 6
      test/vm.cpp
  72. 4
      test/vm.h

2
CMakeLists.txt

@ -158,6 +158,7 @@ if (NOT LANGUAGES)
add_subdirectory(libqethereum)
add_subdirectory(alethzero)
add_subdirectory(third)
add_subdirectory(mix)
if(QTQML)
#add_subdirectory(iethxi)
#add_subdirectory(walleth) // resurect once we want to submit ourselves to QML.
@ -171,3 +172,4 @@ add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testet
#unset(TARGET_PLATFORM CACHE)

11
alethzero/MainWin.cpp

@ -1259,9 +1259,11 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>D/TD: <b>2^" << log2((double)info.difficulty) << "</b>/<b>2^" << log2((double)details.totalDifficulty) << "</b>";
s << "&nbsp;&emsp;&nbsp;Children: <b>" << details.children.size() << "</b></h5>";
s << "<br/>Gas used/limit: <b>" << info.gasUsed << "</b>/<b>" << info.gasLimit << "</b>";
s << "&nbsp;&emsp;&nbsp;Minimum gas price: <b>" << formatBalance(info.minGasPrice) << "</b>";
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>Nonce: <b>" << info.nonce << "</b>";
s << "<br/>Hash w/o nonce: <b>" << info.headerHashWithoutNonce() << "</b>";
s << "<br/>Difficulty: <b>" << info.difficulty << "</b>";
s << "<br/>Proof-of-Work: <b>" << ProofOfWork::eval(info.headerHashWithoutNonce(), info.nonce) << " &lt;= " << (h256)u256((bigint(1) << 256) / info.difficulty) << "</b>";
s << "<br/>Parent: <b>" << info.parentHash << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
@ -1282,6 +1284,7 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>";
s << "<br/>Dump: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[0].data()) << "</span>";
}
else
{
@ -1665,7 +1668,7 @@ void Main::on_data_textChanged()
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>");
}
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped());
ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 0));
ui->gas->setMinimum((qint64)Client::txGas(m_data, 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
@ -1676,7 +1679,7 @@ void Main::on_data_textChanged()
ui->code->setHtml(QString::fromStdString(dev::memDump(m_data, 8, true)));
if (ethereum()->codeAt(fromString(ui->destination->currentText()), 0).size())
{
ui->gas->setMinimum((qint64)Client::txGas(m_data.size(), 1));
ui->gas->setMinimum((qint64)Client::txGas(m_data, 1));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
@ -1685,7 +1688,7 @@ void Main::on_data_textChanged()
{
if (ui->gas->isEnabled())
m_backupGas = ui->gas->value();
ui->gas->setValue((qint64)Client::txGas(m_data.size()));
ui->gas->setValue((qint64)Client::txGas(m_data));
ui->gas->setEnabled(false);
}
}

18
eth/main.cpp

@ -337,10 +337,10 @@ int main(int argc, char** argv)
web3.connect(remoteHost, remotePort);
#if ETH_JSONRPC
auto_ptr<WebThreeStubServer> jsonrpcServer;
shared_ptr<WebThreeStubServer> jsonrpcServer;
if (jsonrpc > -1)
{
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us}));
jsonrpcServer = shared_ptr<WebThreeStubServer>(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us}));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -428,7 +428,7 @@ int main(int argc, char** argv)
{
if (jsonrpc < 0)
jsonrpc = 8080;
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(new jsonrpc::CorsHttpServer(jsonrpc), web3, {us}));
jsonrpcServer = make_shared<WebThreeStubServer>(new jsonrpc::CorsHttpServer(jsonrpc), web3, vector<KeyPair>({us}));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -492,14 +492,12 @@ int main(int argc, char** argv)
cnote << ssbd.str();
int ssize = sechex.length();
int size = hexAddr.length();
u256 minGas = (u256)Client::txGas(data.size(), 0);
u256 minGas = (u256)Client::txGas(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
@ -559,9 +557,9 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(0, 0);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(hexAddr));
c->transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice);
c->transact(us.secret(), amount, dest, bytes(), minGas);
}
}
else
@ -598,11 +596,9 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init.size(), 0);
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
cwarn << "Invalid endowment";
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev
{
char const* Version = "0.7.10";
char const* Version = "0.7.12";
}

18
libethcore/BlockInfo.cpp

@ -58,9 +58,9 @@ h256 BlockInfo::headerHashWithoutNonce() const
void BlockInfo::streamRLP(RLPStream& _s, bool _nonce) const
{
_s.appendList(_nonce ? 15 : 14)
_s.appendList(_nonce ? 14 : 13)
<< parentHash << sha3Uncles << coinbaseAddress << stateRoot << transactionsRoot << receiptsRoot << logBloom
<< difficulty << number << minGasPrice << gasLimit << gasUsed << timestamp << extraData;
<< difficulty << number << gasLimit << gasUsed << timestamp << extraData;
if (_nonce)
_s << nonce;
}
@ -86,12 +86,11 @@ void BlockInfo::populateFromHeader(RLP const& _header, bool _checkNonce)
logBloom = _header[field = 6].toHash<h512>();
difficulty = _header[field = 7].toInt<u256>();
number = _header[field = 8].toInt<u256>();
minGasPrice = _header[field = 9].toInt<u256>();
gasLimit = _header[field = 10].toInt<u256>();
gasUsed = _header[field = 11].toInt<u256>();
timestamp = _header[field = 12].toInt<u256>();
extraData = _header[field = 13].toBytes();
nonce = _header[field = 14].toHash<h256>();
gasLimit = _header[field = 9].toInt<u256>();
gasUsed = _header[field = 10].toInt<u256>();
timestamp = _header[field = 11].toInt<u256>();
extraData = _header[field = 12].toBytes();
nonce = _header[field = 13].toHash<h256>();
}
catch (Exception const& _e)
@ -147,9 +146,6 @@ void BlockInfo::verifyInternals(bytesConstRef _block) const
if (transactionsRoot != t.root())
BOOST_THROW_EXCEPTION(InvalidTransactionsHash(t.root(), transactionsRoot));
if (minGasPrice > mgp)
BOOST_THROW_EXCEPTION(InvalidMinGasPrice(minGasPrice, mgp));
if (sha3Uncles != sha3(root[2].data()))
BOOST_THROW_EXCEPTION(InvalidUnclesHash());
}

4
libethcore/BlockInfo.h

@ -66,7 +66,6 @@ public:
h512 logBloom; // TODO LogBloom - get include
u256 difficulty;
u256 number;
u256 minGasPrice;
u256 gasLimit;
u256 gasUsed;
u256 timestamp;
@ -95,7 +94,6 @@ public:
logBloom == _cmp.logBloom &&
difficulty == _cmp.difficulty &&
number == _cmp.number &&
minGasPrice == _cmp.minGasPrice &&
gasLimit == _cmp.gasLimit &&
gasUsed == _cmp.gasUsed &&
timestamp == _cmp.timestamp &&
@ -122,7 +120,7 @@ public:
inline std::ostream& operator<<(std::ostream& _out, BlockInfo const& _bi)
{
_out << _bi.hash << " " << _bi.parentHash << " " << _bi.sha3Uncles << " " << _bi.coinbaseAddress << " " << _bi.stateRoot << " " << _bi.transactionsRoot << " " <<
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.minGasPrice << " " << _bi.gasLimit << " " <<
_bi.receiptsRoot << " " << _bi.logBloom << " " << _bi.difficulty << " " << _bi.number << " " << _bi.gasLimit << " " <<
_bi.gasUsed << " " << _bi.timestamp << " " << _bi.nonce;
return _out;
}

4
libethcore/CommonEth.cpp

@ -33,8 +33,8 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 44;
const unsigned c_databaseVersion = 4;
const unsigned c_protocolVersion = 46;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =
{

21
libethereum/BlockChain.cpp

@ -101,9 +101,8 @@ bytes BlockChain::createGenesisBlock()
stateRoot = state.root();
}
block.appendList(15)
// TODO: maybe make logbloom correct?
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendList(14)
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42));
block.appendRaw(RLPEmptyList);
block.appendRaw(RLPEmptyList);
return block.out();
@ -169,8 +168,6 @@ void BlockChain::close()
delete m_db;
m_lastBlockHash = m_genesisHash;
m_details.clear();
m_blooms.clear();
m_traces.clear();
m_cache.clear();
}
@ -307,14 +304,10 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
State s(bi.coinbaseAddress, _db);
auto tdIncrease = s.enactOn(&_block, bi, *this);
auto b = s.oldBloom();
BlockBlooms bb;
BlockTraces bt;
BlockLogBlooms blb;
BlockReceipts br;
for (unsigned i = 0; i < s.pending().size(); ++i)
{
bb.blooms.push_back(s.changesFromPending(i).bloom());
bt.traces.push_back(s.changesFromPending(i));
blb.blooms.push_back(s.receipt(i).bloom());
br.receipts.push_back(s.receipt(i));
}
@ -330,14 +323,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_details[newHash] = BlockDetails((unsigned)pd.number + 1, td, bi.parentHash, {}, b);
m_details[bi.parentHash].children.push_back(newHash);
}
{
WriteGuard l(x_blooms);
m_blooms[newHash] = bb;
}
{
WriteGuard l(x_traces);
m_traces[newHash] = bt;
}
{
WriteGuard l(x_logBlooms);
m_logBlooms[newHash] = blb;
@ -349,8 +334,6 @@ h256s BlockChain::import(bytes const& _block, OverlayDB const& _db)
m_extrasDB->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)dev::ref(m_details[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(bi.parentHash), (ldb::Slice)dev::ref(m_details[bi.parentHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 1), (ldb::Slice)dev::ref(m_blooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 2), (ldb::Slice)dev::ref(m_traces[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 3), (ldb::Slice)dev::ref(m_logBlooms[newHash].rlp()));
m_extrasDB->Put(m_writeOptions, toSlice(newHash, 4), (ldb::Slice)dev::ref(m_receipts[newHash].rlp()));
m_db->Put(m_writeOptions, toSlice(newHash), (ldb::Slice)ref(_block));

12
libethereum/BlockChain.h

@ -101,14 +101,6 @@ public:
BlockDetails details(h256 _hash) const { return queryExtras<BlockDetails, 0>(_hash, m_details, x_details, NullBlockDetails); }
BlockDetails details() const { return details(currentHash()); }
/// Get the transactions' bloom filters of a block (or the most recent mined if none given). Thread-safe.
BlockBlooms blooms(h256 _hash) const { return queryExtras<BlockBlooms, 1>(_hash, m_blooms, x_blooms, NullBlockBlooms); }
BlockBlooms blooms() const { return blooms(currentHash()); }
/// Get the transactions' trace manifests of a block (or the most recent mined if none given). Thread-safe.
BlockTraces traces(h256 _hash) const { return queryExtras<BlockTraces, 2>(_hash, m_traces, x_traces, NullBlockTraces); }
BlockTraces traces() const { return traces(currentHash()); }
/// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe.
BlockLogBlooms logBlooms(h256 _hash) const { return queryExtras<BlockLogBlooms, 3>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
@ -193,10 +185,6 @@ private:
/// The caches of the disk DB and their locks.
mutable boost::shared_mutex x_details;
mutable BlockDetailsHash m_details;
mutable boost::shared_mutex x_blooms;
mutable BlockBloomsHash m_blooms;
mutable boost::shared_mutex x_traces;
mutable BlockTracesHash m_traces;
mutable boost::shared_mutex x_logBlooms;
mutable BlockLogBloomsHash m_logBlooms;
mutable boost::shared_mutex x_receipts;

22
libethereum/BlockDetails.h

@ -54,24 +54,6 @@ struct BlockDetails
h256 bloom;
};
struct BlockBlooms
{
BlockBlooms() {}
BlockBlooms(RLP const& _r) { blooms = _r.toVector<h256>(); }
bytes rlp() const { RLPStream s; s << blooms; return s.out(); }
h256s blooms;
};
struct BlockTraces
{
BlockTraces() {}
BlockTraces(RLP const& _r) { for (auto const& i: _r) traces.emplace_back(i.data()); }
bytes rlp() const { RLPStream s(traces.size()); for (auto const& i: traces) i.streamRLP(s); return s.out(); }
Manifests traces;
};
struct BlockLogBlooms
{
BlockLogBlooms() {}
@ -91,14 +73,10 @@ struct BlockReceipts
};
typedef std::map<h256, BlockDetails> BlockDetailsHash;
typedef std::map<h256, BlockBlooms> BlockBloomsHash;
typedef std::map<h256, BlockTraces> BlockTracesHash;
typedef std::map<h256, BlockLogBlooms> BlockLogBloomsHash;
typedef std::map<h256, BlockReceipts> BlockReceiptsHash;
static const BlockDetails NullBlockDetails;
static const BlockBlooms NullBlockBlooms;
static const BlockTraces NullBlockTraces;
static const BlockLogBlooms NullBlockLogBlooms;
static const BlockReceipts NullBlockReceipts;

14
libethereum/Executive.cpp

@ -22,6 +22,7 @@
#include <boost/timer.hpp>
#include <libdevcore/CommonIO.h>
#include <libevm/VM.h>
#include "Interface.h"
#include "Executive.h"
#include "State.h"
#include "ExtVM.h"
@ -58,15 +59,8 @@ bool Executive::setup(bytesConstRef _rlp)
BOOST_THROW_EXCEPTION(InvalidNonce(nonceReq, m_t.nonce()));
}
// Don't like transactions whose gas price is too low. NOTE: this won't stay here forever - it's just until we get a proper gas price discovery protocol going.
if (m_t.gasPrice() < m_s.m_currentBlock.minGasPrice)
{
clog(StateDetail) << "Offered gas-price is too low: Require >" << m_s.m_currentBlock.minGasPrice << " Got" << m_t.gasPrice();
BOOST_THROW_EXCEPTION(GasPriceTooLow());
}
// Check gas cost is enough.
u256 gasCost = m_t.data().size() * c_txDataGas + c_txGas;
auto gasCost = Interface::txGas(m_t.data());
if (m_t.gas() < gasCost)
{
@ -106,9 +100,9 @@ bool Executive::setup(bytesConstRef _rlp)
}
if (m_t.isCreation())
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - gasCost, &m_t.data(), m_sender);
return create(m_sender, m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)gasCost, &m_t.data(), m_sender);
else
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - gasCost, m_sender);
return call(m_t.receiveAddress(), m_sender, m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)gasCost, m_sender);
}
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)

2
libethereum/Interface.h

@ -128,7 +128,7 @@ public:
virtual Addresses addresses(int _block) const = 0;
/// Get the fee associated for a transaction with the given data.
static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; }
template <class T> static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
/// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const = 0;

2
libethereum/MessageFilter.cpp

@ -198,7 +198,7 @@ LogEntries LogFilter::matches(TransactionReceipt const& _m) const
if (!m_addresses.empty() && !m_addresses.count(e.address))
continue;
for (auto const& t: m_topics)
if (!e.topics.count(t))
if (!std::count(e.topics.begin(), e.topics.end(), t))
continue;
ret.push_back(e);
}

3
libethereum/State.cpp

@ -54,7 +54,7 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
memset(_out.data(), 0, _out.size());
if (in.v > 28)
if ((u256)in.v > 28)
return;
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid())
@ -501,7 +501,6 @@ void State::resetCurrent()
m_currentBlock.timestamp = time(0);
m_currentBlock.transactionsRoot = h256();
m_currentBlock.sha3Uncles = h256();
m_currentBlock.minGasPrice = 10 * szabo;
m_currentBlock.populateFromParent(m_previousBlock);
// Update timestamp according to clock.

6
libevm/ExtVMFace.h

@ -49,8 +49,8 @@ using LogBloom = h512;
struct LogEntry
{
LogEntry() {}
LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256Set)_r[1]; data = _r[2].toBytes(); }
LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(toSet(_ts)), data(std::move(_d)) {}
LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256s)_r[1]; data = _r[2].toBytes(); }
LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(_ts), data(std::move(_d)) {}
void streamRLP(RLPStream& _s) const { _s.appendList(3) << address << topics << data; }
@ -64,7 +64,7 @@ struct LogEntry
}
Address address;
h256Set topics;
h256s topics;
bytes data;
};

6
libevm/FeeStructure.cpp

@ -34,9 +34,13 @@ u256 const dev::eth::c_sstoreResetGas = 100;
u256 const dev::eth::c_sstoreRefundGas = 100;
u256 const dev::eth::c_createGas = 100;
u256 const dev::eth::c_callGas = 20;
u256 const dev::eth::c_expGas = 1;
u256 const dev::eth::c_expByteGas = 1;
u256 const dev::eth::c_memoryGas = 1;
u256 const dev::eth::c_txDataGas = 5;
u256 const dev::eth::c_txDataZeroGas = 1;
u256 const dev::eth::c_txDataNonZeroGas = 5;
u256 const dev::eth::c_txGas = 500;
u256 const dev::eth::c_logGas = 32;
u256 const dev::eth::c_logDataGas = 1;
u256 const dev::eth::c_logTopicGas = 32;
u256 const dev::eth::c_copyGas = 1;

6
libevm/FeeStructure.h

@ -37,12 +37,16 @@ extern u256 const c_sstoreResetGas; ///< Once per SSTORE operation if the zeron
extern u256 const c_sstoreRefundGas; ///< Refunded gas, once per SSTORE operation if the zeroness changes to zero.
extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction.
extern u256 const c_callGas; ///< Once per CALL operation & message call transaction.
extern u256 const c_expGas; ///< Once per EXP instuction.
extern u256 const c_expByteGas; ///< Times ceil(log256(exponent)) for the EXP instruction.
extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txDataZeroGas; ///< Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txDataNonZeroGas; ///< Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions.
extern u256 const c_logGas; ///< Per LOG* operation.
extern u256 const c_logDataGas; ///< Per byte in a LOG* operation's data.
extern u256 const c_logTopicGas; ///< Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
extern u256 const c_copyGas; ///< Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
}
}

28
libevm/VM.h

@ -53,9 +53,6 @@ inline Address asAddress(u256 _item)
inline u256 fromAddress(Address _a)
{
return (u160)_a;
// h256 ret;
// memcpy(&ret, &_a, sizeof(_a));
// return ret;
}
/**
@ -131,6 +128,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
// FEES...
bigint runGas = c_stepGas;
bigint newTempSize = m_temp.size();
bigint copySize = 0;
auto onOperation = [&]()
{
@ -172,15 +170,15 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
// These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE:
require(2);
newTempSize = m_stack.back() + 32;
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::MSTORE8:
require(2);
newTempSize = m_stack.back() + 1;
newTempSize = (bigint)m_stack.back() + 1;
break;
case Instruction::MLOAD:
require(1);
newTempSize = m_stack.back() + 32;
newTempSize = (bigint)m_stack.back() + 32;
break;
case Instruction::RETURN:
require(2);
@ -193,14 +191,17 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
break;
case Instruction::CALLDATACOPY:
require(3);
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::CODECOPY:
require(3);
copySize = m_stack[m_stack.size() - 3];
newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 3]);
break;
case Instruction::EXTCODECOPY:
require(4);
copySize = m_stack[m_stack.size() - 4];
newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]);
break;
@ -232,12 +233,19 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::CREATE:
{
require(3);
auto inOff = m_stack[m_stack.size() - 2];
auto inSize = m_stack[m_stack.size() - 3];
newTempSize = inOff + inSize;
u256 inOff = m_stack[m_stack.size() - 2];
u256 inSize = m_stack[m_stack.size() - 3];
newTempSize = (bigint)inOff + inSize;
runGas = c_createGas;
break;
}
case Instruction::EXP:
{
require(2);
auto expon = m_stack[m_stack.size() - 2];
runGas = c_expGas + c_expByteGas * (32 - (h256(expon).firstBitSet() / 8));
break;
}
case Instruction::PC:
case Instruction::MSIZE:
@ -304,7 +312,6 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::SDIV:
case Instruction::MOD:
case Instruction::SMOD:
case Instruction::EXP:
case Instruction::LT:
case Instruction::GT:
case Instruction::SLT:
@ -365,6 +372,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
newTempSize = (newTempSize + 31) / 32 * 32;
if (newTempSize > m_temp.size())
runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32;
runGas += c_copyGas * (copySize + 31) / 32;
onOperation();
// if (_onOp)

117
libevmcore/Assembly.cpp

@ -27,6 +27,32 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
{
switch (m_type)
{
case Operation:
case Tag: // 1 byte for the JUMPDEST
return 1;
case PushString:
return 33;
case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(m_data));
case PushSubSize:
return 4; // worst case: a 16MB program
case PushTag:
case PushData:
case PushSub:
return 1 + _addressLength;
case NoOptimizeBegin:
case NoOptimizeEnd:
return 0;
default:
break;
}
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
int AssemblyItem::deposit() const
{
switch (m_type)
@ -51,32 +77,7 @@ unsigned Assembly::bytesRequired() const
ret += i.second.size();
for (AssemblyItem const& i: m_items)
switch (i.m_type)
{
case Operation:
case Tag: // 1 byte for the JUMPDEST
ret++;
break;
case PushString:
ret += 33;
break;
case Push:
ret += 1 + max<unsigned>(1, dev::bytesRequired(i.m_data));
break;
case PushSubSize:
ret += 4; // worst case: a 16MB program
break;
case PushTag:
case PushData:
case PushSub:
ret += 1 + br;
break;
case NoOptimizeBegin:
case NoOptimizeEnd:
break;
default:
BOOST_THROW_EXCEPTION(InvalidOpcode());
}
ret += i.bytesRequired(br);
if (dev::bytesRequired(ret) <= br)
return ret;
}
@ -243,6 +244,18 @@ inline bool popCountIncreased(AssemblyItemsConstRef _pre, AssemblyItems const& _
return count_if(begin(_post), end(_post), isPop) > count_if(begin(_pre), end(_pre), isPop);
}
//@todo this has to move to a special optimizer class soon
template<class Iterator>
unsigned bytesRequiredBySlice(Iterator _begin, Iterator _end)
{
// this is only used in the optimizer, so we can provide a guess for the address length
unsigned addressLength = 4;
unsigned size = 0;
for (; _begin != _end; ++_begin)
size += _begin->bytesRequired(addressLength);
return size;
}
struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>()
@ -258,7 +271,7 @@ Assembly& Assembly::optimise(bool _enable)
u256 mask = (u256(1) << testBit) - 1;
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask;
};
map<Instruction, function<u256(u256, u256)>> c_simple =
map<Instruction, function<u256(u256, u256)>> const c_simple =
{
{ Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} },
{ Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} },
@ -273,7 +286,7 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} },
{ Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} },
};
map<Instruction, function<u256(u256, u256)>> c_associative =
map<Instruction, function<u256(u256, u256)>> const c_associative =
{
{ Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} },
{ Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} },
@ -281,6 +294,8 @@ Assembly& Assembly::optimise(bool _enable)
{ Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} },
{ Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} },
};
std::vector<pair<AssemblyItem, u256>> const c_identities =
{ { Instruction::ADD, 0}, { Instruction::MUL, 1}, { Instruction::MOD, 0}, { Instruction::OR, 0}, { Instruction::XOR, 0} };
std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
@ -299,8 +314,11 @@ Assembly& Assembly::optimise(bool _enable)
rules.push_back({ { Push, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[1].data(), m[0].data()) }; } });
rules.push_back({ { Push, i.first, Push, i.first }, [&](AssemblyItemsConstRef m) -> AssemblyItems { return { i.second(m[2].data(), m[0].data()), i.first }; } });
}
for (auto const& i: c_identities)
rules.push_back({{Push, i.first}, [&](AssemblyItemsConstRef m) -> AssemblyItems
{ return m[0].data() == i.second ? AssemblyItems() : m.toVector(); }});
// jump to next instruction
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [&](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }});
// pop optimization, do not compute values that are popped again anyway
rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems
@ -315,6 +333,29 @@ Assembly& Assembly::optimise(bool _enable)
return m.toVector();
return AssemblyItems(info.args, Instruction::POP);
} });
// compute constants close to powers of two by expressions
auto computeConstants = [](AssemblyItemsConstRef m) -> AssemblyItems
{
u256 const& c = m[0].data();
unsigned const minBits = 4 * 8;
if (c < (bigint(1) << minBits))
return m.toVector(); // we need at least "PUSH1 <bits> PUSH1 <2> EXP"
if (c == u256(-1))
return {u256(0), Instruction::NOT};
for (unsigned bits = minBits; bits < 256; ++bits)
{
bigint const diff = c - (bigint(1) << bits);
if (abs(diff) > 0xff)
continue;
AssemblyItems powerOfTwo{u256(bits), u256(2), Instruction::EXP};
if (diff == 0)
return powerOfTwo;
return AssemblyItems{u256(abs(diff))} + powerOfTwo +
AssemblyItems{diff > 0 ? Instruction::ADD : Instruction::SUB};
}
return m.toVector();
};
rules.push_back({{Push}, computeConstants});
copt << *this;
@ -336,15 +377,27 @@ Assembly& Assembly::optimise(bool _enable)
if (matches(vr, &r.first))
{
auto rw = r.second(vr);
if (rw.size() < vr.size() || (rw.size() == vr.size() && popCountIncreased(vr, rw)))
unsigned const vrSize = bytesRequiredBySlice(vr.begin(), vr.end());
unsigned const rwSize = bytesRequiredBySlice(rw.begin(), rw.end());
//@todo check the actual size (including constant sizes)
if (rwSize < vrSize || (rwSize == vrSize && popCountIncreased(vr, rw)))
{
copt << vr << "matches" << AssemblyItemsConstRef(&r.first) << "becomes...";
for (unsigned j = 0; j < vr.size(); ++j)
copt << AssemblyItemsConstRef(&rw);
if (rw.size() > vr.size())
{
// create hole in the vector
unsigned sizeIncrease = rw.size() - vr.size();
m_items.resize(m_items.size() + sizeIncrease, AssemblyItem(UndefinedItem));
move_backward(m_items.begin() + i, m_items.end() - sizeIncrease, m_items.end());
}
for (unsigned j = 0; j < max(rw.size(), vr.size()); ++j)
if (j < rw.size())
m_items[i + j] = rw[j];
else
m_items.erase(m_items.begin() + i + rw.size());
copt << AssemblyItemsConstRef(&rw);
count++;
copt << "Now:\n" << m_items;
}

3
libevmcore/Assembly.h

@ -51,6 +51,9 @@ public:
AssemblyItemType type() const { return m_type; }
u256 data() const { return m_data; }
/// @returns an upper bound for the number of bytes required by this item, assuming that
/// the value of a jump tag takes @a _addressLength bytes.
unsigned bytesRequired(unsigned _addressLength) const;
int deposit() const;
bool match(AssemblyItem const& _i) const { return _i.m_type == UndefinedItem || (m_type == _i.m_type && (m_type != Operation || m_data == _i.m_data)); }

2
libevmcore/Instruction.h

@ -168,8 +168,8 @@ enum class Instruction: uint8_t
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data
CALLCODE,
SUICIDE = 0xff ///< halt execution and register account for later deletion
};

6
libp2p/Host.cpp

@ -79,7 +79,7 @@ std::vector<bi::address> Host::getInterfaceAddresses()
char *addrStr = inet_ntoa(addr);
bi::address address(bi::address::from_string(addrStr));
if (!isLocalHostAddress(address))
addresses.push_back(ad.to_v4());
addresses.push_back(address.to_v4());
}
WSACleanup();
@ -154,9 +154,9 @@ int Host::listen4(bi::tcp::acceptor* _acceptor, unsigned short _listenPort)
bi::tcp::endpoint Host::traverseNAT(std::vector<bi::address> const& _ifAddresses, unsigned short _listenPort, bi::address& o_upnpifaddr)
{
asserts(_listenPort);
asserts(_listenPort != 0);
UPnP* upnp;
UPnP* upnp = nullptr;
try
{
upnp = new UPnP;

12
libsolidity/AST.cpp

@ -378,6 +378,9 @@ void Assignment::checkTypeRequirements()
{
m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings
if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_rightHandSide->expectType(*m_leftHandSide->getType());
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
@ -403,7 +406,7 @@ void Expression::expectType(Type const& _expectedType)
void Expression::requireLValue()
{
if (!isLvalue())
if (!isLValue())
BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
m_lvalueRequested = true;
}
@ -495,7 +498,8 @@ void MemberAccess::checkTypeRequirements()
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING);
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
}
void IndexAccess::checkTypeRequirements()
@ -507,7 +511,7 @@ void IndexAccess::checkTypeRequirements()
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLvalue = m_type->getCategory() != Type::Category::MAPPING;
m_lvalue = LValueType::STORAGE;
}
void Identifier::checkTypeRequirements()
@ -521,7 +525,7 @@ void Identifier::checkTypeRequirements()
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
m_isLvalue = true;
m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
return;
}
//@todo can we unify these with TypeName::toType()?

42
libsolidity/AST.h

@ -88,11 +88,16 @@ public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {}
/// Returns the declared name.
/// @returns the declared name.
ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; }
private:
ASTPointer<ASTString> m_name;
Declaration* m_scope;
};
/**
@ -173,14 +178,21 @@ private:
class FunctionDefinition: public Declaration
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, bool _isPublic,
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body) {}
Declaration(_location, _name), m_isPublic(_isPublic),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_returnParameters(_returnParameters),
m_body(_body),
m_documentation(_documentation)
{}
virtual void accept(ASTVisitor& _visitor) override;
bool isPublic() const { return m_isPublic; }
@ -190,6 +202,9 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
@ -203,6 +218,7 @@ private:
bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
std::vector<VariableDeclaration const*> m_localVariables;
};
@ -226,6 +242,8 @@ public:
std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition*>(getScope()); }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -510,12 +528,16 @@ private:
*/
class Expression: public ASTNode
{
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public:
Expression(Location const& _location): ASTNode(_location), m_isLvalue(false), m_lvalueRequested(false) {}
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLvalue() const { return m_isLvalue; }
bool isLValue() const { return m_lvalue != LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -530,9 +552,9 @@ public:
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
//! Whether or not this expression is an lvalue, i.e. something that can be assigned to.
//! This is set during calls to @a checkTypeRequirements()
bool m_isLvalue;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
LValueType m_lvalue;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested;
};

2
libsolidity/Compiler.cpp

@ -324,7 +324,7 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
Type::Category category = expression.getType()->getCategory();
// Type::Category category = expression.getType()->getCategory();
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
return false;

10
libsolidity/Scope.cpp → libsolidity/DeclarationContainer.cpp

@ -20,7 +20,7 @@
* Scope - object that holds declaration of names.
*/
#include <libsolidity/Scope.h>
#include <libsolidity/DeclarationContainer.h>
#include <libsolidity/AST.h>
namespace dev
@ -28,7 +28,7 @@ namespace dev
namespace solidity
{
bool Scope::registerDeclaration(Declaration& _declaration)
bool DeclarationContainer::registerDeclaration(Declaration& _declaration)
{
if (m_declarations.find(_declaration.getName()) != m_declarations.end())
return false;
@ -36,13 +36,13 @@ bool Scope::registerDeclaration(Declaration& _declaration)
return true;
}
Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const
Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
auto result = m_declarations.find(_name);
if (result != m_declarations.end())
return result->second;
if (_recursive && m_enclosingScope)
return m_enclosingScope->resolveName(_name, true);
if (_recursive && m_enclosingContainer)
return m_enclosingContainer->resolveName(_name, true);
return nullptr;
}

10
libsolidity/Scope.h → libsolidity/DeclarationContainer.h

@ -36,18 +36,20 @@ namespace solidity
* Container that stores mappings betwee names and declarations. It also contains a link to the
* enclosing scope.
*/
class Scope
class DeclarationContainer
{
public:
explicit Scope(Scope* _enclosingScope = nullptr): m_enclosingScope(_enclosingScope) {}
explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared.
bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Scope* getEnclosingScope() const { return m_enclosingScope; }
Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
private:
Scope* m_enclosingScope;
Declaration* m_enclosingDeclaration;
DeclarationContainer* m_enclosingContainer;
std::map<ASTString, Declaration*> m_declarations;
};

2
libsolidity/ExpressionCompiler.cpp

@ -363,7 +363,7 @@ void ExpressionCompiler::endVisit(Identifier& _identifier)
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
}
if (VariableDeclaration* varDef = dynamic_cast<VariableDeclaration*>(declaration))
if (/*VariableDeclaration* varDef = */dynamic_cast<VariableDeclaration*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);

2
libsolidity/GlobalContext.cpp

@ -58,7 +58,7 @@ GlobalContext::GlobalContext():
FunctionType::Location::ECRECOVER)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(160, IntegerType::Modifier::HASH)}),
FunctionType::Location::RIPEMD160))}
{
}

19
libsolidity/NameAndTypeResolver.cpp

@ -78,9 +78,9 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
return m_currentScope->resolveName(_name, _recursive);
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, Scope>& _scopes,
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
m_scopes(_scopes), m_currentScope(nullptr)
{
_astRoot.accept(*this);
}
@ -135,31 +135,30 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration)
{
map<ASTNode const*, Scope>::iterator iter;
map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded;
tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
if (asserts(newlyAdded))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope."));
m_currentScope = &iter->second;
m_currentScope = &_declaration;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope."));
m_currentScope = m_currentScope->getEnclosingScope();
m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration registered without scope."));
if (!m_currentScope->registerDeclaration(_declaration))
if (!m_scopes[m_currentScope].registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
//@todo the exception should also contain the location of the first declaration
_declaration.setScope(m_currentScope);
if (_opensScope)
enterNewSubScope(_declaration);
}

14
libsolidity/NameAndTypeResolver.h

@ -25,7 +25,7 @@
#include <map>
#include <boost/noncopyable.hpp>
#include <libsolidity/Scope.h>
#include <libsolidity/DeclarationContainer.h>
#include <libsolidity/ASTVisitor.h>
namespace dev
@ -61,9 +61,9 @@ private:
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code.
std::map<ASTNode const*, Scope> m_scopes;
std::map<ASTNode const*, DeclarationContainer> m_scopes;
Scope* m_currentScope;
DeclarationContainer* m_currentScope;
};
/**
@ -73,7 +73,7 @@ private:
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
DeclarationRegistrationHelper(std::map<ASTNode const*, Scope>& _scopes, ASTNode& _astRoot);
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
@ -85,12 +85,12 @@ private:
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
void enterNewSubScope(ASTNode& _node);
void enterNewSubScope(Declaration& _declaration);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode const*, Scope>& m_scopes;
Scope* m_currentScope;
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration* m_currentScope;
FunctionDefinition* m_currentFunction;
};

7
libsolidity/Parser.cpp

@ -117,6 +117,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters(parseParameterList());
@ -142,7 +146,8 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
}
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters,
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
parameters,
isDeclaredConst, returnParameters, block);
}

288
libsolidity/Scanner.cpp

@ -102,18 +102,55 @@ int hexValue(char c)
}
} // end anonymous namespace
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
enum LiteralType {
LITERAL_TYPE_STRING,
LITERAL_TYPE_NUMBER, // not really different from string type in behaviour
LITERAL_TYPE_COMMENT
};
class LiteralScope
{
public:
explicit LiteralScope(Scanner* _self, enum LiteralType _type): m_type(_type)
, m_scanner(_self)
, m_complete(false)
{
if (_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
else
m_scanner->m_nextToken.literal.clear();
}
~LiteralScope()
{
if (!m_complete)
{
if (m_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
else
m_scanner->m_nextToken.literal.clear();
}
}
void complete() { m_complete = true; }
private:
enum LiteralType m_type;
Scanner* m_scanner;
bool m_complete;
}; // end of LiteralScope class
void Scanner::reset(CharStream const& _source)
{
bool foundDocComment;
m_source = _source;
m_char = m_source.get();
skipWhitespace();
foundDocComment = scanToken();
scanToken();
// special version of Scanner:next() taking the previous scanToken() result into account
m_currentToken = m_nextToken;
if (scanToken() || foundDocComment)
m_skippedComment = m_nextSkippedComment;
next();
}
@ -142,8 +179,9 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
Token::Value Scanner::next()
{
m_currentToken = m_nextToken;
if (scanToken())
m_skippedComment = m_nextSkippedComment;
scanToken();
return m_currentToken.token;
}
@ -156,7 +194,6 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _
return _else;
}
bool Scanner::skipWhitespace()
{
int const startPosition = getSourcePos();
@ -166,7 +203,6 @@ bool Scanner::skipWhitespace()
return getSourcePos() != startPosition;
}
Token::Value Scanner::skipSingleLineComment()
{
// The line terminator at the end of the line is not considered
@ -177,13 +213,28 @@ Token::Value Scanner::skipSingleLineComment()
return Token::WHITESPACE;
}
/// For the moment this function simply consumes a single line triple slash doc comment
Token::Value Scanner::scanDocumentationComment()
{
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/'
while (!isSourcePastEndOfInput() && !isLineTerminator(m_char))
while (!isSourcePastEndOfInput())
{
if (isLineTerminator(m_char))
{
// check if next line is also a documentation comment
skipWhitespace();
if (!m_source.isPastEndOfInput(3) &&
m_source.get(0) == '/' &&
m_source.get(1) == '/' &&
m_source.get(2) == '/')
{
addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(3);
}
else
break; // next line is not a documentation comment, we are done
}
addCommentLiteralChar(m_char);
advance();
}
@ -214,10 +265,10 @@ Token::Value Scanner::skipMultiLineComment()
return Token::ILLEGAL;
}
bool Scanner::scanToken()
void Scanner::scanToken()
{
bool foundDocComment = false;
m_nextToken.literal.clear();
m_nextSkippedComment.literal.clear();
Token::Value token;
do
{
@ -329,7 +380,6 @@ bool Scanner::scanToken()
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
token = Token::WHITESPACE;
foundDocComment = true;
}
else
token = skipSingleLineComment();
@ -425,8 +475,6 @@ bool Scanner::scanToken()
while (token == Token::WHITESPACE);
m_nextToken.location.end = getSourcePos();
m_nextToken.token = token;
return foundDocComment;
}
bool Scanner::scanEscape()
@ -474,7 +522,7 @@ Token::Value Scanner::scanString()
{
char const quote = m_char;
advance(); // consume quote
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_STRING);
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char))
{
char c = m_char;
@ -494,18 +542,16 @@ Token::Value Scanner::scanString()
return Token::STRING_LITERAL;
}
void Scanner::scanDecimalDigits()
{
while (isDecimalDigit(m_char))
addLiteralCharAndAdvance();
}
Token::Value Scanner::scanNumber(char _charSeen)
{
enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_NUMBER);
if (_charSeen == '.')
{
// we have already seen a decimal point of the float
@ -572,209 +618,41 @@ Token::Value Scanner::scanNumber(char _charSeen)
// ----------------------------------------------------------------------------
// Keyword Matcher
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("address", Token::ADDRESS) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
KEYWORD("bool", Token::BOOL) \
KEYWORD_GROUP('c') \
KEYWORD("case", Token::CASE) \
KEYWORD("const", Token::CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD("contract", Token::CONTRACT) \
KEYWORD_GROUP('d') \
KEYWORD("default", Token::DEFAULT) \
KEYWORD("delete", Token::DELETE) \
KEYWORD("do", Token::DO) \
KEYWORD_GROUP('e') \
KEYWORD("else", Token::ELSE) \
KEYWORD("extends", Token::EXTENDS) \
KEYWORD_GROUP('f') \
KEYWORD("false", Token::FALSE_LITERAL) \
KEYWORD("for", Token::FOR) \
KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('h') \
KEYWORD("hash", Token::HASH) \
KEYWORD("hash8", Token::HASH8) \
KEYWORD("hash16", Token::HASH16) \
KEYWORD("hash24", Token::HASH24) \
KEYWORD("hash32", Token::HASH32) \
KEYWORD("hash40", Token::HASH40) \
KEYWORD("hash48", Token::HASH48) \
KEYWORD("hash56", Token::HASH56) \
KEYWORD("hash64", Token::HASH64) \
KEYWORD("hash72", Token::HASH72) \
KEYWORD("hash80", Token::HASH80) \
KEYWORD("hash88", Token::HASH88) \
KEYWORD("hash96", Token::HASH96) \
KEYWORD("hash104", Token::HASH104) \
KEYWORD("hash112", Token::HASH112) \
KEYWORD("hash120", Token::HASH120) \
KEYWORD("hash128", Token::HASH128) \
KEYWORD("hash136", Token::HASH136) \
KEYWORD("hash144", Token::HASH144) \
KEYWORD("hash152", Token::HASH152) \
KEYWORD("hash160", Token::HASH160) \
KEYWORD("hash168", Token::HASH168) \
KEYWORD("hash178", Token::HASH176) \
KEYWORD("hash184", Token::HASH184) \
KEYWORD("hash192", Token::HASH192) \
KEYWORD("hash200", Token::HASH200) \
KEYWORD("hash208", Token::HASH208) \
KEYWORD("hash216", Token::HASH216) \
KEYWORD("hash224", Token::HASH224) \
KEYWORD("hash232", Token::HASH232) \
KEYWORD("hash240", Token::HASH240) \
KEYWORD("hash248", Token::HASH248) \
KEYWORD("hash256", Token::HASH256) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("in", Token::IN) \
KEYWORD("int", Token::INT) \
KEYWORD("int8", Token::INT8) \
KEYWORD("int16", Token::INT16) \
KEYWORD("int24", Token::INT24) \
KEYWORD("int32", Token::INT32) \
KEYWORD("int40", Token::INT40) \
KEYWORD("int48", Token::INT48) \
KEYWORD("int56", Token::INT56) \
KEYWORD("int64", Token::INT64) \
KEYWORD("int72", Token::INT72) \
KEYWORD("int80", Token::INT80) \
KEYWORD("int88", Token::INT88) \
KEYWORD("int96", Token::INT96) \
KEYWORD("int104", Token::INT104) \
KEYWORD("int112", Token::INT112) \
KEYWORD("int120", Token::INT120) \
KEYWORD("int128", Token::INT128) \
KEYWORD("int136", Token::INT136) \
KEYWORD("int144", Token::INT144) \
KEYWORD("int152", Token::INT152) \
KEYWORD("int160", Token::INT160) \
KEYWORD("int168", Token::INT168) \
KEYWORD("int178", Token::INT176) \
KEYWORD("int184", Token::INT184) \
KEYWORD("int192", Token::INT192) \
KEYWORD("int200", Token::INT200) \
KEYWORD("int208", Token::INT208) \
KEYWORD("int216", Token::INT216) \
KEYWORD("int224", Token::INT224) \
KEYWORD("int232", Token::INT232) \
KEYWORD("int240", Token::INT240) \
KEYWORD("int248", Token::INT248) \
KEYWORD("int256", Token::INT256) \
KEYWORD_GROUP('l') \
KEYWORD_GROUP('m') \
KEYWORD("mapping", Token::MAPPING) \
KEYWORD_GROUP('n') \
KEYWORD("new", Token::NEW) \
KEYWORD("null", Token::NULL_LITERAL) \
KEYWORD_GROUP('p') \
KEYWORD("private", Token::PRIVATE) \
KEYWORD("public", Token::PUBLIC) \
KEYWORD_GROUP('r') \
KEYWORD("real", Token::REAL) \
KEYWORD("return", Token::RETURN) \
KEYWORD("returns", Token::RETURNS) \
KEYWORD_GROUP('s') \
KEYWORD("string", Token::STRING_TYPE) \
KEYWORD("struct", Token::STRUCT) \
KEYWORD("switch", Token::SWITCH) \
KEYWORD_GROUP('t') \
KEYWORD("text", Token::TEXT) \
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \
KEYWORD("uint8", Token::UINT8) \
KEYWORD("uint16", Token::UINT16) \
KEYWORD("uint24", Token::UINT24) \
KEYWORD("uint32", Token::UINT32) \
KEYWORD("uint40", Token::UINT40) \
KEYWORD("uint48", Token::UINT48) \
KEYWORD("uint56", Token::UINT56) \
KEYWORD("uint64", Token::UINT64) \
KEYWORD("uint72", Token::UINT72) \
KEYWORD("uint80", Token::UINT80) \
KEYWORD("uint88", Token::UINT88) \
KEYWORD("uint96", Token::UINT96) \
KEYWORD("uint104", Token::UINT104) \
KEYWORD("uint112", Token::UINT112) \
KEYWORD("uint120", Token::UINT120) \
KEYWORD("uint128", Token::UINT128) \
KEYWORD("uint136", Token::UINT136) \
KEYWORD("uint144", Token::UINT144) \
KEYWORD("uint152", Token::UINT152) \
KEYWORD("uint160", Token::UINT160) \
KEYWORD("uint168", Token::UINT168) \
KEYWORD("uint178", Token::UINT176) \
KEYWORD("uint184", Token::UINT184) \
KEYWORD("uint192", Token::UINT192) \
KEYWORD("uint200", Token::UINT200) \
KEYWORD("uint208", Token::UINT208) \
KEYWORD("uint216", Token::UINT216) \
KEYWORD("uint224", Token::UINT224) \
KEYWORD("uint232", Token::UINT232) \
KEYWORD("uint240", Token::UINT240) \
KEYWORD("uint248", Token::UINT248) \
KEYWORD("uint256", Token::UINT256) \
KEYWORD("ureal", Token::UREAL) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD_GROUP('w') \
KEYWORD("while", Token::WHILE) \
static Token::Value KeywordOrIdentifierToken(string const& _input)
static Token::Value keywordOrIdentifierToken(string const& _input)
{
if (asserts(!_input.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
int const kMinLength = 2;
int const kMaxLength = 10;
if (_input.size() < kMinLength || _input.size() > kMaxLength)
return Token::IDENTIFIER;
switch (_input[0])
{
default:
#define KEYWORD_GROUP_CASE(ch) \
break; \
case ch:
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
int const keywordLength = sizeof(keyword) - 1; \
BOOST_STATIC_ASSERT(keywordLength >= kMinLength); \
BOOST_STATIC_ASSERT(keywordLength <= kMaxLength); \
if (_input == keyword) \
return token; \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
}
return Token::IDENTIFIER;
// The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
// and keywords to be put inside the keywords variable.
#define KEYWORD(name, string, precedence) {string, Token::name},
#define TOKEN(name, string, precedence)
static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
#undef KEYWORD
#undef TOKEN
auto it = keywords.find(_input);
return it == keywords.end() ? Token::IDENTIFIER : it->second;
}
Token::Value Scanner::scanIdentifierOrKeyword()
{
if (asserts(isIdentifierStart(m_char)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
LiteralScope literal(this);
LiteralScope literal(this, LITERAL_TYPE_STRING);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
while (isIdentifierPart(m_char))
addLiteralCharAndAdvance();
literal.complete();
return KeywordOrIdentifierToken(m_nextToken.literal);
return keywordOrIdentifierToken(m_nextToken.literal);
}
char CharStream::advanceAndGet()
char CharStream::advanceAndGet(size_t _chars)
{
if (isPastEndOfInput())
return 0;
++m_pos;
m_pos += _chars;
if (isPastEndOfInput())
return 0;
return get();
return m_source[m_pos];
}
char CharStream::rollback(size_t _amount)

32
libsolidity/Scanner.h

@ -74,9 +74,9 @@ public:
CharStream(): m_pos(0) {}
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {}
int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; }
char advanceAndGet();
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); }
char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; }
char advanceAndGet(size_t _chars=1);
char rollback(size_t _amount);
///@{
@ -93,22 +93,11 @@ private:
};
class Scanner
{
friend class LiteralScope;
public:
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
class LiteralScope
{
public:
explicit LiteralScope(Scanner* self): m_scanner(self), m_complete(false) { m_scanner->startNewLiteral(); }
~LiteralScope() { if (!m_complete) m_scanner->dropLiteral(); }
void complete() { m_complete = true; }
private:
Scanner* m_scanner;
bool m_complete;
};
Scanner() { reset(CharStream()); }
explicit Scanner(CharStream const& _source) { reset(_source); }
@ -133,8 +122,12 @@ public:
///@{
///@name Information about the current comment token
Location getCurrentCommentLocation() const { return m_skippedComment.location; }
std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; }
/// Called by the parser during FunctionDefinition parsing to clear the current comment
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
///@}
///@{
@ -165,10 +158,8 @@ private:
///@{
///@name Literal buffer support
inline void startNewLiteral() { m_nextToken.literal.clear(); }
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
inline void dropLiteral() { m_nextToken.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
///@}
@ -181,9 +172,8 @@ private:
bool scanHexByte(char& o_scannedByte);
/// Scans a single Solidity token. Returns true if the scanned token was
/// a skipped documentation comment. False in all other cases.
bool scanToken();
/// Scans a single Solidity token.
void scanToken();
/// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace();

14
libsolidity/Token.h

@ -314,25 +314,11 @@ public:
}
// Predicates
static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; }
static bool isIdentifier(Value tok) { return tok == IDENTIFIER; }
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
static bool isOrderedRelationalCompareOp(Value op)
{
return op == LT || op == LTE || op == GT || op == GTE;
}
static bool isEqualityOp(Value op) { return op == EQ; }
static bool isInequalityOp(Value op) { return op == NE; }
static bool isArithmeticCompareOp(Value op)
{
return isOrderedRelationalCompareOp(op) ||
isEqualityOp(op) || isInequalityOp(op);
}
static Value AssignmentToBinaryOp(Value op)
{

4
libsolidity/Types.cpp

@ -295,9 +295,9 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
u256 offset;
for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers())
{
offset += variable->getType()->getStorageSize();
if (variable->getName() == _name)
return offset;
offset += variable->getType()->getStorageSize();
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
@ -397,11 +397,11 @@ MagicType::MagicType(MagicType::Kind _kind):
break;
case Kind::MSG:
m_members = MemberList({{"sender", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gas", make_shared<IntegerType const>(256)},
{"value", make_shared<IntegerType const>(256)}});
break;
case Kind::TX:
m_members = MemberList({{"origin", make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS)},
{"gas", make_shared<IntegerType const>(256)},
{"gasprice", make_shared<IntegerType const>(256)}});
break;
default:

1
libweb3jsonrpc/WebThreeStubServer.cpp

@ -51,7 +51,6 @@ static Json::Value toJson(dev::eth::BlockInfo const& _bi)
res["transactionsRoot"] = toJS(_bi.transactionsRoot);
res["difficulty"] = toJS(_bi.difficulty);
res["number"] = (int)_bi.number;
res["minGasPrice"] = toJS(_bi.minGasPrice);
res["gasLimit"] = (int)_bi.gasLimit;
res["timestamp"] = (int)_bi.timestamp;
res["extraData"] = jsFromBinary(_bi.extraData);

6
libwhisper/WhisperHost.cpp

@ -156,6 +156,12 @@ void WhisperHost::uninstallWatch(unsigned _i)
m_filters.erase(fit);
}
void WhisperHost::doWork()
{
for (auto& i: peers())
i->cap<WhisperPeer>()->sendMessages();
}
void WhisperHost::cleanup()
{
// remove old messages.

9
libwhisper/WhisperHost.h

@ -27,6 +27,7 @@
#include <memory>
#include <utility>
#include <libdevcore/RLP.h>
#include <libdevcore/Worker.h>
#include <libdevcore/Guards.h>
#include <libdevcrypto/SHA3.h>
#include "Common.h"
@ -38,7 +39,7 @@ namespace dev
namespace shh
{
class WhisperHost: public HostCapability<WhisperPeer>, public Interface
class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public Worker
{
friend class WhisperPeer;
@ -64,7 +65,13 @@ public:
void cleanup();
protected:
void doWork();
private:
virtual void onStarting() { startWorking(); }
virtual void onStopping() { stopWorking(); }
void streamMessage(h256 _m, RLPStream& _s) const;
void noteChanged(h256 _messageHash, h256 _filter);

12
libwhisper/WhisperPeer.cpp

@ -72,7 +72,6 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
for (auto i: _r)
if (n++)
host()->inject(Envelope(i), this);
sendMessages();
break;
}
default:
@ -97,10 +96,15 @@ void WhisperPeer::sendMessages()
}
}
if (!n)
// pause for a bit if no messages to send - this is horrible and broken.
// the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them.
this_thread::sleep_for(chrono::milliseconds(20));
auto diff = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - m_timer);
if (n || diff.count() > 0)
{
RLPStream s;
prep(s, MessagesPacket, n).appendRaw(amalg.out(), n);
sealAndSend(s);
m_timer = chrono::system_clock::now();
}
{
RLPStream s;

2
libwhisper/WhisperPeer.h

@ -68,6 +68,8 @@ private:
mutable dev::Mutex x_unseen;
std::map<unsigned, h256> m_unseen; ///< Rated according to what they want.
std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now();
};
}

1
mix/.gitignore

@ -0,0 +1 @@
*.pro

36
mix/ApplicationCtx.cpp

@ -0,0 +1,36 @@
/*
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 ApplicationCtx.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
*/
#include <QQmlApplicationEngine>
#include "ApplicationCtx.h"
using namespace dev::mix;
ApplicationCtx* ApplicationCtx::Instance = nullptr;
QQmlApplicationEngine* ApplicationCtx::appEngine()
{
return m_applicationEngine;
}
void ApplicationCtx::setApplicationContext(QQmlApplicationEngine* _engine)
{
if (Instance == nullptr)
Instance = new ApplicationCtx(_engine);
}

55
mix/ApplicationCtx.h

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Provide an access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
*/
#pragma once
#include <QQmlApplicationEngine>
namespace dev
{
namespace mix
{
class ApplicationCtx: public QObject
{
Q_OBJECT
public:
ApplicationCtx(QQmlApplicationEngine* _engine) { m_applicationEngine = _engine; }
~ApplicationCtx() { delete m_applicationEngine; }
static ApplicationCtx* getInstance() { return Instance; }
static void setApplicationContext(QQmlApplicationEngine* _engine);
QQmlApplicationEngine* appEngine();
private:
static ApplicationCtx* Instance;
QQmlApplicationEngine* m_applicationEngine;
public slots:
void quitApplication() { delete Instance; }
};
}
}

94
mix/CMakeLists.txt

@ -0,0 +1,94 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(..)
if (APPLE)
# Add homebrew path for qt5
set(CMAKE_PREFIX_PATH /usr/local/opt/qt5)
include_directories(/usr/local/opt/qt5/include /usr/local/include)
elseif ("${TARGET_PLATFORM}" STREQUAL "w64")
set(SRC_LIST ${SRC_LIST} ../windows/qt_plugin_import.cpp)
include_directories(/usr/x86_64-w64-mingw32/include /usr/x86_64-w64-mingw32/include/QtCore /usr/x86_64-w64-mingw32/include/QtGui /usr/x86_64-w64-mingw32/include/QtQuick /usr/x86_64-w64-mingw32/include/QtQml /usr/x86_64-w64-mingw32/include/QtNetwork /usr/x86_64-w64-mingw32/include/QtWidgets /usr/x86_64-w64-mingw32/include/QtWebKit /usr/x86_64-w64-mingw32/include/QtWebKitWidgets)
elseif (UNIX)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ";$ENV{QTDIR}/lib/cmake")
endif ()
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5WebKitWidgets REQUIRED)
#qt5_wrap_ui(ui_Main.h Main.ui)
qt5_add_resources(UI_RESOURCES qml.qrc)
# Set name of binary and add_executable()
file(GLOB HEADERS "*.h")
if (APPLE)
set(EXECUTEABLE mix)
set(BIN_INSTALL_DIR ".")
set(DOC_INSTALL_DIR ".")
set(PROJECT_VERSION "${ETH_VERSION}")
set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}")
set(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}")
set(MACOSX_BUNDLE_BUNDLE_NAME ${EXECUTEABLE})
set(MACOSX_BUNDLE_ICON_FILE mix)
include(BundleUtilities)
add_executable(${EXECUTEABLE} MACOSX_BUNDLE ${SRC_LIST} ${HEADERS} ${UI_RESOURCES})
set_target_properties(${EXECUTEABLE} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/EthereumMacOSXBundleInfo.plist.in")
SET_SOURCE_FILES_PROPERTIES(${EXECUTEABLE} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS)
SET_SOURCE_FILES_PROPERTIES(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
else ()
set(EXECUTEABLE mix)
add_executable(${EXECUTEABLE} ${SRC_LIST} ${HEADERS} ${UI_RESOURCES})
endif ()
qt5_use_modules(${EXECUTEABLE} Core)# Gui Widgets Network WebKit WebKitWidgets)
target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore devcrypto secp256k1 gmp ${CRYPTOPP_LS} serpent lll solidity evmcore devcore web3jsonrpc jsqrc)
if (APPLE)
# First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD
COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# This tool and next will inspect linked libraries in order to determine which dependencies are required
if (${CMAKE_CFG_INTDIR} STREQUAL ".")
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXECUTEABLE}.app")
else ()
set(APP_BUNDLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/\$ENV{CONFIGURATION}/${EXECUTEABLE}.app")
endif ()
install(CODE "
include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS 1)
fixup_bundle(\"${APP_BUNDLE_PATH}\" \"${BUNDLELIBS}\" \"../libqethereum ../libethereum ../secp256k1\")
" COMPONENT RUNTIME )
# Cleanup duplicate libs from macdeployqt
install(CODE "
file(GLOB LINGER_RM \"${APP_BUNDLE_PATH}/Contents/Frameworks/*.dylib\")
if (LINGER_RM)
file(REMOVE \${LINGER_RM})
endif ()
")
elseif (UNIX)
else ()
target_link_libraries(${EXECUTEABLE} boost_system)
target_link_libraries(${EXECUTEABLE} boost_filesystem)
find_package(Threads REQUIRED)
target_link_libraries(${EXECUTEABLE} ${CMAKE_THREAD_LIBS_INIT})
install( TARGETS ${EXECUTEABLE} RUNTIME DESTINATION bin )
endif ()
qt5_use_modules(${EXECUTEABLE} Core Gui)

89
mix/CodeEditorExtensionManager.cpp

@ -0,0 +1,89 @@
/*
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 CodeEditorExtensionMan.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QGraphicsObject>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickTextDocument>
#include <libevm/VM.h>
#include "ConstantCompilationCtrl.h"
#include "features.h"
#include "ApplicationCtx.h"
#include "CodeEditorExtensionManager.h"
using namespace dev::mix;
CodeEditorExtensionManager::~CodeEditorExtensionManager()
{
m_features.clear();
}
void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{
if (!_editor)
return;
try
{
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
{
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
if (qqdoc)
m_doc = qqdoc->textDocument();
}
}
catch (...)
{
qDebug() << "unable to load editor: ";
}
}
void CodeEditorExtensionManager::initExtensions()
{
//only one for now
std::shared_ptr<ConstantCompilationCtrl> constantCompilation = std::make_shared<ConstantCompilationCtrl>(m_doc);
if (constantCompilation.get()->contentUrl() != "")
{
try
{
constantCompilation.get()->addContentOn(m_tabView);
}
catch (...)
{
qDebug() << "Exception when adding content into view.";
return;
}
}
constantCompilation.get()->start();
m_features.append(constantCompilation);
}
void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)
{
this->loadEditor(_editor);
this->initExtensions();
}
void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView)
{
m_tabView = _tabView;
}

61
mix/CodeEditorExtensionManager.h

@ -0,0 +1,61 @@
/*
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 CodeEditorExtensionMan.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include "memory"
#include <QQuickItem>
#include <QTextDocument>
#include <QVector>
#include "ConstantCompilationCtrl.h"
namespace dev
{
namespace mix
{
class CodeEditorExtensionManager: public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor)
Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView)
public:
CodeEditorExtensionManager() {}
~CodeEditorExtensionManager();
void initExtensions();
void setEditor(QQuickItem*);
void setTabView(QQuickItem*);
private:
QQuickItem* m_editor;
QVector<std::shared_ptr<ConstantCompilationCtrl>> m_features;
QQuickItem* m_tabView;
QTextDocument* m_doc;
void loadEditor(QQuickItem*);
};
}
}

97
mix/ConstantCompilationCtrl.cpp

@ -0,0 +1,97 @@
/*
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 ConstantCompilation.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtCore/QtCore>
#include <QDebug>
#include "ConstantCompilationCtrl.h"
#include "ConstantCompilationModel.h"
using namespace dev::mix;
ConstantCompilationCtrl::ConstantCompilationCtrl(QTextDocument* _doc)
{
m_editor = _doc;
m_compilationModel = new ConstantCompilationModel();
}
ConstantCompilationCtrl::~ConstantCompilationCtrl()
{
delete m_compilationModel;
}
QString ConstantCompilationCtrl::contentUrl() const
{
return QStringLiteral("qrc:/qml/BasicContent.qml");
}
QString ConstantCompilationCtrl::title() const
{
return "compiler";
}
void ConstantCompilationCtrl::start() const
{
connect(m_editor, SIGNAL(contentsChange(int,int,int)), this, SLOT(compile()));
}
void ConstantCompilationCtrl::compile()
{
QString codeContent = m_editor->toPlainText().replace("\n", "");
if (codeContent.isEmpty())
{
resetOutPut();
return;
}
CompilerResult res = m_compilationModel->compile(m_editor->toPlainText());
writeOutPut(res);
}
void ConstantCompilationCtrl::resetOutPut()
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
status->setProperty("text", "");
content->setProperty("text", "");
}
void ConstantCompilationCtrl::writeOutPut(CompilerResult const& _res)
{
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);
if (_res.success)
{
status->setProperty("text", "succeeded");
status->setProperty("color", "green");
content->setProperty("text", _res.hexCode);
qDebug() << QString("compile succeeded " + _res.hexCode);
}
else
{
status->setProperty("text", "failure");
status->setProperty("color", "red");
content->setProperty("text", _res.comment);
qDebug() << QString("compile failed " + _res.comment);
}
}

55
mix/ConstantCompilationCtrl.h

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ConstantCompilation.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QTextDocument>
#include "ConstantCompilationModel.h"
#include "Extension.h"
namespace dev
{
namespace mix
{
class ConstantCompilationCtrl: public Extension
{
Q_OBJECT
public:
ConstantCompilationCtrl(QTextDocument*);
~ConstantCompilationCtrl();
void start() const override;
QString title() const override;
QString contentUrl() const override;
private:
QTextDocument* m_editor;
ConstantCompilationModel* m_compilationModel;
void writeOutPut(CompilerResult const&);
void resetOutPut();
public Q_SLOTS:
void compile();
};
}
}

61
mix/ConstantCompilationModel.cpp

@ -0,0 +1,61 @@
/*
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 ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QObject>
#include <libevm/VM.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include "ConstantCompilationModel.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
CompilerResult ConstantCompilationModel::compile(QString _code)
{
dev::solidity::CompilerStack compiler;
dev::bytes m_data;
CompilerResult res;
try
{
m_data = compiler.compile(_code.toStdString(), true);
res.success = true;
res.comment = "ok";
res.hexCode = QString::fromStdString(dev::eth::disassemble(m_data));
}
catch (dev::Exception const& _exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner());
res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped();
res.hexCode = "";
}
catch (...)
{
res.success = false;
res.comment = "Uncaught exception.";
res.hexCode = "";
}
return res;
}

51
mix/ConstantCompilationModel.h

@ -0,0 +1,51 @@
/*
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 ApplicationCtx.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
namespace dev
{
namespace mix
{
struct CompilerResult
{
QString hexCode;
QString comment;
bool success;
};
class ConstantCompilationModel
{
public:
ConstantCompilationModel() {}
~ConstantCompilationModel() {}
CompilerResult compile(QString code);
};
}
}

44
mix/Extension.cpp

@ -0,0 +1,44 @@
/*
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 Feature.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QMessageBox>
#include <QDebug>
#include <libevm/VM.h>
#include "Extension.h"
#include "ApplicationCtx.h"
using namespace dev;
using namespace dev::mix;
void Extension::addContentOn(QObject* _tabView)
{
if (contentUrl() == "")
return;
QVariant returnValue;
QQmlComponent* component = new QQmlComponent(
ApplicationCtx::getInstance()->appEngine(),
QUrl(this->contentUrl()), _tabView);
QMetaObject::invokeMethod(_tabView, "addTab",
Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(QVariant, this->title()),
Q_ARG(QVariant, QVariant::fromValue(component)));
m_view = qvariant_cast<QObject*>(returnValue);
}

48
mix/Extension.h

@ -0,0 +1,48 @@
/*
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 Feature.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QApplication>
#include <QQmlComponent>
namespace dev
{
namespace mix
{
class Extension: public QObject
{
Q_OBJECT
public:
Extension() {}
virtual QString contentUrl() const { return ""; }
virtual QString title() const { return ""; }
virtual void start() const {}
void addContentOn(QObject* tabView);
protected:
QObject* m_view;
};
}
}

45
mix/MixApplication.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/>.
*/
/** @file main.cpp
* @author Yann yann@ethdev.com
* @date 2014
*/
#include <QDebug>
#include "MixApplication.h"
using namespace dev::mix;
MixApplication::MixApplication(int _argc, char *_argv[]): QApplication(_argc, _argv)
{
}
bool MixApplication::notify(QObject* _receiver, QEvent* _event)
{
try
{
return MixApplication::notify(_receiver, _event);
}
catch (std::exception& _ex)
{
qDebug() << "std::exception was caught " << _ex.what();
}
catch (...)
{
qDebug() << "uncaught exception ";
}
return false;
}

46
mix/MixApplication.h

@ -0,0 +1,46 @@
/*
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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
* This class will be use instead of QApplication to launch the application. the method 'notify' allows to catch all exceptions.
* Not use for now: TODO.
*/
#pragma once
#include <QApplication>
namespace dev
{
namespace mix
{
class MixApplication: public QApplication
{
Q_OBJECT
public:
MixApplication(int _argc, char* _argv[]);
virtual ~MixApplication() {}
virtual bool notify(QObject* _receiver, QEvent* _event);
};
}
}

40
mix/main.cpp

@ -0,0 +1,40 @@
/*
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 main.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include "CodeEditorExtensionManager.h"
#include "ApplicationCtx.h"
#include "MixApplication.h"
using namespace dev::mix;
int main(int _argc, char *_argv[])
{
QApplication app(_argc, _argv);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QQmlApplicationEngine* engine = new QQmlApplicationEngine();
ApplicationCtx::setApplicationContext(engine);
QObject::connect(&app, SIGNAL(lastWindowClosed()), ApplicationCtx::getInstance(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
return app.exec();
}

8
mix/qml.qrc

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>qml/BasicContent.qml</file>
<file>qml/main.qml</file>
<file>qml/MainContent.qml</file>
<file>qml/TabStyle.qml</file>
</qresource>
</RCC>

35
mix/qml/BasicContent.qml

@ -0,0 +1,35 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
Rectangle {
anchors.fill: parent
width: parent.width
height: parent.height
color: "lightgray"
Text {
font.pointSize: 7
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 3
anchors.leftMargin: 3
height: 9
font.family: "Sego UI light"
objectName: "status"
id: status
}
TextArea {
readOnly: true
anchors.left: parent.left
anchors.leftMargin: 10
anchors.top: status.bottom
anchors.topMargin: 3
font.pointSize: 7
font.family: "Sego UI light"
height: parent.height * 0.8
width: parent.width - 20
wrapMode: Text.Wrap
backgroundVisible: false
objectName: "content"
id: content
}
}

54
mix/qml/MainContent.qml

@ -0,0 +1,54 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.2
import CodeEditorExtensionManager 1.0
Rectangle {
anchors.fill: parent
height: parent.height
width: parent.width;
id:root
SplitView {
anchors.fill: parent
orientation: Qt.Vertical
Rectangle {
anchors.top: parent.top
id: contentView
width: parent.width
height: parent.height * 0.7
TextArea {
id: codeEditor
height: parent.height
font.family: "Verdana"
font.pointSize: 9
width: parent.width
anchors.centerIn: parent
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
Rectangle {
anchors.bottom: parent.bottom
id: contextualView
width: parent.width
Layout.minimumHeight: 20
height: parent.height * 0.3
TabView {
id: contextualTabs
antialiasing: true
anchors.fill: parent
style: TabStyle {}
}
}
CodeEditorExtensionManager{
tabView: contextualTabs
editor: codeEditor
}
}
}

23
mix/qml/TabStyle.qml

@ -0,0 +1,23 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
TabViewStyle {
frameOverlap: 1
tabBar: Rectangle {
color: "lightgray"
}
tab: Rectangle {
color: "lightsteelblue"
implicitWidth: Math.max(text.width + 4, 80)
implicitHeight: 20
radius: 2
Text {
id: text
anchors.centerIn: parent
text: styleData.title
color: styleData.selected ? "white" : "black"
}
}
frame: Rectangle { color: "steelblue" }
}

24
mix/qml/main.qml

@ -0,0 +1,24 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import CodeEditorExtensionManager 1.0
ApplicationWindow {
visible: true
width: 1000
height: 480
minimumWidth: 400
minimumHeight: 300
title: qsTr("mix")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
MainContent{
}
}

24
neth/main.cpp

@ -475,10 +475,10 @@ int main(int argc, char** argv)
c.startMining();
#if ETH_JSONRPC
auto_ptr<WebThreeStubServer> jsonrpcServer;
shared_ptr<WebThreeStubServer> jsonrpcServer;
if (jsonrpc > -1)
{
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(new jsonrpc::HttpServer(jsonrpc), web3, {us}));
jsonrpcServer = make_shared<WebThreeStubServer>(new jsonrpc::HttpServer(jsonrpc), web3, vector<KeyPair>({us}));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -552,7 +552,7 @@ int main(int argc, char** argv)
{
if (jsonrpc < 0)
jsonrpc = 8080;
jsonrpcServer = auto_ptr<WebThreeStubServer>(new WebThreeStubServer(new jsonrpc::HttpServer(jsonrpc), web3, {us}));
jsonrpcServer = make_shared<WebThreeStubServer>(new jsonrpc::HttpServer(jsonrpc), web3, vector<KeyPair>({us}));
jsonrpcServer->setIdentities({us});
jsonrpcServer->StartListening();
}
@ -603,7 +603,7 @@ int main(int argc, char** argv)
vector<string> l;
l.push_back("Amount");
stringstream label;
label << "Gas price (" << info.minGasPrice << ")";
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
@ -646,14 +646,12 @@ int main(int argc, char** argv)
ssbd << bbd;
cnote << ssbd.str();
int ssize = fields[4].length();
u256 minGas = (u256)Client::txGas(data.size(), 0);
u256 minGas = (u256)Client::txGas(data, 0);
if (size < 40)
{
if (size > 0)
cwarn << "Invalid address length:" << size;
}
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else if (ssize < 40)
@ -702,9 +700,9 @@ int main(int argc, char** argv)
auto h = bc.currentHash();
auto blockData = bc.block(h);
BlockInfo info(blockData);
u256 minGas = (u256)Client::txGas(0, 0);
u256 minGas = (u256)Client::txGas(bytes(), 0);
Address dest = h160(fromHex(fields[0]));
c.transact(us.secret(), amount, dest, bytes(), minGas, info.minGasPrice);
c.transact(us.secret(), amount, dest, bytes(), minGas);
}
}
}
@ -718,7 +716,7 @@ int main(int argc, char** argv)
vector<string> l;
l.push_back("Endowment");
stringstream label;
label << "Gas price (" << info.minGasPrice << ")";
label << "Gas price";
l.push_back(label.str());
l.push_back("Gas");
vector<string> b;
@ -763,16 +761,14 @@ int main(int argc, char** argv)
cnote << "Init:";
cnote << ssc.str();
}
u256 minGas = (u256)Client::txGas(init.size(), 0);
u256 minGas = (u256)Client::txGas(init, 0);
if (endowment < 0)
cwarn << "Invalid endowment";
else if (gasPrice < info.minGasPrice)
cwarn << "Minimum gas price is" << info.minGasPrice;
else if (gas < minGas)
cwarn << "Minimum gas amount is" << minGas;
else
{
c.transact(us.secret(), endowment, init, gas, gasPrice);
c.transact(us.secret(), endowment, init, gas);
}
}
}

42
test/TestHelper.cpp

@ -72,6 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o
if (!isFiller)
{
importState(_o["post"].get_obj(), m_statePost);
m_environment.sub.logs = importLog(_o["logs"].get_obj());
}
}
@ -148,6 +149,9 @@ void ImportTest::exportTest(bytes _output, State& _statePost)
// export output
m_TestObject["out"] = "0x" + toHex(_output);
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
// export post state
json_spirit::mObject postState;
@ -255,6 +259,44 @@ bytes importCode(json_spirit::mObject& _o)
return code;
}
LogEntries importLog(json_spirit::mObject& _o)
{
LogEntries logEntries;
for (auto const& l: _o)
{
json_spirit::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);
logEntries.push_back(log);
}
return logEntries;
}
json_spirit::mObject exportLog(eth::LogEntries _logs)
{
json_spirit::mObject ret;
if (_logs.size() == 0) return ret;
for (LogEntry const& l: _logs)
{
json_spirit::mObject o;
o["address"] = toString(l.address);
json_spirit::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 checkOutput(bytes const& _output, json_spirit::mObject& _o)
{
int j = 0;

2
test/TestHelper.h

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

17
test/createRandomTest.cpp

@ -121,14 +121,14 @@ void doMyTests(json_spirit::mValue& v)
{
for (auto& i: v.get_obj())
{
cnote << i.first;
mObject& o = i.second.get_obj();
assert(o.count("env") > 0);
assert(o.count("pre") > 0);
assert(o.count("exec") > 0);
eth::VM vm;
test::FakeExtVM fev;
dev::test::FakeExtVM fev;
fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj());
@ -141,17 +141,20 @@ void doMyTests(json_spirit::mValue& v)
fev.code = fev.thisTxCode;
}
vm.reset(fev.gas);
bytes output;
eth::VM vm(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev).toBytes();
output = vm.go(fev, fev.simpleTrace()).toBytes();
gas = vm.gas();
}
catch (eth::VMException const& _e)
{
cnote << "VM did throw an exception: " << diagnostic_information(_e);
gas = 0;
vmExceptionOccured = true;
}
catch (Exception const& _e)
{
@ -180,9 +183,13 @@ void doMyTests(json_spirit::mValue& v)
o["env"] = mValue(fev.exportEnv());
o["exec"] = mValue(fev.exportExec());
if (!vmExceptionOccured)
{
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(test::exportLog(fev.sub.logs));
}
}
}

30
test/solidityNameAndTypeResolution.cpp

@ -244,6 +244,36 @@ BOOST_AUTO_TEST_CASE(balance_invalid)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(assignment_to_mapping)
{
char const* text = "contract test {\n"
" struct str {\n"
" mapping(uint=>uint) map;\n"
" }\n"
" str data;"
" function fun() {\n"
" var a = data.map;\n"
" data.map = a;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(assignment_to_struct)
{
char const* text = "contract test {\n"
" struct str {\n"
" mapping(uint=>uint) map;\n"
" }\n"
" str data;"
" function fun() {\n"
" var a = data;\n"
" data = a;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END()
}

161
test/solidityParser.cpp

@ -37,13 +37,14 @@ namespace test
namespace
{
ASTPointer<ASTNode> parseText(std::string const& _source)
ASTPointer<ContractDefinition> parseText(std::string const& _source)
{
Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source)));
}
}
BOOST_AUTO_TEST_SUITE(SolidityParser)
BOOST_AUTO_TEST_CASE(smoke_test)
@ -91,6 +92,164 @@ BOOST_AUTO_TEST_CASE(single_function_param)
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(function_natspec_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" function functionName(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is a test function");
}
BOOST_AUTO_TEST_CASE(function_normal_comments)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" // We won't see this comment\n"
" function functionName(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr,
"Should not have gotten a Natspect comment for this function");
}
BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is test function 1\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 2\n"
" function functionName2(hash hashin) returns (hash hashout) {}\n"
" // nothing to see here\n"
" function functionName3(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 4\n"
" function functionName4(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 1");
BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 2");
BOOST_REQUIRE_NO_THROW(function = functions.at(2));
BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr,
"Should not have gotten natspec comment for functionName3()");
BOOST_REQUIRE_NO_THROW(function = functions.at(3));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 4");
}
BOOST_AUTO_TEST_CASE(multiline_function_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" /// and it has 2 lines\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n"
" and it has 2 lines");
}
BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" /// fun1 description\n"
" function fun1(uint256 a) {\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" /// and it has 2 lines\n"
" function fun(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " fun1 description");
BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n"
" and it has 2 lines");
}
BOOST_AUTO_TEST_CASE(natspec_docstring_between_keyword_and_signature)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" function ///I am in the wrong place \n"
" fun1(uint256 a) {\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(!function->getDocumentation(),
"Shouldn't get natspec docstring for this function");
}
BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" function fun1(uint256 a) {\n"
" /// I should have been above the function signature\n"
" var b;\n"
" /// I should not interfere with actual natspec comments\n"
" uint256 c;\n"
" mapping(address=>hash) d;\n"
" string name = \"Solidity\";"
" }\n"
"}\n";
BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_MESSAGE(!function->getDocumentation(),
"Shouldn't get natspec docstring for this function");
}
BOOST_AUTO_TEST_CASE(struct_definition)
{
char const* text = "contract test {\n"

37
test/stExampleFiller.json

@ -0,0 +1,37 @@
{
"add11" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"code" : "0x6001600101600055",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "10000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "100000"
}
}
}

12
test/stSystemOperationsTestFiller.json

@ -12,7 +12,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -46,7 +46,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 1000 4 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -80,7 +80,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 0xfffffffffff 28) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -114,7 +114,7 @@
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }",
"code" : "{ (MSTORE 0 0x601080600c6000396000f30060003554156009570060203560003555) [[ 0 ]] (CREATE 23 4 0xfffffffffff) }",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
@ -195,7 +195,7 @@
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23",
"code" : "0x6001600155603760005360026000f2",
"code" : "0x6001600155603760005360026000f3",
"nonce" : "0",
"storage" : {
}
@ -321,7 +321,7 @@
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "23",
"code" : "0x6001600155603760005360026000f2",
"code" : "0x6001600155603760005360026000f3",
"nonce" : "0",
"storage" : {
}

3
test/state.cpp

@ -81,6 +81,9 @@ void doStateTests(json_spirit::mValue& v, bool _fillin)
// check output
checkOutput(output, o);
// check logs
checkLog(theState.pending().size() ? theState.log(0) : LogEntries(), importer.m_environment.sub.logs);
// check addresses
auto expectedAddrs = importer.m_statePost.addresses();
auto resultAddrs = theState.addresses();

6
test/vm.cpp

@ -149,7 +149,7 @@ void FakeExtVM::importLog(mObject& _o)
LogEntry log;
log.address = Address(o["address"].get_str());
for (auto const& t: o["topics"].get_array())
log.topics.insert(h256(t.get_str()));
log.topics.push_back(h256(t.get_str()));
log.data = importData(o);
sub.logs.push_back(log);
}
@ -384,7 +384,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(fev.exportLog());
o["logs"] = mValue(exportLog(fev.sub.logs));
}
}
else
@ -402,7 +402,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
dev::test::FakeExtVM test;
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.importLog(o["logs"].get_obj());
test.sub.logs = importLog(o["logs"].get_obj());
checkOutput(output, o);

4
test/vm.h

@ -66,14 +66,14 @@ public:
u256 doPosts();
json_spirit::mObject exportEnv();
void importEnv(json_spirit::mObject& _o);
json_spirit::mObject exportLog();
void importLog(json_spirit::mObject& _o);
json_spirit::mObject exportState();
void importState(json_spirit::mObject& _object);
json_spirit::mObject exportExec();
void importExec(json_spirit::mObject& _o);
json_spirit::mArray exportCallCreates();
void importCallCreates(json_spirit::mArray& _callcreates);
json_spirit::mObject exportLog();
void importLog(json_spirit::mObject& _o);
eth::OnOpFunc simpleTrace();

Loading…
Cancel
Save