Browse Source

Merge branch 'develop' into natspec_export_json

cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
3f19a1585a
  1. 2
      CMakeLists.txt
  2. 4
      alethzero/MainWin.cpp
  3. 6
      eth/main.cpp
  4. 2
      libdevcore/Common.cpp
  5. 2
      libethcore/CommonEth.cpp
  6. 2
      libethereum/MessageFilter.cpp
  7. 6
      libevm/ExtVMFace.h
  8. 17
      libevm/VM.h
  9. 117
      libevmcore/Assembly.cpp
  10. 3
      libevmcore/Assembly.h
  11. 2
      libevmcore/Instruction.h
  12. 33
      libp2p/Host.cpp
  13. 6
      libp2p/Host.h
  14. 12
      libsolidity/AST.cpp
  15. 23
      libsolidity/AST.h
  16. 2
      libsolidity/Compiler.cpp
  17. 10
      libsolidity/DeclarationContainer.cpp
  18. 10
      libsolidity/DeclarationContainer.h
  19. 19
      libsolidity/NameAndTypeResolver.cpp
  20. 14
      libsolidity/NameAndTypeResolver.h
  21. 197
      libsolidity/Scanner.cpp
  22. 14
      libsolidity/Token.h
  23. 2
      libsolidity/Types.cpp
  24. 2
      libwhisper/WhisperHost.cpp
  25. 34
      libwhisper/WhisperPeer.cpp
  26. 1
      mix/.gitignore
  27. 36
      mix/ApplicationCtx.cpp
  28. 55
      mix/ApplicationCtx.h
  29. 94
      mix/CMakeLists.txt
  30. 88
      mix/CodeEditorExtensionManager.cpp
  31. 61
      mix/CodeEditorExtensionManager.h
  32. 97
      mix/ConstantCompilationCtrl.cpp
  33. 55
      mix/ConstantCompilationCtrl.h
  34. 61
      mix/ConstantCompilationModel.cpp
  35. 51
      mix/ConstantCompilationModel.h
  36. 38
      mix/EthereumMacOSXBundleInfo.plist.in
  37. 44
      mix/Extension.cpp
  38. 48
      mix/Extension.h
  39. 45
      mix/MixApplication.cpp
  40. 46
      mix/MixApplication.h
  41. 40
      mix/main.cpp
  42. 8
      mix/qml.qrc
  43. 35
      mix/qml/BasicContent.qml
  44. 54
      mix/qml/MainContent.qml
  45. 23
      mix/qml/TabStyle.qml
  46. 24
      mix/qml/main.qml
  47. 6
      neth/main.cpp
  48. 42
      test/TestHelper.cpp
  49. 2
      test/TestHelper.h
  50. 25
      test/createRandomTest.cpp
  51. 30
      test/solidityNameAndTypeResolution.cpp
  52. 37
      test/stExampleFiller.json
  53. 12
      test/stSystemOperationsTestFiller.json
  54. 3
      test/state.cpp
  55. 6
      test/vm.cpp
  56. 4
      test/vm.h

2
CMakeLists.txt

@ -158,6 +158,7 @@ if (NOT LANGUAGES)
add_subdirectory(libqethereum) add_subdirectory(libqethereum)
add_subdirectory(alethzero) add_subdirectory(alethzero)
add_subdirectory(third) add_subdirectory(third)
add_subdirectory(mix)
if(QTQML) if(QTQML)
#add_subdirectory(iethxi) #add_subdirectory(iethxi)
#add_subdirectory(walleth) // resurect once we want to submit ourselves to QML. #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) #unset(TARGET_PLATFORM CACHE)

4
alethzero/MainWin.cpp

@ -1261,6 +1261,9 @@ void Main::on_blocks_currentItemChanged()
s << "<br/>Gas used/limit: <b>" << info.gasUsed << "</b>/<b>" << info.gasLimit << "</b>"; s << "<br/>Gas used/limit: <b>" << info.gasUsed << "</b>/<b>" << info.gasLimit << "</b>";
s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress; s << "<br/>Coinbase: <b>" << pretty(info.coinbaseAddress).toHtmlEscaped().toStdString() << "</b> " << info.coinbaseAddress;
s << "<br/>Nonce: <b>" << info.nonce << "</b>"; 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/>Parent: <b>" << info.parentHash << "</b>";
// s << "<br/>Bloom: <b>" << details.bloom << "</b>"; // s << "<br/>Bloom: <b>" << details.bloom << "</b>";
s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>"; s << "<br/>Log Bloom: <b>" << info.logBloom << "</b>";
@ -1281,6 +1284,7 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1]) 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/>" << 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/>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 else
{ {

6
eth/main.cpp

@ -337,10 +337,10 @@ int main(int argc, char** argv)
web3.connect(remoteHost, remotePort); web3.connect(remoteHost, remotePort);
#if ETH_JSONRPC #if ETH_JSONRPC
auto_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
if (jsonrpc > -1) 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->setIdentities({us});
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }
@ -428,7 +428,7 @@ int main(int argc, char** argv)
{ {
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = 8080; 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->setIdentities({us});
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }

2
libdevcore/Common.cpp

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

2
libethcore/CommonEth.cpp

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

2
libethereum/MessageFilter.cpp

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

6
libevm/ExtVMFace.h

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

17
libevm/VM.h

@ -53,9 +53,6 @@ inline Address asAddress(u256 _item)
inline u256 fromAddress(Address _a) inline u256 fromAddress(Address _a)
{ {
return (u160)_a; return (u160)_a;
// h256 ret;
// memcpy(&ret, &_a, sizeof(_a));
// return ret;
} }
/** /**
@ -173,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: // These all operate on memory and therefore potentially expand it:
case Instruction::MSTORE: case Instruction::MSTORE:
require(2); require(2);
newTempSize = m_stack.back() + 32; newTempSize = (bigint)m_stack.back() + 32;
break; break;
case Instruction::MSTORE8: case Instruction::MSTORE8:
require(2); require(2);
newTempSize = m_stack.back() + 1; newTempSize = (bigint)m_stack.back() + 1;
break; break;
case Instruction::MLOAD: case Instruction::MLOAD:
require(1); require(1);
newTempSize = m_stack.back() + 32; newTempSize = (bigint)m_stack.back() + 32;
break; break;
case Instruction::RETURN: case Instruction::RETURN:
require(2); require(2);
@ -236,10 +233,10 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::CREATE: case Instruction::CREATE:
{ {
require(3); require(3);
auto inOff = m_stack[m_stack.size() - 2]; u256 inOff = m_stack[m_stack.size() - 2];
auto inSize = m_stack[m_stack.size() - 3]; u256 inSize = m_stack[m_stack.size() - 3];
newTempSize = inOff + inSize; newTempSize = (bigint)inOff + inSize;
runGas = c_createGas; runGas = c_createGas;
break; break;
} }
case Instruction::EXP: case Instruction::EXP:

117
libevmcore/Assembly.cpp

@ -27,6 +27,32 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; 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 int AssemblyItem::deposit() const
{ {
switch (m_type) switch (m_type)
@ -51,32 +77,7 @@ unsigned Assembly::bytesRequired() const
ret += i.second.size(); ret += i.second.size();
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
switch (i.m_type) ret += i.bytesRequired(br);
{
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());
}
if (dev::bytesRequired(ret) <= br) if (dev::bytesRequired(ret) <= br)
return ret; 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); 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; }; struct OptimiserChannel: public LogChannel { static const char* name() { return "OPT"; } static const int verbosity = 12; };
#define copt dev::LogOutputStream<OptimiserChannel, true>() #define copt dev::LogOutputStream<OptimiserChannel, true>()
@ -258,7 +271,7 @@ Assembly& Assembly::optimise(bool _enable)
u256 mask = (u256(1) << testBit) - 1; u256 mask = (u256(1) << testBit) - 1;
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; 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::SUB, [](u256 a, u256 b)->u256{return a - b;} },
{ Instruction::DIV, [](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::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;} }, { 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::ADD, [](u256 a, u256 b)->u256{return a + b;} },
{ Instruction::MUL, [](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::OR, [](u256 a, u256 b)->u256{return a | b;} },
{ Instruction::XOR, [](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 = std::vector<pair<AssemblyItems, function<AssemblyItems(AssemblyItemsConstRef)>>> rules =
{ {
{ { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { 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, 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 }; } }); 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 // 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 // pop optimization, do not compute values that are popped again anyway
rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems rules.push_back({ { AssemblyItem(UndefinedItem), Instruction::POP }, [](AssemblyItemsConstRef m) -> AssemblyItems
@ -315,6 +333,29 @@ Assembly& Assembly::optimise(bool _enable)
return m.toVector(); return m.toVector();
return AssemblyItems(info.args, Instruction::POP); 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; copt << *this;
@ -336,15 +377,27 @@ Assembly& Assembly::optimise(bool _enable)
if (matches(vr, &r.first)) if (matches(vr, &r.first))
{ {
auto rw = r.second(vr); 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..."; 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()) if (j < rw.size())
m_items[i + j] = rw[j]; m_items[i + j] = rw[j];
else else
m_items.erase(m_items.begin() + i + rw.size()); m_items.erase(m_items.begin() + i + rw.size());
copt << AssemblyItemsConstRef(&rw);
count++; count++;
copt << "Now:\n" << m_items; copt << "Now:\n" << m_items;
} }

3
libevmcore/Assembly.h

@ -51,6 +51,9 @@ public:
AssemblyItemType type() const { return m_type; } AssemblyItemType type() const { return m_type; }
u256 data() const { return m_data; } 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; 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)); } 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 CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data RETURN, ///< halt execution returning output data
CALLCODE,
SUICIDE = 0xff ///< halt execution and register account for later deletion SUICIDE = 0xff ///< halt execution and register account for later deletion
}; };

33
libp2p/Host.cpp

@ -34,6 +34,7 @@
#include <set> #include <set>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <mutex>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
@ -78,7 +79,7 @@ std::vector<bi::address> Host::getInterfaceAddresses()
char *addrStr = inet_ntoa(addr); char *addrStr = inet_ntoa(addr);
bi::address address(bi::address::from_string(addrStr)); bi::address address(bi::address::from_string(addrStr));
if (!isLocalHostAddress(address)) if (!isLocalHostAddress(address))
addresses.push_back(ad.to_v4()); addresses.push_back(address.to_v4());
} }
WSACleanup(); WSACleanup();
@ -153,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) 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 try
{ {
upnp = new UPnP; upnp = new UPnP;
@ -199,7 +200,7 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool
m_clientVersion(_clientVersion), m_clientVersion(_clientVersion),
m_netPrefs(_n), m_netPrefs(_n),
m_ifAddresses(getInterfaceAddresses()), m_ifAddresses(getInterfaceAddresses()),
m_ioService(new ba::io_service), m_ioService(new ba::io_service(2)),
m_acceptor(new bi::tcp::acceptor(*m_ioService)), m_acceptor(new bi::tcp::acceptor(*m_ioService)),
m_socket(new bi::tcp::socket(*m_ioService)), m_socket(new bi::tcp::socket(*m_ioService)),
m_key(KeyPair::create()) m_key(KeyPair::create())
@ -227,7 +228,7 @@ void Host::stop()
{ {
{ {
// prevent m_run from being set to false at same time as set to true by start() // prevent m_run from being set to false at same time as set to true by start()
lock_guard<mutex> l(x_runtimer); Guard l(x_runTimer);
// once m_run is false the scheduler will shutdown network and stopWorking() // once m_run is false the scheduler will shutdown network and stopWorking()
m_run = false; m_run = false;
} }
@ -537,6 +538,15 @@ void Host::connect(std::shared_ptr<Node> const& _n)
if (!m_ioService) if (!m_ioService)
return; return;
// prevent concurrently connecting to a node; todo: better abstraction
Node *nptr = _n.get();
{
Guard l(x_pendingNodeConns);
if (m_pendingNodeConns.count(nptr))
return;
m_pendingNodeConns.insert(nptr);
}
clog(NetConnect) << "Attempting connection to node" << _n->id.abridged() << "@" << _n->address << "from" << id().abridged(); clog(NetConnect) << "Attempting connection to node" << _n->id.abridged() << "@" << _n->address << "from" << id().abridged();
_n->lastAttempted = std::chrono::system_clock::now(); _n->lastAttempted = std::chrono::system_clock::now();
_n->failedAttempts++; _n->failedAttempts++;
@ -559,6 +569,8 @@ void Host::connect(std::shared_ptr<Node> const& _n)
p->start(); p->start();
} }
delete s; delete s;
Guard l(x_pendingNodeConns);
m_pendingNodeConns.erase(nptr);
}); });
} }
@ -688,8 +700,7 @@ PeerInfos Host::peers(bool _updatePing) const
void Host::run(boost::system::error_code const& error) void Host::run(boost::system::error_code const& error)
{ {
static unsigned s_lasttick = 0; m_lastTick += c_timerInterval;
s_lasttick += c_timerInterval;
if (error || !m_ioService) if (error || !m_ioService)
{ {
@ -701,11 +712,11 @@ void Host::run(boost::system::error_code const& error)
// network running // network running
if (m_run) if (m_run)
{ {
if (s_lasttick >= c_timerInterval * 50) if (m_lastTick >= c_timerInterval * 10)
{ {
growPeers(); growPeers();
prunePeers(); prunePeers();
s_lasttick = 0; m_lastTick = 0;
} }
if (m_hadNewNodes) if (m_hadNewNodes)
@ -771,7 +782,7 @@ void Host::run(boost::system::error_code const& error)
m_socket->close(); m_socket->close();
// m_run is false, so we're stopping; kill timer // m_run is false, so we're stopping; kill timer
s_lasttick = 0; m_lastTick = 0;
// causes parent thread's stop() to continue which calls stopWorking() // causes parent thread's stop() to continue which calls stopWorking()
m_timer.reset(); m_timer.reset();
@ -794,7 +805,7 @@ void Host::startedWorking()
// prevent m_run from being set to true at same time as set to false by stop() // prevent m_run from being set to true at same time as set to false by stop()
// don't release mutex until m_timer is set so in case stop() is called at same // don't release mutex until m_timer is set so in case stop() is called at same
// time, stop will wait on m_timer and graceful network shutdown. // time, stop will wait on m_timer and graceful network shutdown.
lock_guard<mutex> l(x_runtimer); Guard l(x_runTimer);
// reset io service and create deadline timer // reset io service and create deadline timer
m_timer.reset(new boost::asio::deadline_timer(*m_ioService)); m_timer.reset(new boost::asio::deadline_timer(*m_ioService));
m_run = true; m_run = true;

6
libp2p/Host.h

@ -224,7 +224,7 @@ private:
Nodes potentialPeers(RangeMask<unsigned> const& _known); Nodes potentialPeers(RangeMask<unsigned> const& _known);
bool m_run = false; ///< Whether network is running. bool m_run = false; ///< Whether network is running.
std::mutex x_runtimer; ///< Start/stop mutex. std::mutex x_runTimer; ///< Start/stop mutex.
std::string m_clientVersion; ///< Our version string. std::string m_clientVersion; ///< Our version string.
@ -241,6 +241,10 @@ private:
std::unique_ptr<boost::asio::deadline_timer> m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms. std::unique_ptr<boost::asio::deadline_timer> m_timer; ///< Timer which, when network is running, calls scheduler() every c_timerInterval ms.
static const unsigned c_timerInterval = 100; ///< Interval which m_timer is run when network is connected. static const unsigned c_timerInterval = 100; ///< Interval which m_timer is run when network is connected.
unsigned m_lastTick = 0; ///< Used by run() for scheduling; must not be mutated outside of run().
std::set<Node*> m_pendingNodeConns; /// Used only by connect(Node&) to limit concurrently connecting to same node. See connect(shared_ptr<Node>const&).
Mutex x_pendingNodeConns;
bi::tcp::endpoint m_public; ///< Our public listening endpoint. bi::tcp::endpoint m_public; ///< Our public listening endpoint.
KeyPair m_key; ///< Our unique ID. KeyPair m_key; ///< Our unique ID.

12
libsolidity/AST.cpp

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

23
libsolidity/AST.h

@ -88,11 +88,16 @@ public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {} ASTNode(_location), m_name(_name) {}
/// Returns the declared name. /// @returns the declared name.
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Declaration* m_scope;
}; };
/** /**
@ -237,6 +242,8 @@ public:
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition*>(getScope()); }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -521,12 +528,16 @@ private:
*/ */
class Expression: public ASTNode class Expression: public ASTNode
{ {
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public: 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; virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; } 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 /// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception. /// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -541,9 +552,9 @@ public:
protected: protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements(). //! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;
//! Whether or not this expression is an lvalue, i.e. something that can be assigned to. //! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! This is set during calls to @a checkTypeRequirements() //! locally or in storage. This is set during calls to @a checkTypeRequirements()
bool m_isLvalue; LValueType m_lvalue;
//! Whether the outer expression requested the address (true) or the value (false) of this expression. //! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested; bool m_lvalueRequested;
}; };

2
libsolidity/Compiler.cpp

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

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

@ -20,7 +20,7 @@
* Scope - object that holds declaration of names. * Scope - object that holds declaration of names.
*/ */
#include <libsolidity/Scope.h> #include <libsolidity/DeclarationContainer.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
namespace dev namespace dev
@ -28,7 +28,7 @@ namespace dev
namespace solidity namespace solidity
{ {
bool Scope::registerDeclaration(Declaration& _declaration) bool DeclarationContainer::registerDeclaration(Declaration& _declaration)
{ {
if (m_declarations.find(_declaration.getName()) != m_declarations.end()) if (m_declarations.find(_declaration.getName()) != m_declarations.end())
return false; return false;
@ -36,13 +36,13 @@ bool Scope::registerDeclaration(Declaration& _declaration)
return true; 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); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())
return result->second; return result->second;
if (_recursive && m_enclosingScope) if (_recursive && m_enclosingContainer)
return m_enclosingScope->resolveName(_name, true); return m_enclosingContainer->resolveName(_name, true);
return nullptr; 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 * Container that stores mappings betwee names and declarations. It also contains a link to the
* enclosing scope. * enclosing scope.
*/ */
class Scope class DeclarationContainer
{ {
public: 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 /// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared. /// it was not yet declared.
bool registerDeclaration(Declaration& _declaration); bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Scope* getEnclosingScope() const { return m_enclosingScope; } Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
private: private:
Scope* m_enclosingScope; Declaration* m_enclosingDeclaration;
DeclarationContainer* m_enclosingContainer;
std::map<ASTString, Declaration*> m_declarations; std::map<ASTString, Declaration*> m_declarations;
}; };

19
libsolidity/NameAndTypeResolver.cpp

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

14
libsolidity/NameAndTypeResolver.h

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

197
libsolidity/Scanner.cpp

@ -194,7 +194,6 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _
return _else; return _else;
} }
bool Scanner::skipWhitespace() bool Scanner::skipWhitespace()
{ {
int const startPosition = getSourcePos(); int const startPosition = getSourcePos();
@ -204,7 +203,6 @@ bool Scanner::skipWhitespace()
return getSourcePos() != startPosition; return getSourcePos() != startPosition;
} }
Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::skipSingleLineComment()
{ {
// The line terminator at the end of the line is not considered // The line terminator at the end of the line is not considered
@ -215,7 +213,6 @@ Token::Value Scanner::skipSingleLineComment()
return Token::WHITESPACE; return Token::WHITESPACE;
} }
/// For the moment this function simply consumes a single line triple slash doc comment
Token::Value Scanner::scanDocumentationComment() Token::Value Scanner::scanDocumentationComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
@ -545,14 +542,12 @@ Token::Value Scanner::scanString()
return Token::STRING_LITERAL; return Token::STRING_LITERAL;
} }
void Scanner::scanDecimalDigits() void Scanner::scanDecimalDigits()
{ {
while (isDecimalDigit(m_char)) while (isDecimalDigit(m_char))
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
} }
Token::Value Scanner::scanNumber(char _charSeen) Token::Value Scanner::scanNumber(char _charSeen)
{ {
enum { DECIMAL, HEX, BINARY } kind = DECIMAL; enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
@ -623,186 +618,18 @@ Token::Value Scanner::scanNumber(char _charSeen)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Keyword Matcher // Keyword Matcher
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \ static Token::Value keywordOrIdentifierToken(string const& _input)
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)
{ {
if (asserts(!_input.empty())) // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored
BOOST_THROW_EXCEPTION(InternalCompilerError()); // and keywords to be put inside the keywords variable.
int const kMinLength = 2; #define KEYWORD(name, string, precedence) {string, Token::name},
int const kMaxLength = 10; #define TOKEN(name, string, precedence)
if (_input.size() < kMinLength || _input.size() > kMaxLength) static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)});
return Token::IDENTIFIER; #undef KEYWORD
switch (_input[0]) #undef TOKEN
{ auto it = keywords.find(_input);
default: return it == keywords.end() ? Token::IDENTIFIER : it->second;
#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;
} }
Token::Value Scanner::scanIdentifierOrKeyword() Token::Value Scanner::scanIdentifierOrKeyword()
@ -815,7 +642,7 @@ Token::Value Scanner::scanIdentifierOrKeyword()
while (isIdentifierPart(m_char)) while (isIdentifierPart(m_char))
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
literal.complete(); literal.complete();
return KeywordOrIdentifierToken(m_nextToken.literal); return keywordOrIdentifierToken(m_nextToken.literal);
} }
char CharStream::advanceAndGet(size_t _chars) char CharStream::advanceAndGet(size_t _chars)

14
libsolidity/Token.h

@ -314,25 +314,11 @@ public:
} }
// Predicates // 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 isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= 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 isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } 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) static Value AssignmentToBinaryOp(Value op)
{ {

2
libsolidity/Types.cpp

@ -295,9 +295,9 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
u256 offset; u256 offset;
for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers())
{ {
offset += variable->getType()->getStorageSize();
if (variable->getName() == _name) if (variable->getName() == _name)
return offset; return offset;
offset += variable->getType()->getStorageSize();
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
} }

2
libwhisper/WhisperHost.cpp

@ -118,7 +118,6 @@ unsigned WhisperHost::installWatch(shh::TopicFilter const& _f)
h256s WhisperHost::watchMessages(unsigned _watchId) h256s WhisperHost::watchMessages(unsigned _watchId)
{ {
cleanup();
h256s ret; h256s ret;
auto wit = m_watches.find(_watchId); auto wit = m_watches.find(_watchId);
if (wit == m_watches.end()) if (wit == m_watches.end())
@ -160,6 +159,7 @@ void WhisperHost::doWork()
{ {
for (auto& i: peers()) for (auto& i: peers())
i->cap<WhisperPeer>()->sendMessages(); i->cap<WhisperPeer>()->sendMessages();
cleanup();
} }
void WhisperHost::cleanup() void WhisperHost::cleanup()

34
libwhisper/WhisperPeer.cpp

@ -82,33 +82,23 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
void WhisperPeer::sendMessages() void WhisperPeer::sendMessages()
{ {
RLPStream amalg; if (m_unseen.size())
unsigned n = 0;
{ {
Guard l(x_unseen); RLPStream amalg;
while (m_unseen.size()) unsigned msgCount;
{ {
auto p = *m_unseen.begin(); Guard l(x_unseen);
m_unseen.erase(m_unseen.begin()); msgCount = m_unseen.size();
host()->streamMessage(p.second, amalg); while (m_unseen.size())
n++; {
auto p = *m_unseen.begin();
m_unseen.erase(m_unseen.begin());
host()->streamMessage(p.second, amalg);
}
} }
}
// the message subsystem should really just keep pumping out messages while m_unseen.size() and there's bandwidth for them.
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; RLPStream s;
prep(s, MessagesPacket, n).appendRaw(amalg.out(), n); prep(s, MessagesPacket, msgCount).appendRaw(amalg.out(), msgCount);
sealAndSend(s); sealAndSend(s);
} }
} }

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 -qmldir=${CMAKE_CURRENT_SOURCE_DIR}/qml ${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)

88
mix/CodeEditorExtensionManager.cpp

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

38
mix/EthereumMacOSXBundleInfo.plist.in

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

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.2
import QtQuick.Controls 1.1
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.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
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.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
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.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
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{
}
}

6
neth/main.cpp

@ -475,10 +475,10 @@ int main(int argc, char** argv)
c.startMining(); c.startMining();
#if ETH_JSONRPC #if ETH_JSONRPC
auto_ptr<WebThreeStubServer> jsonrpcServer; shared_ptr<WebThreeStubServer> jsonrpcServer;
if (jsonrpc > -1) 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->setIdentities({us});
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }
@ -552,7 +552,7 @@ int main(int argc, char** argv)
{ {
if (jsonrpc < 0) if (jsonrpc < 0)
jsonrpc = 8080; 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->setIdentities({us});
jsonrpcServer->StartListening(); jsonrpcServer->StartListening();
} }

42
test/TestHelper.cpp

@ -72,6 +72,7 @@ ImportTest::ImportTest(json_spirit::mObject& _o, bool isFiller): m_TestObject(_o
if (!isFiller) if (!isFiller)
{ {
importState(_o["post"].get_obj(), m_statePost); importState(_o["post"].get_obj(), m_statePost);
m_environment.sub.logs = importLog(_o["logs"].get_obj());
} }
} }
@ -148,6 +149,9 @@ void ImportTest::exportTest(bytes _output, State& _statePost)
// export output // export output
m_TestObject["out"] = "0x" + toHex(_output); m_TestObject["out"] = "0x" + toHex(_output);
// export logs
m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries());
// export post state // export post state
json_spirit::mObject postState; json_spirit::mObject postState;
@ -255,6 +259,44 @@ bytes importCode(json_spirit::mObject& _o)
return code; 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) void checkOutput(bytes const& _output, json_spirit::mObject& _o)
{ {
int j = 0; 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); byte toByte(json_spirit::mValue const& _v);
bytes importCode(json_spirit::mObject& _o); bytes importCode(json_spirit::mObject& _o);
bytes importData(json_spirit::mObject& _o); bytes importData(json_spirit::mObject& _o);
eth::LogEntries importLog(json_spirit::mObject& _o);
json_spirit::mObject exportLog(eth::LogEntries _logs);
void checkOutput(bytes const& _output, json_spirit::mObject& _o); void checkOutput(bytes const& _output, json_spirit::mObject& _o);
void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr); void checkStorage(std::map<u256, u256> _expectedStore, std::map<u256, u256> _resultStore, Address _expectedAddr);
void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);

25
test/createRandomTest.cpp

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

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

3
test/state.cpp

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

6
test/vm.cpp

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

4
test/vm.h

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

Loading…
Cancel
Save