diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bab77585..a3adde425 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,10 @@ function(configureProject) if (EVMJIT) add_definitions(-DETH_EVMJIT) endif() + + if (HEADLESS) + add_definitions(-DETH_HEADLESS) + endif() endfunction() @@ -65,13 +69,13 @@ function(createBuildInfo) endif () if (EVMJIT) - set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/JIT") + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/JIT") else () - set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/int") + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/int") endif () if (PARANOIA) - set(ETH_BUILD_PLATFORM "${TARGET_PLATFORM}/PARA") + set(ETH_BUILD_PLATFORM "${ETH_BUILD_PLATFORM}/PARA") endif () #cmake build type may be not specified when using msvc @@ -131,11 +135,15 @@ endif() add_subdirectory(libdevcore) add_subdirectory(libevmcore) add_subdirectory(liblll) -add_subdirectory(libserpent) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) + add_subdirectory(libserpent) +endif () add_subdirectory(libsolidity) add_subdirectory(lllc) add_subdirectory(solc) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) add_subdirectory(sc) +endif() if (JSONRPC) add_subdirectory(libweb3jsonrpc) @@ -164,6 +172,7 @@ endif () if (NOT HEADLESS) + add_subdirectory(libnatspec) add_subdirectory(libjsqrc) add_subdirectory(libqwebthree) add_subdirectory(alethzero) diff --git a/CodingStandards.txt b/CodingStandards.txt index 6188b9a70..78885ce25 100644 --- a/CodingStandards.txt +++ b/CodingStandards.txt @@ -93,7 +93,7 @@ b. Only one per line. c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). d. Favour declarations close to use; don't habitually declare at top of scope ala C. e. Always pass non-trivial parameters with a const& suffix. -f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. +f. If a func tion returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. g. Never use a macro where adequate non-preprocessor C++ can be written. h. Prefer "using NewType = OldType" to "typedef OldType NewType". @@ -128,7 +128,7 @@ d. Use override, final and const judiciously. e. No implementations with the class declaration, except: - template or force-inline method (though prefer implementation at bottom of header file). - one-line implementation (in which case include it in same line as declaration). -f. For a method 'foo' +f. For a property 'foo' - Member: m_foo; - Getter: foo() [ also: for booleans, isFoo() ]; - Setter: setFoo(); diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 219a8cb99..b58446935 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -40,13 +40,16 @@ target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} web3jsonrpc) target_link_libraries(${EXECUTABLE} jsqrc) +target_link_libraries(${EXECUTABLE} natspec) # eth_install_executable is defined in cmake/EthExecutableHelper.cmake eth_install_executable(${EXECUTABLE}) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2a0a056a5..9a29aff40 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -30,8 +30,10 @@ #include #include #include +#ifndef _MSC_VER #include #include +#endif #include #include #include @@ -1719,6 +1721,7 @@ void Main::on_data_textChanged() solidity = "

Solidity

Uncaught exception.
"; } } +#ifndef _MSC_VER else if (sourceIsSerpent(src)) { try @@ -1732,6 +1735,7 @@ void Main::on_data_textChanged() errors.push_back("Serpent " + err); } } +#endif else { m_data = compileLLL(src, m_enableOptimizer, &errors); @@ -1936,7 +1940,7 @@ void Main::on_send_clicked() { h256 contractHash = compiler.getContractCodeHash(s); m_natspecDB.add(contractHash, - compiler.getMetadata(s, dev::solidity::DocumentationType::NATSPEC_USER)); + compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser)); } } catch (...) diff --git a/alethzero/OurWebThreeStubServer.cpp b/alethzero/OurWebThreeStubServer.cpp index 37d37bce1..02d7236df 100644 --- a/alethzero/OurWebThreeStubServer.cpp +++ b/alethzero/OurWebThreeStubServer.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "MainWin.h" @@ -84,36 +85,9 @@ bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t) return showAuthenticationPopup("Unverified Pending Transaction", "An undocumented transaction is about to be executed."); - QNatspecExpressionEvaluator evaluator(this, m_main); + NatspecExpressionEvaluator evaluator; userNotice = evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString(); // otherwise it's a transaction to a contract for which we have the natspec return showAuthenticationPopup("Pending Transaction", userNotice); } - -QNatspecExpressionEvaluator::QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main) -: m_server(_server), m_main(_main) -{} - -QNatspecExpressionEvaluator::~QNatspecExpressionEvaluator() -{} - -QString QNatspecExpressionEvaluator::evalExpression(QString const& _expression) const -{ - // load natspec.js only when we need it for natspec evaluation - m_main->evalRaw(contentsOfQResource(":/js/natspec.js")); - QVariant result = m_main->evalRaw("evaluateExpression('" + _expression + "')"); - if (result.type() == QVariant::Invalid) - { - cerr << "Could not evaluate natspec expression: \"" << _expression.toStdString() << "\"" << endl; - // return the expression unevaluated - return _expression; - } - return result.toString(); -} - - - - - - diff --git a/alethzero/OurWebThreeStubServer.h b/alethzero/OurWebThreeStubServer.h index 16981f9e1..303b73111 100644 --- a/alethzero/OurWebThreeStubServer.h +++ b/alethzero/OurWebThreeStubServer.h @@ -47,19 +47,3 @@ private: dev::WebThreeDirect* m_web3; Main* m_main; }; - - -class QNatspecExpressionEvaluator: public QObject -{ - Q_OBJECT - -public: - QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main); - virtual ~QNatspecExpressionEvaluator(); - - QString evalExpression(QString const& _expression) const; - -private: - OurWebThreeStubServer* m_server; - Main* m_main; -}; diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 9b00a182f..8ff3d1460 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -122,6 +122,8 @@ if (NOT HEADLESS) string(REGEX REPLACE "npm" "" ETH_NPM_DIRECTORY ${ETH_NPM}) message(" - npm location : ${ETH_NPM}") + find_program(ETH_JSON_RPC_STUB jsonrpcstub) + endif() #HEADLESS # use multithreaded boost libraries, with -mt suffix diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 8ec5cd83c..30df7f26a 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -28,7 +28,7 @@ namespace jit namespace { -typedef ReturnCode(*EntryFuncPtr)(Runtime*); +using EntryFuncPtr = ReturnCode(*)(Runtime*); std::string codeHash(i256 const& _hash) { diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h index 652c7bc35..7e6133ced 100644 --- a/evmjit/libevmjit/Utils.h +++ b/evmjit/libevmjit/Utils.h @@ -1,4 +1,7 @@ #pragma once + +#include + #include "Common.h" namespace dev diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index dada9dd5d..883e76dd9 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -246,4 +246,13 @@ inline std::set<_T> operator+(std::set<_T> const& _a, std::set<_T> const& _b) /// Make normal string from fixed-length string. std::string toString(string32 const& _s); +template +std::vector keysOf(std::map const& _m) +{ + std::vector ret; + for (auto const& i: _m) + ret.push_back(i.first); + return ret; +} + } diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index e13d41476..f3f9b1bf0 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -44,10 +44,10 @@ struct FileError: virtual Exception {}; struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): m_f("Interface " + _f + " not supported.") {} virtual const char* what() const noexcept { return m_f.c_str(); } private: std::string m_f; }; // error information to be added to exceptions -typedef boost::error_info errinfo_invalidSymbol; -typedef boost::error_info errinfo_wrongAddress; -typedef boost::error_info errinfo_comment; -typedef boost::error_info errinfo_required; -typedef boost::error_info errinfo_got; -typedef boost::tuple RequirementError; +using errinfo_invalidSymbol = boost::error_info; +using errinfo_wrongAddress = boost::error_info; +using errinfo_comment = boost::error_info; +using errinfo_required = boost::error_info; +using errinfo_got = boost::error_info; +using RequirementError = boost::tuple; } diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h index 2eedbaba2..34a0fc65f 100644 --- a/libdevcore/RLP.h +++ b/libdevcore/RLP.h @@ -37,7 +37,7 @@ namespace dev { class RLP; -typedef std::vector RLPs; +using RLPs = std::vector; template struct intTraits { static const unsigned maxSize = sizeof(_T); }; template <> struct intTraits { static const unsigned maxSize = 20; }; @@ -125,7 +125,7 @@ public: /// @note if used to access items in ascending order, this is efficient. RLP operator[](unsigned _i) const; - typedef RLP element_type; + using element_type = RLP; /// @brief Iterator class for iterating through items of RLP list. class iterator @@ -133,8 +133,8 @@ public: friend class RLP; public: - typedef RLP value_type; - typedef RLP element_type; + using value_type = RLP; + using element_type = RLP; iterator& operator++(); iterator operator++(int) { auto ret = *this; operator++(); return ret; } diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 0cd3f8064..4b1df924d 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -12,9 +12,9 @@ template class vector_ref { public: - typedef _T value_type; - typedef _T element_type; - typedef typename std::conditional::value, typename std::remove_const<_T>::type, _T>::type mutable_value_type; + using value_type = _T; + using element_type = _T; + using mutable_value_type = typename std::conditional::value, typename std::remove_const<_T>::type, _T>::type; vector_ref(): m_data(nullptr), m_count(0) {} vector_ref(_T* _data, size_t _count): m_data(_data), m_count(_count) {} diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index f77f7fcff..4450aec4b 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -31,7 +31,7 @@ namespace crypto { /// Public key of remote and corresponding shared secret. -typedef std::pair AliasSession; +using AliasSession = std::pair; /** * @brief An addressable EC key pair. diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 62017796e..3d1bcaa2a 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -208,7 +208,7 @@ void BlockInfo::verifyParent(BlockInfo const& _parent) const if (parentHash != _parent.hash) BOOST_THROW_EXCEPTION(InvalidParentHash()); - if (timestamp < _parent.timestamp) + if (timestamp <= _parent.timestamp) BOOST_THROW_EXCEPTION(InvalidTimestamp()); if (number != _parent.number + 1) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index c530beef1..f71969525 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -32,35 +32,45 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 52; +const unsigned c_protocolVersion = 53; const unsigned c_databaseVersion = 5; -static const vector> g_units = +template constexpr u256 exp10() { - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Uether"}, - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Vether"}, - {((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Dether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000, "Nether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000, "Yether"}, - {(((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000, "Zether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000000000, "Eether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000000, "Pether"}, - {((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"}, - {(u256(1000000000) * 1000000000) * 1000000000, "Gether"}, - {(u256(1000000000) * 1000000000) * 1000000, "Mether"}, - {(u256(1000000000) * 1000000000) * 1000, "grand"}, - {u256(1000000000) * 1000000000, "ether"}, - {u256(1000000000) * 1000000, "finney"}, - {u256(1000000000) * 1000, "szabo"}, - {u256(1000000000), "Gwei"}, - {u256(1000000), "Mwei"}, - {u256(1000), "Kwei"}, - {u256(1), "wei"} -}; + return exp10() * u256(10); +} + +template <> constexpr u256 exp10<0>() +{ + return u256(1); +} vector> const& units() { - return g_units; + static const vector> s_units = + { + {exp10<54>(), "Uether"}, + {exp10<51>(), "Vether"}, + {exp10<48>(), "Dether"}, + {exp10<45>(), "Nether"}, + {exp10<42>(), "Yether"}, + {exp10<39>(), "Zether"}, + {exp10<36>(), "Eether"}, + {exp10<33>(), "Pether"}, + {exp10<30>(), "Tether"}, + {exp10<27>(), "Gether"}, + {exp10<24>(), "Mether"}, + {exp10<21>(), "grand"}, + {exp10<18>(), "ether"}, + {exp10<15>(), "finney"}, + {exp10<12>(), "szabo"}, + {exp10<9>(), "Gwei"}, + {exp10<6>(), "Mwei"}, + {exp10<3>(), "Kwei"}, + {exp10<0>(), "wei"} + }; + + return s_units; } std::string formatBalance(bigint const& _b) @@ -75,13 +85,13 @@ std::string formatBalance(bigint const& _b) else b = (u256)_b; - if (b > g_units[0].first * 10000) + if (b > units()[0].first * 10000) { - ret << (b / g_units[0].first) << " " << g_units[0].second; + ret << (b / units()[0].first) << " " << units()[0].second; return ret.str(); } ret << setprecision(5); - for (auto const& i: g_units) + for (auto const& i: units()) if (i.first != 1 && b >= i.first * 100) { ret << (double(b / (i.first / 1000)) / 1000.0) << " " << i.second; diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index b53a62562..6127a3d00 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -29,10 +29,10 @@ namespace eth { // information to add to exceptions -typedef boost::error_info errinfo_name; -typedef boost::error_info errinfo_field; -typedef boost::error_info errinfo_data; -typedef boost::tuple BadFieldError; +using errinfo_name = boost::error_info; +using errinfo_field = boost::error_info; +using errinfo_data = boost::error_info; +using BadFieldError = boost::tuple; struct DatabaseAlreadyOpen: virtual dev::Exception {}; struct NotEnoughCash: virtual dev::Exception {}; diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index d942d06f7..64ce502af 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -51,7 +51,7 @@ class ProofOfWorkEngine: public Evaluator public: static bool verify(h256 const& _root, h256 const& _nonce, u256 const& _difficulty) { return (bigint)(u256)Evaluator::eval(_root, _nonce) <= (bigint(1) << 256) / _difficulty; } - inline MineInfo mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false); + inline std::pair mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout = 100, bool _continue = true, bool _turbo = false); protected: h256 m_last; @@ -79,14 +79,14 @@ using SHA3ProofOfWork = ProofOfWorkEngine; using ProofOfWork = SHA3ProofOfWork; template -MineInfo ProofOfWorkEngine::mine(h256& o_solution, h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo) +std::pair ProofOfWorkEngine::mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo) { - MineInfo ret; + std::pair ret; static std::mt19937_64 s_eng((time(0) + (unsigned)m_last)); u256 s = (m_last = h256::random(s_eng)); bigint d = (bigint(1) << 256) / _difficulty; - ret.requirement = log2((double)d); + ret.first.requirement = log2((double)d); // 2^ 0 32 64 128 256 // [--------*-------------------------] @@ -95,20 +95,26 @@ MineInfo ProofOfWorkEngine::mine(h256& o_solution, h256 const& _root, auto startTime = std::chrono::steady_clock::now(); if (!_turbo) std::this_thread::sleep_for(std::chrono::milliseconds(_msTimeout * 90 / 100)); - for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, ret.hashes++) + double best = 1e99; // high enough to be effectively infinity :) + h256 solution; + unsigned h = 0; + for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++) { - o_solution = (h256)s; - auto e = (bigint)(u256)Evaluator::eval(_root, o_solution); - ret.best = std::min(ret.best, log2((double)e)); + solution = (h256)s; + auto e = (bigint)(u256)Evaluator::eval(_root, solution); + best = std::min(best, log2((double)e)); if (e <= d) { - ret.completed = true; + ret.first.completed = true; break; } } + ret.first.hashes = h; + ret.first.best = best; + ret.second = solution; - if (ret.completed) - assert(verify(_root, o_solution, _difficulty)); + if (ret.first.completed) + assert(verify(_root, solution, _difficulty)); return ret; } diff --git a/libethereum/BlockDetails.h b/libethereum/BlockDetails.h index bbfd48e56..61b9667c4 100644 --- a/libethereum/BlockDetails.h +++ b/libethereum/BlockDetails.h @@ -70,9 +70,9 @@ struct BlockReceipts TransactionReceipts receipts; }; -typedef std::map BlockDetailsHash; -typedef std::map BlockLogBloomsHash; -typedef std::map BlockReceiptsHash; +using BlockDetailsHash = std::map; +using BlockLogBloomsHash = std::map; +using BlockReceiptsHash = std::map; static const BlockDetails NullBlockDetails; static const BlockLogBlooms NullBlockLogBlooms; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index c5b6241d9..a42d1df99 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -126,7 +126,7 @@ void Client::killChain() m_tq.clear(); m_bq.clear(); - m_miners.clear(); + m_localMiners.clear(); m_preMine = State(); m_postMine = State(); @@ -167,8 +167,8 @@ void Client::clearPending() } { - ReadGuard l(x_miners); - for (auto& m: m_miners) + ReadGuard l(x_localMiners); + for (auto& m: m_localMiners) m.noteStateChange(); } @@ -223,13 +223,17 @@ void Client::uninstallWatch(unsigned _i) auto fit = m_filters.find(id); if (fit != m_filters.end()) if (!--fit->second.refCount) + { + cwatch << "*X*" << fit->first << ":" << fit->second.filter; m_filters.erase(fit); + } } void Client::noteChanged(h256Set const& _filters) { Guard l(m_filterLock); - cnote << "noteChanged(" << _filters << ")"; + if (_filters.size()) + cnote << "noteChanged(" << _filters << ")"; // accrue all changes left in each filter into the watches. for (auto& i: m_watches) if (_filters.count(i.second.id)) @@ -250,8 +254,11 @@ LocalisedLogEntries Client::peekWatch(unsigned _watchId) const Guard l(m_filterLock); try { - return m_watches.at(_watchId).changes; + auto& w = m_watches.at(_watchId); + w.lastPoll = chrono::system_clock::now(); + return w.changes; } catch (...) {} + return LocalisedLogEntries(); } @@ -261,7 +268,9 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId) LocalisedLogEntries ret; try { - std::swap(ret, m_watches.at(_watchId).changes); + auto& w = m_watches.at(_watchId); + std::swap(ret, w.changes); + w.lastPoll = chrono::system_clock::now(); } catch (...) {} return ret; @@ -311,8 +320,8 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) void Client::setForceMining(bool _enable) { m_forceMining = _enable; - ReadGuard l(x_miners); - for (auto& m: m_miners) + ReadGuard l(x_localMiners); + for (auto& m: m_localMiners) m.noteStateChange(); } @@ -321,19 +330,19 @@ void Client::setMiningThreads(unsigned _threads) stopMining(); auto t = _threads ? _threads : thread::hardware_concurrency(); - WriteGuard l(x_miners); - m_miners.clear(); - m_miners.resize(t); + WriteGuard l(x_localMiners); + m_localMiners.clear(); + m_localMiners.resize(t); unsigned i = 0; - for (auto& m: m_miners) + for (auto& m: m_localMiners) m.setup(this, i++); } MineProgress Client::miningProgress() const { MineProgress ret; - ReadGuard l(x_miners); - for (auto& m: m_miners) + ReadGuard l(x_localMiners); + for (auto& m: m_localMiners) ret.combine(m.miningProgress()); return ret; } @@ -342,13 +351,13 @@ std::list Client::miningHistory() { std::list ret; - ReadGuard l(x_miners); - if (m_miners.empty()) + ReadGuard l(x_localMiners); + if (m_localMiners.empty()) return ret; - ret = m_miners[0].miningHistory(); - for (unsigned i = 1; i < m_miners.size(); ++i) + ret = m_localMiners[0].miningHistory(); + for (unsigned i = 1; i < m_localMiners.size(); ++i) { - auto l = m_miners[i].miningHistory(); + auto l = m_localMiners[i].miningHistory(); auto ri = ret.begin(); auto li = l.begin(); for (; ri != ret.end() && li != l.end(); ++ri, ++li) @@ -465,6 +474,22 @@ void Client::inject(bytesConstRef _rlp) m_tq.attemptImport(_rlp); } +pair Client::getWork() +{ + Guard l(x_remoteMiner); + { + ReadGuard l(x_stateDB); + m_remoteMiner.update(m_postMine, m_bc); + } + return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); +} + +bool Client::submitNonce(h256 const&_nonce) +{ + Guard l(x_remoteMiner); + return m_remoteMiner.submitWork(_nonce); +} + void Client::doWork() { // TODO: Use condition variable rather than polling. @@ -472,27 +497,34 @@ void Client::doWork() cworkin << "WORK"; h256Set changeds; + auto maintainMiner = [&](Miner& m) { - ReadGuard l(x_miners); - for (auto& m: m_miners) - if (m.isComplete()) + if (m.isComplete()) + { + cwork << "CHAIN <== postSTATE"; + h256s hs; { - cwork << "CHAIN <== postSTATE"; - h256s hs; - { - WriteGuard l(x_stateDB); - hs = m_bc.attemptImport(m.blockData(), m_stateDB); - } - if (hs.size()) - { - for (auto h: hs) - appendFromNewBlock(h, changeds); - changeds.insert(ChainChangedFilter); - //changeds.insert(PendingChangedFilter); // if we mined the new block, then we've probably reset the pending transactions. - } - for (auto& m: m_miners) - m.noteStateChange(); + WriteGuard l(x_stateDB); + hs = m_bc.attemptImport(m.blockData(), m_stateDB); + } + if (hs.size()) + { + for (auto const& h: hs) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); } + for (auto& m: m_localMiners) + m.noteStateChange(); + } + }; + { + ReadGuard l(x_localMiners); + for (auto& m: m_localMiners) + maintainMiner(m); + } + { + Guard l(x_remoteMiner); + maintainMiner(m_remoteMiner); } // Synchronise state to block chain. @@ -502,7 +534,7 @@ void Client::doWork() // if there are no checkpoints before our fork) reverting to the genesis block and replaying // all blocks. // Resynchronise state with block chain & trans - bool rsm = false; + bool resyncStateNeeded = false; { WriteGuard l(x_stateDB); cwork << "BQ ==> CHAIN ==> STATE"; @@ -525,7 +557,7 @@ void Client::doWork() if (isMining()) cnote << "New block on chain: Restarting mining operation."; m_postMine = m_preMine; - rsm = true; + resyncStateNeeded = true; changeds.insert(PendingChangedFilter); // TODO: Move transactions pending from m_postMine back to transaction queue. } @@ -541,13 +573,13 @@ void Client::doWork() if (isMining()) cnote << "Additional transaction ready: Restarting mining operation."; - rsm = true; + resyncStateNeeded = true; } } - if (rsm) + if (resyncStateNeeded) { - ReadGuard l(x_miners); - for (auto& m: m_miners) + ReadGuard l(x_localMiners); + for (auto& m: m_localMiners) m.noteStateChange(); } @@ -556,6 +588,23 @@ void Client::doWork() cworkout << "WORK"; this_thread::sleep_for(chrono::milliseconds(100)); + if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) + { + // garbage collect on watches + vector toUninstall; + { + Guard l(m_filterLock); + for (auto key: keysOf(m_watches)) + if (chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20)) + { + toUninstall.push_back(key); + cnote << "GC: Uninstall" << key << "(" << chrono::duration_cast(chrono::system_clock::now() - m_watches[key].lastPoll).count() << "s old)"; + } + } + for (auto i: toUninstall) + uninstallWatch(i); + m_lastGarbageCollection = chrono::system_clock::now(); + } } unsigned Client::numberOf(int _n) const diff --git a/libethereum/Client.h b/libethereum/Client.h index 0476db4fd..ce3506bde 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -89,11 +89,12 @@ static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); struct ClientWatch { - ClientWatch() {} - explicit ClientWatch(h256 _id): id(_id) {} + ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} + explicit ClientWatch(h256 _id): id(_id), lastPoll(std::chrono::system_clock::now()) {} h256 id; LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; + mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; @@ -134,6 +135,28 @@ template T abiOut(bytes const& _data) return ABIDeserialiser::deserialise(o); } +class RemoteMiner: public Miner +{ +public: + RemoteMiner() {} + + void update(State const& _provisional, BlockChain const& _bc) { m_state = _provisional; m_state.commitToMine(_bc); } + + h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); } + u256 const& difficulty() const { return m_state.info().difficulty; } + + bool submitWork(h256 const& _nonce) { return (m_isComplete = m_state.completeMine(_nonce)); } + + virtual bool isComplete() const override { return m_isComplete; } + virtual bytes const& blockData() const { return m_state.blockData(); } + + virtual void noteStateChange() override {} + +private: + bool m_isComplete = false; + State m_state; +}; + /** * @brief Main API hub for interfacing with Ethereum. */ @@ -252,20 +275,26 @@ public: /// Stops mining and sets the number of mining threads (0 for automatic). virtual void setMiningThreads(unsigned _threads = 0); /// Get the effective number of mining threads. - virtual unsigned miningThreads() const { ReadGuard l(x_miners); return m_miners.size(); } + virtual unsigned miningThreads() const { ReadGuard l(x_localMiners); return m_localMiners.size(); } /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - virtual void startMining() { startWorking(); ReadGuard l(x_miners); for (auto& m: m_miners) m.start(); } + virtual void startMining() { startWorking(); ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.start(); } /// Stop mining. /// NOT thread-safe - virtual void stopMining() { ReadGuard l(x_miners); for (auto& m: m_miners) m.stop(); } + virtual void stopMining() { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); } /// Are we mining now? - virtual bool isMining() { ReadGuard l(x_miners); return m_miners.size() && m_miners[0].isRunning(); } + virtual bool isMining() { ReadGuard l(x_localMiners); return m_localMiners.size() && m_localMiners[0].isRunning(); } /// Check the progress of the mining. virtual MineProgress miningProgress() const; /// Get and clear the mining history. std::list miningHistory(); + /// Update to the latest transactions and get hash of the current block to be mined minus the + /// nonce (the 'work hash') and the difficulty to be met. + virtual std::pair getWork() override; + /// Submit the nonce for the proof-of-work. + virtual bool submitNonce(h256 const&_nonce) override; + // Debug stuff: DownloadMan const* downloadMan() const; @@ -294,6 +323,7 @@ private: /// Do some work. Handles blockchain maintenance and mining. virtual void doWork(); + /// Called when Worker is exiting. virtual void doneWorking(); /// Overrides for being a mining host. @@ -308,26 +338,31 @@ private: State asOf(unsigned _h) const; VersionChecker m_vc; ///< Dummy object to check & update the protocol version. - CanonBlockChain m_bc; ///< Maintains block database. + CanonBlockChain m_bc; ///< Maintains block database. TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). - mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. + mutable SharedMutex x_stateDB; ///< Lock on the state DB, effectively a lock on m_postMine. OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. - std::vector m_miners; - mutable SharedMutex x_miners; + mutable Mutex x_remoteMiner; ///< The remote miner lock. + RemoteMiner m_remoteMiner; ///< The remote miner. + + std::vector m_localMiners; ///< The in-process miners. + mutable SharedMutex x_localMiners; ///< The in-process miners lock. bool m_paranoia = false; ///< Should we be paranoid about our state? bool m_turboMining = false; ///< Don't squander all of our time mining actually just sleeping. bool m_forceMining = false; ///< Mine even when there are no transactions pending? - mutable std::mutex m_filterLock; + mutable Mutex m_filterLock; std::map m_filters; std::map m_watches; + + mutable std::chrono::system_clock::time_point m_lastGarbageCollection; }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 35cd59663..09a134f1f 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -149,6 +149,11 @@ public: /// Are we mining now? virtual bool isMining() = 0; + /// Get hash of the current block to be mined minus the nonce (the 'work hash'). + virtual std::pair getWork() = 0; + /// Submit the nonce for the proof-of-work. + virtual bool submitNonce(h256 const&) = 0; + /// Check the progress of the mining. virtual MineProgress miningProgress() const = 0; diff --git a/libethereum/LogFilter.cpp b/libethereum/LogFilter.cpp index fa8fbc333..1784094b0 100644 --- a/libethereum/LogFilter.cpp +++ b/libethereum/LogFilter.cpp @@ -27,7 +27,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -std::ostream& dev::operator<<(std::ostream& _out, LogFilter const& _s) +std::ostream& dev::eth::operator<<(std::ostream& _out, LogFilter const& _s) { // TODO _out << "(@" << _s.m_addresses << "#" << _s.m_topics << ">" << _s.m_earliest << "-" << _s.m_latest << "< +" << _s.m_skip << "^" << _s.m_max << ")"; diff --git a/libethereum/LogFilter.h b/libethereum/LogFilter.h index 39e4ac7ee..5f68cd5d0 100644 --- a/libethereum/LogFilter.h +++ b/libethereum/LogFilter.h @@ -34,12 +34,12 @@ namespace eth class LogFilter; } -/// Simple stream output for the StateDiff. -std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); - namespace eth { +/// Simple stream output for the StateDiff. +std::ostream& operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); + class State; class LogFilter @@ -65,7 +65,7 @@ public: LogFilter withEarliest(int _e) { m_earliest = _e; return *this; } LogFilter withLatest(int _e) { m_latest = _e; return *this; } - friend std::ostream& dev::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); + friend std::ostream& dev::eth::operator<<(std::ostream& _out, dev::eth::LogFilter const& _s); private: AddressSet m_addresses; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index 638947a89..a049fca2f 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Miner.cpp - * @author Alex Leverington * @author Gav Wood + * @author Giacomo Tazzari * @date 2014 */ @@ -28,19 +28,21 @@ using namespace std; using namespace dev; using namespace dev::eth; -Miner::Miner(MinerHost* _host, unsigned _id): +Miner::~Miner() {} + +LocalMiner::LocalMiner(MinerHost* _host, unsigned _id): Worker("miner-" + toString(_id)), m_host(_host) { } -void Miner::setup(MinerHost* _host, unsigned _id) +void LocalMiner::setup(MinerHost* _host, unsigned _id) { m_host = _host; setName("miner-" + toString(_id)); } -void Miner::doWork() +void LocalMiner::doWork() { // Do some mining. if (m_miningStatus != Waiting && m_miningStatus != Mined) @@ -63,7 +65,7 @@ void Miner::doWork() if (m_miningStatus == Mining) { - // Mine for a while. + // Mine for a while. MineInfo mineInfo = m_mineState.mine(100, m_host->turbo()); { diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 358a0428b..fd449e995 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -63,6 +63,16 @@ public: virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions. }; +class Miner +{ +public: + virtual ~Miner(); + + virtual void noteStateChange() = 0; + virtual bool isComplete() const = 0; + virtual bytes const& blockData() const = 0; +}; + /** * @brief Implements Miner. * To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the @@ -75,23 +85,23 @@ public: * @threadsafe * @todo Signal Miner to restart once with condition variables. */ -class Miner: Worker +class LocalMiner: public Miner, Worker { public: /// Null constructor. - Miner(): m_host(nullptr) {} + LocalMiner(): m_host(nullptr) {} /// Constructor. - Miner(MinerHost* _host, unsigned _id = 0); + LocalMiner(MinerHost* _host, unsigned _id = 0); /// Move-constructor. - Miner(Miner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); } + LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); } /// Move-assignment. - Miner& operator=(Miner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; } + LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); return *this; } /// Destructor. Stops miner. - ~Miner() { stop(); } + ~LocalMiner() { stop(); } /// Setup its basics. void setup(MinerHost* _host, unsigned _id = 0); @@ -103,16 +113,16 @@ public: void stop() { stopWorking(); } /// Call to notify Miner of a state change. - void noteStateChange() { m_miningStatus = Preparing; } + virtual void noteStateChange() override { m_miningStatus = Preparing; } /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). bool isRunning() { return isWorking(); } /// @returns true if mining is complete. - bool isComplete() const { return m_miningStatus == Mined; } + virtual bool isComplete() const override { return m_miningStatus == Mined; } /// @returns the internal State object. - bytes const& blockData() { return m_mineState.blockData(); } + virtual bytes const& blockData() const override { return m_mineState.blockData(); } /// Check the progress of the mining. MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index fd5f9187f..e44b81c83 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -774,19 +774,31 @@ MineInfo State::mine(unsigned _msTimeout, bool _turbo) // Update difficulty according to timestamp. m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); + MineInfo ret; // TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). - auto ret = m_pow.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo); + tie(ret, m_currentBlock.nonce) = m_pow.mine(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.difficulty, _msTimeout, true, _turbo); if (!ret.completed) m_currentBytes.clear(); else - { cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty); - } return ret; } +bool State::completeMine(h256 const& _nonce) +{ + if (!m_pow.verify(m_currentBlock.headerHash(WithoutNonce), _nonce, m_currentBlock.difficulty)) + return false; + + m_currentBlock.nonce = _nonce; + cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock.headerHash(WithoutNonce), m_currentBlock.nonce, m_currentBlock.difficulty); + + completeMine(); + + return true; +} + void State::completeMine() { cdebug << "Completing mine!"; diff --git a/libethereum/State.h b/libethereum/State.h index 65a333fb4..17e773a95 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -113,6 +113,10 @@ public: /// This may be called multiple times and without issue. void commitToMine(BlockChain const& _bc); + /// Pass in a solution to the proof-of-work. + /// @returns true iff the given nonce is a proof-of-work for this State's block. + bool completeMine(h256 const& _nonce); + /// Attempt to find valid nonce for block that this state represents. /// This function is thread-safe. You can safely have other interactions with this object while it is happening. /// @param _msTimeout Timeout before return in milliseconds. diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 45017db9c..9274628fb 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -354,7 +354,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) auto base = m_stack.back(); auto expon = m_stack[m_stack.size() - 2]; m_stack.pop_back(); - m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); + m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(1) << 256); break; } case Instruction::NOT: diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index f7a71102f..889865d9f 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -291,7 +291,7 @@ Assembly& Assembly::optimise(bool _enable) { Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, { Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, { Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, - { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(2) << 256);} }, + { Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} }, { Instruction::SIGNEXTEND, signextend }, { Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, { Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, diff --git a/libevmcore/Assembly.h b/libevmcore/Assembly.h index 81e4a9ff7..9d9e4093c 100644 --- a/libevmcore/Assembly.h +++ b/libevmcore/Assembly.h @@ -63,8 +63,8 @@ private: u256 m_data; }; -typedef std::vector AssemblyItems; -typedef vector_ref AssemblyItemsConstRef; +using AssemblyItems = std::vector; +using AssemblyItemsConstRef = vector_ref; std::ostream& operator<<(std::ostream& _out, AssemblyItemsConstRef _i); inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _i) { return operator<<(_out, AssemblyItemsConstRef(&_i)); } diff --git a/libjsqrc/ethereumjs/dist/ethereum.js b/libjsqrc/ethereumjs/dist/ethereum.js index fa4bcc591..4c36a7c71 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js +++ b/libjsqrc/ethereumjs/dist/ethereum.js @@ -1043,33 +1043,16 @@ var ProviderManager = function() { var self = this; var poll = function () { - if (self.provider) { - var pollsBatch = self.polls.map(function (data) { - return data.data; - }); - - var payload = jsonrpc.toBatchPayload(pollsBatch); - var results = self.provider.send(payload); - - self.polls.forEach(function (data, index) { - var result = results[index]; - - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - return; - } - - result = result.result; - // dont call the callback if result is not an array, or empty one - if (!(result instanceof Array) || result.length === 0) { - return; - } + self.polls.forEach(function (data) { + var result = self.send(data.data); - data.callback(result); + if (!(result instanceof Array) || result.length === 0) { + return; + } - }); + data.callback(result); + }); - } setTimeout(poll, 1000); }; poll(); diff --git a/libjsqrc/ethereumjs/dist/ethereum.js.map b/libjsqrc/ethereumjs/dist/ethereum.js.map index 118372970..e441da86c 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.js.map +++ b/libjsqrc/ethereumjs/dist/ethereum.js.map @@ -18,7 +18,7 @@ "index.js" ], "names": [], - "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", + "mappings": "AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9IA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "file": "generated.js", "sourceRoot": "", "sourcesContent": [ @@ -31,7 +31,7 @@ "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file formatters.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nif (\"build\" !== 'build') {/*\n var BigNumber = require('bignumber.js'); // jshint ignore:line\n*/}\n\nvar utils = require('./utils');\nvar c = require('./const');\n\n/// @param string string to be padded\n/// @param number of characters that result string should have\n/// @param sign, by default 0\n/// @returns right aligned string\nvar padLeft = function (string, chars, sign) {\n return new Array(chars - string.length + 1).join(sign ? sign : \"0\") + string;\n};\n\n/// Formats input value to byte representation of int\n/// If value is negative, return it's two's complement\n/// If the value is floating point, round it down\n/// @returns right-aligned byte representation of int\nvar formatInputInt = function (value) {\n var padding = c.ETH_PADDING * 2;\n if (value instanceof BigNumber || typeof value === 'number') {\n if (typeof value === 'number')\n value = new BigNumber(value);\n BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE);\n value = value.round();\n\n if (value.lessThan(0)) \n value = new BigNumber(\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\", 16).plus(value).plus(1);\n value = value.toString(16);\n }\n else if (value.indexOf('0x') === 0)\n value = value.substr(2);\n else if (typeof value === 'string')\n value = formatInputInt(new BigNumber(value));\n else\n value = (+value).toString(16);\n return padLeft(value, padding);\n};\n\n/// Formats input value to byte representation of string\n/// @returns left-algined byte representation of string\nvar formatInputString = function (value) {\n return utils.fromAscii(value, c.ETH_PADDING).substr(2);\n};\n\n/// Formats input value to byte representation of bool\n/// @returns right-aligned byte representation bool\nvar formatInputBool = function (value) {\n return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');\n};\n\n/// Formats input value to byte representation of real\n/// Values are multiplied by 2^m and encoded as integers\n/// @returns byte representation of real\nvar formatInputReal = function (value) {\n return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); \n};\n\n\n/// Check if input value is negative\n/// @param value is hex format\n/// @returns true if it is negative, otherwise false\nvar signedIsNegative = function (value) {\n return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';\n};\n\n/// Formats input right-aligned input bytes to int\n/// @returns right-aligned input bytes formatted to int\nvar formatOutputInt = function (value) {\n value = value || \"0\";\n // check if it's negative number\n // it it is, return two's complement\n if (signedIsNegative(value)) {\n return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);\n }\n return new BigNumber(value, 16);\n};\n\n/// Formats big right-aligned input bytes to uint\n/// @returns right-aligned input bytes formatted to uint\nvar formatOutputUInt = function (value) {\n value = value || \"0\";\n return new BigNumber(value, 16);\n};\n\n/// @returns input bytes formatted to real\nvar formatOutputReal = function (value) {\n return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns input bytes formatted to ureal\nvar formatOutputUReal = function (value) {\n return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); \n};\n\n/// @returns right-aligned input bytes formatted to hex\nvar formatOutputHash = function (value) {\n return \"0x\" + value;\n};\n\n/// @returns right-aligned input bytes formatted to bool\nvar formatOutputBool = function (value) {\n return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;\n};\n\n/// @returns left-aligned input bytes formatted to ascii string\nvar formatOutputString = function (value) {\n return utils.toAscii(value);\n};\n\n/// @returns right-aligned input bytes formatted to address\nvar formatOutputAddress = function (value) {\n return \"0x\" + value.slice(value.length - 40, value.length);\n};\n\n\nmodule.exports = {\n formatInputInt: formatInputInt,\n formatInputString: formatInputString,\n formatInputBool: formatInputBool,\n formatInputReal: formatInputReal,\n formatOutputInt: formatOutputInt,\n formatOutputUInt: formatOutputUInt,\n formatOutputReal: formatOutputReal,\n formatOutputUReal: formatOutputUReal,\n formatOutputHash: formatOutputHash,\n formatOutputBool: formatOutputBool,\n formatOutputString: formatOutputString,\n formatOutputAddress: formatOutputAddress\n};\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file httpsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nif (\"build\" !== 'build') {/*\n var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line\n*/}\n\nvar HttpSyncProvider = function (host) {\n this.handlers = [];\n this.host = host || 'http://localhost:8080';\n};\n\nHttpSyncProvider.prototype.send = function (payload) {\n //var data = formatJsonRpcObject(payload);\n \n var request = new XMLHttpRequest();\n request.open('POST', this.host, false);\n request.send(JSON.stringify(payload));\n \n // check request.status\n var result = request.responseText;\n return JSON.parse(result);\n};\n\nmodule.exports = HttpSyncProvider;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file jsonrpc.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar messageId = 1;\n\n/// Should be called to valid json create payload object\n/// @param method of jsonrpc call, required\n/// @param params, an array of method params, optional\n/// @returns valid jsonrpc payload object\nvar toPayload = function (method, params) {\n if (!method)\n console.error('jsonrpc method should be specified!');\n\n return {\n jsonrpc: '2.0',\n method: method,\n params: params || [],\n id: messageId++\n }; \n};\n\n/// Should be called to check if jsonrpc response is valid\n/// @returns true if response is valid, otherwise false \nvar isValidResponse = function (response) {\n return !!response &&\n !response.error &&\n response.jsonrpc === '2.0' &&\n typeof response.id === 'number' &&\n response.result !== undefined; // only undefined is not valid json object\n};\n\n/// Should be called to create batch payload object\n/// @param messages, an array of objects with method (required) and params (optional) fields\nvar toBatchPayload = function (messages) {\n return messages.map(function (message) {\n return toPayload(message.method, message.params);\n }); \n};\n\nmodule.exports = {\n toPayload: toPayload,\n isValidResponse: isValidResponse,\n toBatchPayload: toBatchPayload\n};\n\n\n", - "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar jsonrpc = require('./jsonrpc');\n\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n\n var self = this;\n var poll = function () {\n if (self.provider) {\n var pollsBatch = self.polls.map(function (data) {\n return data.data;\n });\n\n var payload = jsonrpc.toBatchPayload(pollsBatch);\n var results = self.provider.send(payload);\n\n self.polls.forEach(function (data, index) {\n var result = results[index];\n \n if (!jsonrpc.isValidResponse(result)) {\n console.log(result);\n return;\n }\n\n result = result.result;\n // dont call the callback if result is not an array, or empty one\n if (!(result instanceof Array) || result.length === 0) {\n return;\n }\n\n data.callback(result);\n\n });\n\n }\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\n/// @params data - an object with at least 'method' property\nProviderManager.prototype.send = function(data) {\n var payload = jsonrpc.toPayload(data.method, data.params);\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n var result = this.provider.send(payload);\n\n if (!jsonrpc.isValidResponse(result)) {\n console.log(result);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", + "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file providermanager.js\n * @authors:\n * Jeffrey Wilcke \n * Marek Kotewicz \n * Marian Oancea \n * Gav Wood \n * @date 2014\n */\n\nvar web3 = require('./web3'); \nvar jsonrpc = require('./jsonrpc');\n\n\n/**\n * Provider manager object prototype\n * It's responsible for passing messages to providers\n * If no provider is set it's responsible for queuing requests\n * It's also responsible for polling the ethereum node for incoming messages\n * Default poll timeout is 12 seconds\n * If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,\n * and provider manager polling mechanism is not used\n */\nvar ProviderManager = function() {\n this.polls = [];\n this.provider = undefined;\n\n var self = this;\n var poll = function () {\n self.polls.forEach(function (data) {\n var result = self.send(data.data);\n\n if (!(result instanceof Array) || result.length === 0) {\n return;\n }\n\n data.callback(result);\n });\n\n setTimeout(poll, 1000);\n };\n poll();\n};\n\n/// sends outgoing requests\n/// @params data - an object with at least 'method' property\nProviderManager.prototype.send = function(data) {\n var payload = jsonrpc.toPayload(data.method, data.params);\n\n if (this.provider === undefined) {\n console.error('provider is not set');\n return null; \n }\n\n var result = this.provider.send(payload);\n\n if (!jsonrpc.isValidResponse(result)) {\n console.log(result);\n return null;\n }\n\n return result.result;\n};\n\n/// setups provider, which will be used for sending messages\nProviderManager.prototype.set = function(provider) {\n this.provider = provider;\n};\n\n/// this method is only used, when we do not have native qt bindings and have to do polling on our own\n/// should be callled, on start watching for eth/shh changes\nProviderManager.prototype.startPolling = function (data, pollId, callback) {\n this.polls.push({data: data, id: pollId, callback: callback});\n};\n\n/// should be called to stop polling for certain watch changes\nProviderManager.prototype.stopPolling = function (pollId) {\n for (var i = this.polls.length; i--;) {\n var poll = this.polls[i];\n if (poll.id === pollId) {\n this.polls.splice(i, 1);\n }\n }\n};\n\nmodule.exports = ProviderManager;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file qtsync.js\n * @authors:\n * Marek Kotewicz \n * Marian Oancea \n * @date 2014\n */\n\nvar QtSyncProvider = function () {\n};\n\nQtSyncProvider.prototype.send = function (payload) {\n var result = navigator.qt.callMethod(JSON.stringify(payload));\n return JSON.parse(result);\n};\n\nmodule.exports = QtSyncProvider;\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file types.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar f = require('./formatters');\n\n/// @param expected type prefix (string)\n/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false\nvar prefixedType = function (prefix) {\n return function (type) {\n return type.indexOf(prefix) === 0;\n };\n};\n\n/// @param expected type name (string)\n/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false\nvar namedType = function (name) {\n return function (type) {\n return name === type;\n };\n};\n\n/// Setups input formatters for solidity types\n/// @returns an array of input formatters \nvar inputTypes = function () {\n \n return [\n { type: prefixedType('uint'), format: f.formatInputInt },\n { type: prefixedType('int'), format: f.formatInputInt },\n { type: prefixedType('hash'), format: f.formatInputInt },\n { type: prefixedType('string'), format: f.formatInputString }, \n { type: prefixedType('real'), format: f.formatInputReal },\n { type: prefixedType('ureal'), format: f.formatInputReal },\n { type: namedType('address'), format: f.formatInputInt },\n { type: namedType('bool'), format: f.formatInputBool }\n ];\n};\n\n/// Setups output formaters for solidity types\n/// @returns an array of output formatters\nvar outputTypes = function () {\n\n return [\n { type: prefixedType('uint'), format: f.formatOutputUInt },\n { type: prefixedType('int'), format: f.formatOutputInt },\n { type: prefixedType('hash'), format: f.formatOutputHash },\n { type: prefixedType('string'), format: f.formatOutputString },\n { type: prefixedType('real'), format: f.formatOutputReal },\n { type: prefixedType('ureal'), format: f.formatOutputUReal },\n { type: namedType('address'), format: f.formatOutputAddress },\n { type: namedType('bool'), format: f.formatOutputBool }\n ];\n};\n\nmodule.exports = {\n prefixedType: prefixedType,\n namedType: namedType,\n inputTypes: inputTypes,\n outputTypes: outputTypes\n};\n\n", "/*\n This file is part of ethereum.js.\n\n ethereum.js is free software: you can redistribute it and/or modify\n it under the terms of the GNU Lesser General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n ethereum.js is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public License\n along with ethereum.js. If not, see .\n*/\n/** @file utils.js\n * @authors:\n * Marek Kotewicz \n * @date 2015\n */\n\nvar c = require('./const');\n\n/// Finds first index of array element matching pattern\n/// @param array\n/// @param callback pattern\n/// @returns index of element\nvar findIndex = function (array, callback) {\n var end = false;\n var i = 0;\n for (; i < array.length && !end; i++) {\n end = callback(array[i]);\n }\n return end ? i - 1 : -1;\n};\n\n/// @returns ascii string representation of hex value prefixed with 0x\nvar toAscii = function(hex) {\n// Find termination\n var str = \"\";\n var i = 0, l = hex.length;\n if (hex.substring(0, 2) === '0x') {\n i = 2;\n }\n for (; i < l; i+=2) {\n var code = parseInt(hex.substr(i, 2), 16);\n if (code === 0) {\n break;\n }\n\n str += String.fromCharCode(code);\n }\n\n return str;\n};\n \nvar toHex = function(str) {\n var hex = \"\";\n for(var i = 0; i < str.length; i++) {\n var n = str.charCodeAt(i).toString(16);\n hex += n.length < 2 ? '0' + n : n;\n }\n\n return hex;\n};\n\n/// @returns hex representation (prefixed by 0x) of ascii string\nvar fromAscii = function(str, pad) {\n pad = pad === undefined ? 0 : pad;\n var hex = toHex(str);\n while (hex.length < pad*2)\n hex += \"00\";\n return \"0x\" + hex;\n};\n\n/// @returns display name for function/event eg. multiply(uint256) -> multiply\nvar extractDisplayName = function (name) {\n var length = name.indexOf('('); \n return length !== -1 ? name.substr(0, length) : name;\n};\n\n/// @returns overloaded part of function/event name\nvar extractTypeName = function (name) {\n /// TODO: make it invulnerable\n var length = name.indexOf('(');\n return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : \"\";\n};\n\n/// Filters all function from input abi\n/// @returns abi array with filtered objects of type 'function'\nvar filterFunctions = function (json) {\n return json.filter(function (current) {\n return current.type === 'function'; \n }); \n};\n\n/// Filters all events form input abi\n/// @returns abi array with filtered objects of type 'event'\nvar filterEvents = function (json) {\n return json.filter(function (current) {\n return current.type === 'event';\n });\n};\n\n/// used to transform value/string to eth string\n/// TODO: use BigNumber.js to parse int\n/// TODO: add tests for it!\nvar toEth = function (str) {\n var val = typeof str === \"string\" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;\n var unit = 0;\n var units = c.ETH_UNITS;\n while (val > 3000 && unit < units.length - 1)\n {\n val /= 1000;\n unit++;\n }\n var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);\n var replaceFunction = function($0, $1, $2) {\n return $1 + ',' + $2;\n };\n\n while (true) {\n var o = s;\n s = s.replace(/(\\d)(\\d\\d\\d[\\.\\,])/, replaceFunction);\n if (o === s)\n break;\n }\n return s + ' ' + units[unit];\n};\n\nmodule.exports = {\n findIndex: findIndex,\n toAscii: toAscii,\n fromAscii: fromAscii,\n extractDisplayName: extractDisplayName,\n extractTypeName: extractTypeName,\n filterFunctions: filterFunctions,\n filterEvents: filterEvents,\n toEth: toEth\n};\n\n", diff --git a/libjsqrc/ethereumjs/dist/ethereum.min.js b/libjsqrc/ethereumjs/dist/ethereum.min.js index a724e6261..df408d3e4 100644 --- a/libjsqrc/ethereumjs/dist/ethereum.min.js +++ b/libjsqrc/ethereumjs/dist/ethereum.min.js @@ -1 +1 @@ -require=function t(e,n,r){function i(a,u){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(o)return o(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;av;v++)g.push(h(e.slice(0,r))),e=e.slice(r);n.push(g)}else i.prefixedType("string")(t[f].type)?(c=c.slice(r),n.push(h(e.slice(0,r))),e=e.slice(r)):(n.push(h(e.slice(0,r))),e=e.slice(r))}),n},d=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},g=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},v=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*o.ETH_SIGNATURE_LENGTH)},y=function(t){return n.sha3(n.fromAscii(t))};e.exports={inputParser:d,outputParser:g,formatInput:l,formatOutput:h,signatureFromAscii:v,eventSignatureFromAscii:y}},{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(t,e){var n=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:n,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:BigNumber.ROUND_DOWN}}},{}],3:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./utils"),o=t("./event"),a=function(t){n._currentContractAbi=t.abi,n._currentContractAddress=t.address,n._currentContractMethodName=t.method,n._currentContractMethodParams=t.params},u=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},s=function(t,e,o){var u=r.inputParser(e),s=r.outputParser(e);i.filterFunctions(e).forEach(function(f){var c=i.extractDisplayName(f.name),l=i.extractTypeName(f.name),p=function(){var i=Array.prototype.slice.call(arguments),p=r.signatureFromAscii(f.name),m=u[c][l].apply(null,i),h=t._options||{};h.to=o,h.data=p+m;var d=t._isTransact===!0||t._isTransact!==!1&&!f.constant,g=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return a({abi:e,address:o,method:f.name,params:i}),void n.eth.transact(h);var v=n.eth.call(h),y=s[c][l](v);return g&&(1===y.length?y=y[0]:0===y.length&&(y=null)),y};void 0===t[c]&&(t[c]=p),t[c][l]=p})},f=function(t,e,n){t.address=n,t._onWatchEventResult=function(t){var n=event.getMatchingEvent(i.filterEvents(e)),r=o.outputParser(n);return r(t)},Object.defineProperty(t,"topic",{get:function(){return i.filterEvents(e).map(function(t){return r.eventSignatureFromAscii(t.name)})}})},c=function(t,e,a){i.filterEvents(e).forEach(function(e){var u=function(){var t=Array.prototype.slice.call(arguments),i=r.eventSignatureFromAscii(e.name),u=o.inputParser(a,i,e),s=u.apply(null,t);return s._onWatchEventResult=function(t){var n=o.outputParser(e);return n(t)},n.eth.watch(s)};u._isEvent=!0;var s=i.extractDisplayName(e.name),f=i.extractTypeName(e.name);void 0===t[s]&&(t[s]=u),t[s][f]=u})},l=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return u(n),s(n,e,t),f(n,e,t),c(n,e,t),n};e.exports=l},{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(t,e){var n=t("./abi"),r=t("./utils"),i=function(t,e){return t.filter(function(t){return t.indexed===e})},o=function(t,e){var n=r.findIndex(t,function(t){return t.name===e});return-1===n?void console.error("indexed param with name "+e+" not found"):t[n]},a=function(t,e){return Object.keys(e).map(function(r){var a=[o(i(t.inputs,!0),r)],u=e[r];return u instanceof Array?u.map(function(t){return n.formatInput(a,[t])}):n.formatInput(a,[u])})},u=function(t,e,n){return function(r,i){var o=i||{};return o.address=t,o.topic=[],o.topic.push(e),r&&(o.topic=o.topic.concat(a(n,r))),o}},s=function(t,e,n){e.slice(),n.slice();return t.reduce(function(t,r){var i;return i=r.indexed?e.splice(0,1)[0]:n.splice(0,1)[0],t[r.name]=i,t},{})},f=function(t){return function(e){var o={event:r.extractDisplayName(t.name),number:e.number,args:{}};if(!e.topic)return o;var a=i(t.inputs,!0),u="0x"+e.topic.slice(1,e.topic.length).map(function(t){return t.slice(2)}).join(""),f=n.formatOutput(a,u),c=i(t.inputs,!1),l=n.formatOutput(c,e.data);return o.args=s(t.inputs,f,l),o}},c=function(t,e){for(var r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&r2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:n.toEth,eth:{contractFromAbi:function(t){return function(e){e=e||"0xc6d9d2cd449a754c494264e1809c50e34d64562b";var n=p.eth.contract(e,t);return n.address=e,n}},watch:function(t,e,n){return t._isEvent?t(e,n):new p.filter(t,m)}},db:{},shh:{watch:function(t){return new p.filter(t,h)}}};c(p,r()),c(p.eth,i()),l(p.eth,o()),c(p.db,a()),c(p.shh,u());var m={changed:"eth_changed"};c(m,s());var h={changed:"shh_changed"};c(h,f()),p.setProvider=function(t){p.provider.set(t)},e.exports=p},{"./utils":12}],web3:[function(t,e){var n=t("./lib/web3"),r=t("./lib/providermanager");n.provider=new r,n.filter=t("./lib/filter"),n.providers.HttpSyncProvider=t("./lib/httpsync"),n.providers.QtSyncProvider=t("./lib/qtsync"),n.eth.contract=t("./lib/contract"),n.abi=t("./lib/abi"),e.exports=n},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"]); \ No newline at end of file +require=function t(e,n,r){function i(a,u){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(o)return o(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;av;v++)g.push(h(e.slice(0,r))),e=e.slice(r);n.push(g)}else i.prefixedType("string")(t[f].type)?(c=c.slice(r),n.push(h(e.slice(0,r))),e=e.slice(r)):(n.push(h(e.slice(0,r))),e=e.slice(r))}),n},d=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(){var e=Array.prototype.slice.call(arguments);return l(t.inputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},g=function(t){var e={};return t.forEach(function(t){var n=r.extractDisplayName(t.name),i=r.extractTypeName(t.name),o=function(e){return h(t.outputs,e)};void 0===e[n]&&(e[n]=o),e[n][i]=o}),e},v=function(t){return n.sha3(n.fromAscii(t)).slice(0,2+2*o.ETH_SIGNATURE_LENGTH)},y=function(t){return n.sha3(n.fromAscii(t))};e.exports={inputParser:d,outputParser:g,formatInput:l,formatOutput:h,signatureFromAscii:v,eventSignatureFromAscii:y}},{"./const":2,"./formatters":6,"./types":11,"./utils":12,"./web3":13}],2:[function(t,e){var n=["wei","Kwei","Mwei","Gwei","szabo","finney","ether","grand","Mether","Gether","Tether","Pether","Eether","Zether","Yether","Nether","Dether","Vether","Uether"];e.exports={ETH_PADDING:32,ETH_SIGNATURE_LENGTH:4,ETH_UNITS:n,ETH_BIGNUMBER_ROUNDING_MODE:{ROUNDING_MODE:BigNumber.ROUND_DOWN}}},{}],3:[function(t,e){var n=t("./web3"),r=t("./abi"),i=t("./utils"),o=t("./event"),a=function(t){n._currentContractAbi=t.abi,n._currentContractAddress=t.address,n._currentContractMethodName=t.method,n._currentContractMethodParams=t.params},u=function(t){t.call=function(e){return t._isTransact=!1,t._options=e,t},t.transact=function(e){return t._isTransact=!0,t._options=e,t},t._options={},["gas","gasPrice","value","from"].forEach(function(e){t[e]=function(n){return t._options[e]=n,t}})},s=function(t,e,o){var u=r.inputParser(e),s=r.outputParser(e);i.filterFunctions(e).forEach(function(f){var c=i.extractDisplayName(f.name),l=i.extractTypeName(f.name),p=function(){var i=Array.prototype.slice.call(arguments),p=r.signatureFromAscii(f.name),m=u[c][l].apply(null,i),h=t._options||{};h.to=o,h.data=p+m;var d=t._isTransact===!0||t._isTransact!==!1&&!f.constant,g=h.collapse!==!1;if(t._options={},t._isTransact=null,d)return a({abi:e,address:o,method:f.name,params:i}),void n.eth.transact(h);var v=n.eth.call(h),y=s[c][l](v);return g&&(1===y.length?y=y[0]:0===y.length&&(y=null)),y};void 0===t[c]&&(t[c]=p),t[c][l]=p})},f=function(t,e,n){t.address=n,t._onWatchEventResult=function(t){var n=event.getMatchingEvent(i.filterEvents(e)),r=o.outputParser(n);return r(t)},Object.defineProperty(t,"topic",{get:function(){return i.filterEvents(e).map(function(t){return r.eventSignatureFromAscii(t.name)})}})},c=function(t,e,a){i.filterEvents(e).forEach(function(e){var u=function(){var t=Array.prototype.slice.call(arguments),i=r.eventSignatureFromAscii(e.name),u=o.inputParser(a,i,e),s=u.apply(null,t);return s._onWatchEventResult=function(t){var n=o.outputParser(e);return n(t)},n.eth.watch(s)};u._isEvent=!0;var s=i.extractDisplayName(e.name),f=i.extractTypeName(e.name);void 0===t[s]&&(t[s]=u),t[s][f]=u})},l=function(t,e){e.forEach(function(t){if(-1===t.name.indexOf("(")){var e=t.name,n=t.inputs.map(function(t){return t.type}).join();t.name=e+"("+n+")"}});var n={};return u(n),s(n,e,t),f(n,e,t),c(n,e,t),n};e.exports=l},{"./abi":1,"./event":4,"./utils":12,"./web3":13}],4:[function(t,e){var n=t("./abi"),r=t("./utils"),i=function(t,e){return t.filter(function(t){return t.indexed===e})},o=function(t,e){var n=r.findIndex(t,function(t){return t.name===e});return-1===n?void console.error("indexed param with name "+e+" not found"):t[n]},a=function(t,e){return Object.keys(e).map(function(r){var a=[o(i(t.inputs,!0),r)],u=e[r];return u instanceof Array?u.map(function(t){return n.formatInput(a,[t])}):n.formatInput(a,[u])})},u=function(t,e,n){return function(r,i){var o=i||{};return o.address=t,o.topic=[],o.topic.push(e),r&&(o.topic=o.topic.concat(a(n,r))),o}},s=function(t,e,n){e.slice(),n.slice();return t.reduce(function(t,r){var i;return i=r.indexed?e.splice(0,1)[0]:n.splice(0,1)[0],t[r.name]=i,t},{})},f=function(t){return function(e){var o={event:r.extractDisplayName(t.name),number:e.number,args:{}};if(!e.topic)return o;var a=i(t.inputs,!0),u="0x"+e.topic.slice(1,e.topic.length).map(function(t){return t.slice(2)}).join(""),f=n.formatOutput(a,u),c=i(t.inputs,!1),l=n.formatOutput(c,e.data);return o.args=s(t.inputs,f,l),o}},c=function(t,e){for(var r=0;rn;n+=2){var i=parseInt(t.substr(n,2),16);if(0===i)break;e+=String.fromCharCode(i)}return e},o=function(t){for(var e="",n=0;n3e3&&r2?t.substring(2):"0",new BigNumber(t,16).toString(10)},fromDecimal:function(t){return"0x"+new BigNumber(t).toString(16)},toEth:n.toEth,eth:{contractFromAbi:function(t){return function(e){e=e||"0xc6d9d2cd449a754c494264e1809c50e34d64562b";var n=p.eth.contract(e,t);return n.address=e,n}},watch:function(t,e,n){return t._isEvent?t(e,n):new p.filter(t,m)}},db:{},shh:{watch:function(t){return new p.filter(t,h)}}};c(p,r()),c(p.eth,i()),l(p.eth,o()),c(p.db,a()),c(p.shh,u());var m={changed:"eth_changed"};c(m,s());var h={changed:"shh_changed"};c(h,f()),p.setProvider=function(t){p.provider.set(t)},e.exports=p},{"./utils":12}],web3:[function(t,e){var n=t("./lib/web3"),r=t("./lib/providermanager");n.provider=new r,n.filter=t("./lib/filter"),n.providers.HttpSyncProvider=t("./lib/httpsync"),n.providers.QtSyncProvider=t("./lib/qtsync"),n.eth.contract=t("./lib/contract"),n.abi=t("./lib/abi"),e.exports=n},{"./lib/abi":1,"./lib/contract":3,"./lib/filter":5,"./lib/httpsync":7,"./lib/providermanager":9,"./lib/qtsync":10,"./lib/web3":13}]},{},["web3"]); \ No newline at end of file diff --git a/libjsqrc/ethereumjs/lib/providermanager.js b/libjsqrc/ethereumjs/lib/providermanager.js index 55f166bcd..55b072634 100644 --- a/libjsqrc/ethereumjs/lib/providermanager.js +++ b/libjsqrc/ethereumjs/lib/providermanager.js @@ -42,33 +42,16 @@ var ProviderManager = function() { var self = this; var poll = function () { - if (self.provider) { - var pollsBatch = self.polls.map(function (data) { - return data.data; - }); + self.polls.forEach(function (data) { + var result = self.send(data.data); - var payload = jsonrpc.toBatchPayload(pollsBatch); - var results = self.provider.send(payload); + if (!(result instanceof Array) || result.length === 0) { + return; + } - self.polls.forEach(function (data, index) { - var result = results[index]; - - if (!jsonrpc.isValidResponse(result)) { - console.log(result); - return; - } + data.callback(result); + }); - result = result.result; - // dont call the callback if result is not an array, or empty one - if (!(result instanceof Array) || result.length === 0) { - return; - } - - data.callback(result); - - }); - - } setTimeout(poll, 1000); }; poll(); diff --git a/libjsqrc/js.qrc b/libjsqrc/js.qrc index 50b5e098c..b23aa144d 100644 --- a/libjsqrc/js.qrc +++ b/libjsqrc/js.qrc @@ -3,6 +3,5 @@ bignumber.min.js setup.js ethereumjs/dist/ethereum.js - natspec.js diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp index e94e88e19..df30f3897 100644 --- a/liblll/Parser.cpp +++ b/liblll/Parser.cpp @@ -89,8 +89,8 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) using qi::standard::space; using qi::standard::space_type; using dev::eth::parseTreeLLL_::tagNode; - typedef sp::basic_string symbol_type; - typedef string::const_iterator it; + using symbol_type = sp::basic_string; + using it = string::const_iterator; static const u256 ether = u256(1000000000) * 1000000000; static const u256 finney = u256(1000000000) * 1000000; diff --git a/libnatspec/CMakeLists.txt b/libnatspec/CMakeLists.txt new file mode 100644 index 000000000..b5bd7903e --- /dev/null +++ b/libnatspec/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_policy(SET CMP0015 NEW) +# let cmake autolink dependencies on windows +cmake_policy(SET CMP0020 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) + cmake_policy(SET CMP0043 OLD) +endif() +set(CMAKE_AUTOMOC OFF) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +aux_source_directory(. SRC_LIST) + +include_directories(..) + +set(EXECUTABLE natspec) + +file(GLOB HEADERS "*.h") + +qt5_add_resources(NATSPECQRC natspec.qrc) + +if (ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS} ${NATSPECQRC}) +else() + add_library(${EXECUTABLE} SHARED ${RESOURCE_ADDED} ${SRC_LIST} ${HEADERS} ${NATSPECQRC}) +endif() + +target_link_libraries(${EXECUTABLE} Qt5::Core) +target_link_libraries(${EXECUTABLE} Qt5::Qml) +target_link_libraries(${EXECUTABLE} devcore) + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libnatspec/NatspecExpressionEvaluator.cpp b/libnatspec/NatspecExpressionEvaluator.cpp new file mode 100644 index 000000000..fd8369145 --- /dev/null +++ b/libnatspec/NatspecExpressionEvaluator.cpp @@ -0,0 +1,59 @@ +/* + 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 . + */ +/** @file NatspecExpressionEvaluator.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include "NatspecExpressionEvaluator.h" + +using namespace std; +using namespace dev; + +static QString contentsOfQResource(string const& _res) +{ + QFile file(QString::fromStdString(_res)); + if (!file.open(QFile::ReadOnly)) + BOOST_THROW_EXCEPTION(FileError()); + QTextStream in(&file); + return in.readAll(); +} + +NatspecExpressionEvaluator::NatspecExpressionEvaluator(QString const& _abi, QString const& _method, QString const& _params) +{ + Q_INIT_RESOURCE(natspec); + QJSValue result = m_engine.evaluate(contentsOfQResource(":/natspec/natspec.js")); + if (result.isError()) + BOOST_THROW_EXCEPTION(FileError()); + + m_engine.evaluate("globals.abi = " + _abi); + m_engine.evaluate("globals.method = " + _method); + m_engine.evaluate("globals.params = " + _params); +} + +QString NatspecExpressionEvaluator::evalExpression(QString const& _expression) +{ + QJSValue result = m_engine.evaluate("evaluateExpression(\"" + _expression + "\")"); + if (result.isError()) + { + cerr << "Could not evaluate expression: \"" << _expression.toStdString() << "\"" << endl; + return _expression; + } + return result.toString(); +} diff --git a/libnatspec/NatspecExpressionEvaluator.h b/libnatspec/NatspecExpressionEvaluator.h new file mode 100644 index 000000000..fc122084e --- /dev/null +++ b/libnatspec/NatspecExpressionEvaluator.h @@ -0,0 +1,49 @@ +/* + 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 . + */ +/** @file NatspecExpressionEvaluator.h + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include +#include + +/** + * Should be used to evaluate natspec expression. + * @see test/natspec.cpp for natspec expression examples + */ +class NatspecExpressionEvaluator +{ +public: + /// Construct natspec expression evaluator + /// @params abi - contract's abi in json format, passed as string + /// @params method - name of the contract's method for which we evaluate the natspec. + /// If we want to use raw string, it should be passed with quotation marks eg. "\"helloWorld\"" + /// If we pass string "helloWorld", the value of the object with name "helloWorld" will be used + /// @params params - array of method input params, passed as string, objects in array should be + /// javascript valid objects + NatspecExpressionEvaluator(QString const& _abi = "[]", QString const& _method = "", QString const& _params = "[]"); + + /// Should be called to evaluate natspec expression + /// @params expression - natspec expression + /// @returns evaluated natspec expression if it was valid, otherwise original expression + QString evalExpression(QString const& _expression); + +private: + QJSEngine m_engine; +}; diff --git a/libjsqrc/natspec.js b/libnatspec/natspec.js similarity index 65% rename from libjsqrc/natspec.js rename to libnatspec/natspec.js index c8cf07496..ed30b5378 100644 --- a/libjsqrc/natspec.js +++ b/libnatspec/natspec.js @@ -2,13 +2,19 @@ /** * This plugin exposes 'evaluateExpression' method which should be used * to evaluate natspec description - * It should be reloaded each time we want to evaluate set of expressions - * Just because of security reasons - * TODO: make use of sync api (once it's finished) and remove unnecessary - * code from 'getContractMethods' - * TODO: unify method signature creation with abi.js (and make a separate method from it) */ +/// Object which should be used by NatspecExpressionEvaluator +/// abi - abi of the contract that will be used +/// method - name of the method that is called +/// params - input params of the method that will be called +var globals = { + abi: [], + method: "", + params: [] +}; + +/// Helper method /// Should be called to copy values from object to global context var copyToContext = function (obj, context) { var keys = Object.keys(obj); @@ -17,32 +23,38 @@ var copyToContext = function (obj, context) { }); } +/// Helper method +/// Should be called to get method with given name from the abi +/// @param contract's abi +/// @param name of the method that we are looking for +var getMethodWithName = function(abi, name) { + for (var i = 0; i < abi.length; i++) { + if (abi[i].name === name) { + return abi[i]; + } + } + //console.warn('could not find method with name: ' + name); + return undefined; +}; + /// Function called to get all contract's storage values /// @returns hashmap with contract properties which are used +/// TODO: check if this function will be used var getContractProperties = function (address, abi) { return {}; }; /// Function called to get all contract's methods /// @returns hashmap with used contract's methods +/// TODO: check if this function will be used var getContractMethods = function (address, abi) { - return web3.eth.contract(address, abi); -}; - -var getMethodWithName = function(abi, name) { - for (var i = 0; i < abi.length; i++) { - if (abi[i].name === name) { - return abi[i]; - } - } - console.warn('could not find method with name: ' + name); - return undefined; + //return web3.eth.contract(address, abi); // commented out web3 usage + return {}; }; /// Function called to get all contract method input variables /// @returns hashmap with all contract's method input variables -var getContractInputParams = function (abi, methodName, params) { - var method = getMethodWithName(abi, methodName); +var getMethodInputParams = function (method, params) { return method.inputs.reduce(function (acc, current, index) { acc[current.name] = params[index]; return acc; @@ -55,18 +67,15 @@ var getContractInputParams = function (abi, methodName, params) { var evaluateExpression = function (expression) { var self = this; - var abi = web3._currentContractAbi; - var address = web3._currentContractAddress; - var methodName = web3._currentContractMethodName; - var params = web3._currentContractMethodParams; - - var storage = getContractProperties(address, abi); - var methods = getContractMethods(address, abi); - var inputParams = getContractInputParams(abi, methodName, params); - - copyToContext(storage, self); - copyToContext(methods, self); - copyToContext(inputParams, self); + + //var storage = getContractProperties(address, abi); + //var methods = getContractMethods(address, abi); + + var method = getMethodWithName(globals.abi, globals.method); + if (method) { + var input = getMethodInputParams(method, globals.params); + copyToContext(input, self); + } // TODO: test if it is safe var evaluatedExpression = ""; diff --git a/libnatspec/natspec.qrc b/libnatspec/natspec.qrc new file mode 100644 index 000000000..db125974c --- /dev/null +++ b/libnatspec/natspec.qrc @@ -0,0 +1,5 @@ + + + natspec.js + + diff --git a/libp2p/Common.h b/libp2p/Common.h index d46c5eed1..61df1350f 100644 --- a/libp2p/Common.h +++ b/libp2p/Common.h @@ -112,9 +112,9 @@ inline bool isPermanentProblem(DisconnectReason _r) /// @returns the string form of the given disconnection reason. std::string reasonOf(DisconnectReason _r); -typedef std::pair CapDesc; -typedef std::set CapDescSet; -typedef std::vector CapDescs; +using CapDesc = std::pair; +using CapDescSet = std::set; +using CapDescs = std::vector; struct PeerInfo { diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index dfb677f7e..041b6277d 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -58,11 +58,19 @@ void ContractDefinition::checkTypeRequirements() BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( "Non-empty \"returns\" directive for constructor.")); - FunctionDefinition const* fallbackFunction = getFallbackFunction(); - if (fallbackFunction && fallbackFunction->getScope() == this && !fallbackFunction->getParameters().empty()) - BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError( - "Fallback function cannot take parameters.")); - + FunctionDefinition const* fallbackFunction = nullptr; + for (ASTPointer const& function: getDefinedFunctions()) + if (function->getName().empty()) + { + if (fallbackFunction) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed.")); + else + { + fallbackFunction = function.get(); + if (!fallbackFunction->getParameters().empty()) + BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters.")); + } + } for (ASTPointer const& modifier: getFunctionModifiers()) modifier->checkTypeRequirements(); @@ -239,7 +247,7 @@ void StructDefinition::checkRecursion() const << errinfo_comment("Recursive struct definition.")); definitionsSeen.insert(def); for (ASTPointer const& member: def->getMembers()) - if (member->getType()->getCategory() == Type::Category::STRUCT) + if (member->getType()->getCategory() == Type::Category::Struct) { UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); @@ -271,9 +279,9 @@ string FunctionDefinition::getCanonicalSignature() const Declaration::LValueType VariableDeclaration::getLValueType() const { if (dynamic_cast(getScope()) || dynamic_cast(getScope())) - return Declaration::LValueType::LOCAL; + return Declaration::LValueType::Local; else - return Declaration::LValueType::STORAGE; + return Declaration::LValueType::Storage; } TypePointer ModifierDefinition::getType(ContractDefinition const*) const @@ -376,13 +384,15 @@ void VariableDefinition::checkTypeRequirements() // no type declared and no previous assignment, infer the type m_value->checkTypeRequirements(); TypePointer type = m_value->getType(); - if (type->getCategory() == Type::Category::INTEGER_CONSTANT) + if (type->getCategory() == Type::Category::IntegerConstant) { auto intType = dynamic_pointer_cast(type)->getIntegerType(); if (!intType) BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString())); type = intType; } + else if (type->getCategory() == Type::Category::Void) + BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type")); m_variable->setType(type); } } @@ -396,7 +406,7 @@ void Assignment::checkTypeRequirements() if (!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue()) BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue.")); m_type = m_leftHandSide->getType(); - if (m_assigmentOperator == Token::ASSIGN) + if (m_assigmentOperator == Token::Assign) m_rightHandSide->expectType(*m_type); else { @@ -415,7 +425,7 @@ void Assignment::checkTypeRequirements() void ExpressionStatement::checkTypeRequirements() { m_expression->checkTypeRequirements(); - if (m_expression->getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant) if (!dynamic_pointer_cast(m_expression->getType())->getIntegerType()) BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); } @@ -439,9 +449,9 @@ void Expression::requireLValue() void UnaryOperation::checkTypeRequirements() { - // INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE + // Inc, Dec, Add, Sub, Not, BitNot, Delete m_subExpression->checkTypeRequirements(); - if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE) + if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete) m_subExpression->requireLValue(); m_type = m_subExpression->getType()->unaryOperatorResult(m_operator); if (!m_type) @@ -476,7 +486,7 @@ void FunctionCall::checkTypeRequirements() if (m_arguments.size() != 1) BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); if (!m_names.empty()) - BOOST_THROW_EXCEPTION(createTypeError("Type conversion can't allow named arguments.")); + BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); m_type = type.getActualType(); @@ -487,28 +497,30 @@ void FunctionCall::checkTypeRequirements() // and then ask if that is implicitly convertible to the struct represented by the // function parameters TypePointers const& parameterTypes = functionType->getParameterTypes(); - if (parameterTypes.size() != m_arguments.size()) + if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); if (m_names.empty()) { for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) - BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); + if (!functionType->takesArbitraryParameters() && + !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) + BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Invalid type for argument in function call.")); } else { + if (functionType->takesArbitraryParameters()) + BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " + "that take arbitrary parameters.")); auto const& parameterNames = functionType->getParameterNames(); if (parameterNames.size() != m_names.size()) BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing.")); // check duplicate names - for (size_t i = 0; i < m_names.size(); i++) { - for (size_t j = i + 1; j < m_names.size(); j++) { + for (size_t i = 0; i < m_names.size(); i++) + for (size_t j = i + 1; j < m_names.size(); j++) if (*m_names[i] == *m_names[j]) - BOOST_THROW_EXCEPTION(createTypeError("Duplicate named argument.")); - } - } + BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument.")); for (size_t i = 0; i < m_names.size(); i++) { bool found = false; @@ -523,7 +535,7 @@ void FunctionCall::checkTypeRequirements() } } if (!found) - BOOST_THROW_EXCEPTION(createTypeError("Named argument doesn't match function declaration.")); + BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration.")); } } @@ -540,7 +552,7 @@ void FunctionCall::checkTypeRequirements() bool FunctionCall::isTypeConversion() const { - return m_expression->getType()->getCategory() == Type::Category::TYPE; + return m_expression->getType()->getCategory() == Type::Category::TypeType; } void NewExpression::checkTypeRequirements() @@ -552,7 +564,7 @@ void NewExpression::checkTypeRequirements() shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, - FunctionType::Location::CREATION); + FunctionType::Location::Creation); } void MemberAccess::checkTypeRequirements() @@ -564,19 +576,19 @@ void MemberAccess::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); //@todo later, this will not always be STORAGE - m_lvalue = type.getCategory() == Type::Category::STRUCT ? Declaration::LValueType::STORAGE : Declaration::LValueType::NONE; + m_lvalue = type.getCategory() == Type::Category::Struct ? Declaration::LValueType::Storage : Declaration::LValueType::None; } void IndexAccess::checkTypeRequirements() { m_base->checkTypeRequirements(); - if (m_base->getType()->getCategory() != Type::Category::MAPPING) + if (m_base->getType()->getCategory() != Type::Category::Mapping) BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " + m_base->getType()->toString() + ")")); MappingType const& type = dynamic_cast(*m_base->getType()); m_index->expectType(*type.getKeyType()); m_type = type.getValueType(); - m_lvalue = Declaration::LValueType::STORAGE; + m_lvalue = Declaration::LValueType::Storage; } void Identifier::checkTypeRequirements() diff --git a/libsolidity/AST.h b/libsolidity/AST.h old mode 100755 new mode 100644 index 525907bf4..4e79026e8 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -132,17 +132,17 @@ private: class Declaration: public ASTNode { public: - enum class LValueType { NONE, LOCAL, STORAGE }; - enum class Visibility { DEFAULT, PUBLIC, PROTECTED, PRIVATE }; + enum class LValueType { None, Local, Storage }; + enum class Visibility { Default, Public, Protected, Private }; Declaration(Location const& _location, ASTPointer const& _name, - Visibility _visibility = Visibility::DEFAULT): + Visibility _visibility = Visibility::Default): ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} /// @returns the declared name. ASTString const& getName() const { return *m_name; } - Visibility getVisibility() const { return m_visibility == Visibility::DEFAULT ? getDefaultVisibility() : m_visibility; } - bool isPublic() const { return getVisibility() == Visibility::PUBLIC; } + Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; } + bool isPublic() const { return getVisibility() == Visibility::Public; } /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. @@ -154,10 +154,10 @@ public: /// contract types. virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; /// @returns the lvalue type of expressions referencing this declaration - virtual LValueType getLValueType() const { return LValueType::NONE; } + virtual LValueType getLValueType() const { return LValueType::None; } protected: - virtual Visibility getDefaultVisibility() const { return Visibility::PUBLIC; } + virtual Visibility getDefaultVisibility() const { return Visibility::Public; } private: ASTPointer m_name; @@ -250,7 +250,7 @@ public: /// Returns the constructor or nullptr if no constructor was specified. FunctionDefinition const* getConstructor() const; - /// Returns the fallback function or nullptr if no constructor was specified. + /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* getFallbackFunction() const; private: @@ -414,7 +414,7 @@ public: bool isIndexed() const { return m_isIndexed; } protected: - Visibility getDefaultVisibility() const override { return Visibility::PROTECTED; } + Visibility getDefaultVisibility() const override { return Visibility::Protected; } private: ASTPointer m_typeName; ///< can be empty ("var") @@ -847,8 +847,8 @@ public: virtual void checkTypeRequirements() = 0; std::shared_ptr const& getType() const { return m_type; } - bool isLValue() const { return m_lvalue != Declaration::LValueType::NONE; } - bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::LOCAL; } + bool isLValue() const { return m_lvalue != Declaration::LValueType::None; } + bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::Local; } /// Helper function, infer the type via @ref checkTypeRequirements and then check that it /// is implicitly convertible to @a _expectedType. If not, throw exception. @@ -865,7 +865,7 @@ protected: std::shared_ptr m_type; //! If this expression is an lvalue (i.e. something that can be assigned to) and is stored //! locally or in storage. This is set during calls to @a checkTypeRequirements() - Declaration::LValueType m_lvalue = Declaration::LValueType::NONE; + Declaration::LValueType m_lvalue = Declaration::LValueType::None; //! Whether the outer expression requested the address (true) or the value (false) of this expression. bool m_lvalueRequested = false; }; @@ -1112,13 +1112,23 @@ private: }; /** - * A literal string or number. @see Type::literalToBigEndian is used to actually parse its value. + * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. */ class Literal: public PrimaryExpression { public: - Literal(Location const& _location, Token::Value _token, ASTPointer const& _value): - PrimaryExpression(_location), m_token(_token), m_value(_value) {} + enum class SubDenomination + { + None = Token::Illegal, + Wei = Token::SubWei, + Szabo = Token::SubSzabo, + Finney = Token::SubFinney, + Ether = Token::SubEther + }; + Literal(Location const& _location, Token::Value _token, + ASTPointer const& _value, + SubDenomination _sub = SubDenomination::None): + PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; @@ -1127,9 +1137,12 @@ public: /// @returns the non-parsed value of the literal ASTString const& getValue() const { return *m_value; } + SubDenomination getSubDenomination() const { return m_subDenomination; } + private: Token::Value m_token; ASTPointer m_value; + SubDenomination m_subDenomination; }; /// @} diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp index d9332990d..a216a59ac 100644 --- a/libsolidity/ASTJsonConverter.cpp +++ b/libsolidity/ASTJsonConverter.cpp @@ -118,7 +118,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) bool ASTJsonConverter::visit(VariableDeclaration const& _node) { - bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::LOCAL); + bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::Local); addJsonNode("VariableDeclaration", { make_pair("name", _node.getName()), make_pair("local", boost::lexical_cast(isLocalVariable))}, diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 389f826b7..b36153677 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -189,7 +189,7 @@ unsigned Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, b if (c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Type " + type->toString() + " not yet supported.")); - bool const c_leftAligned = type->getCategory() == Type::Category::STRING; + bool const c_leftAligned = type->getCategory() == Type::Category::String; bool const c_padToWords = true; dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned, !_fromMemory, c_padToWords); @@ -213,7 +213,7 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) << errinfo_comment("Type " + type->toString() + " not yet supported.")); CompilerUtils(m_context).copyToStackTop(stackDepth, *type); ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true); - bool const c_leftAligned = type->getCategory() == Type::Category::STRING; + bool const c_leftAligned = type->getCategory() == Type::Category::String; bool const c_padToWords = true; dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords); stackDepth -= type->getSizeOnStack(); diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 2d77bc0a4..762edb521 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -227,12 +227,12 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN string const& CompilerStack::getInterface(string const& _contractName) const { - return getMetadata(_contractName, DocumentationType::ABI_INTERFACE); + return getMetadata(_contractName, DocumentationType::ABIInterface); } string const& CompilerStack::getSolidityInterface(string const& _contractName) const { - return getMetadata(_contractName, DocumentationType::ABI_SOLIDITY_INTERFACE); + return getMetadata(_contractName, DocumentationType::ABISolidityInterface); } string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const @@ -245,16 +245,16 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat std::unique_ptr* doc; switch (_type) { - case DocumentationType::NATSPEC_USER: + case DocumentationType::NatspecUser: doc = &contract.userDocumentation; break; - case DocumentationType::NATSPEC_DEV: + case DocumentationType::NatspecDev: doc = &contract.devDocumentation; break; - case DocumentationType::ABI_INTERFACE: + case DocumentationType::ABIInterface: doc = &contract.interface; break; - case DocumentationType::ABI_SOLIDITY_INTERFACE: + case DocumentationType::ABISolidityInterface: doc = &contract.solidityInterface; break; default: diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index a6c3df8ee..439077f36 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -43,10 +43,10 @@ class InterfaceHandler; enum class DocumentationType: uint8_t { - NATSPEC_USER = 1, - NATSPEC_DEV, - ABI_INTERFACE, - ABI_SOLIDITY_INTERFACE + NatspecUser = 1, + NatspecDev, + ABIInterface, + ABISolidityInterface }; extern const std::map StandardSources; diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index c7081bc78..2e810a4cf 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -30,6 +30,9 @@ namespace solidity bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update) { + if (_declaration.getName().empty()) + return true; + if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end()) return false; m_declarations[_declaration.getName()] = &_declaration; @@ -38,6 +41,7 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { + solAssert(!_name.empty(), "Attempt to resolve empty name."); auto result = m_declarations.find(_name); if (result != m_declarations.end()) return result->second; diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index e4b793259..1216fcef2 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -42,8 +42,8 @@ public: explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, DeclarationContainer const* _enclosingContainer = nullptr): m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} - /// Registers the declaration in the scope unless its name is already declared. - /// @returns true iff it was not yet declared. + /// Registers the declaration in the scope unless its name is already declared or the name is empty. + /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, bool _update = false); Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 14f919772..0b25abee1 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -38,7 +38,7 @@ struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; struct DocstringParsingError: virtual Exception {}; -typedef boost::error_info errinfo_sourceLocation; +using errinfo_sourceLocation = boost::error_info; } } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 875e00bc2..1d3f23e69 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -64,7 +64,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) solAssert(m_currentLValue.isValid(), "LValue not retrieved."); Token::Value op = _assignment.getAssignmentOperator(); - if (op != Token::ASSIGN) // compound assignment + if (op != Token::Assign) // compound assignment { if (m_currentLValue.storesReferenceOnStack()) m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; @@ -85,7 +85,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) // the operator should know how to convert itself and to which types it applies, so // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // represents the operator - if (_unaryOperation.getType()->getCategory() == Type::Category::INTEGER_CONSTANT) + if (_unaryOperation.getType()->getCategory() == Type::Category::IntegerConstant) { m_context << _unaryOperation.getType()->literalValue(nullptr); return false; @@ -95,19 +95,19 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) switch (_unaryOperation.getOperator()) { - case Token::NOT: // ! + case Token::Not: // ! m_context << eth::Instruction::ISZERO; break; - case Token::BIT_NOT: // ~ + case Token::BitNot: // ~ m_context << eth::Instruction::NOT; break; - case Token::DELETE: // delete + case Token::Delete: // delete solAssert(m_currentLValue.isValid(), "LValue not retrieved."); m_currentLValue.setToZero(_unaryOperation); m_currentLValue.reset(); break; - case Token::INC: // ++ (pre- or postfix) - case Token::DEC: // -- (pre- or postfix) + case Token::Inc: // ++ (pre- or postfix) + case Token::Dec: // -- (pre- or postfix) solAssert(m_currentLValue.isValid(), "LValue not retrieved."); m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation()); if (!_unaryOperation.isPrefixOperation()) @@ -118,7 +118,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) m_context << eth::Instruction::DUP1; } m_context << u256(1); - if (_unaryOperation.getOperator() == Token::INC) + if (_unaryOperation.getOperator() == Token::Inc) m_context << eth::Instruction::ADD; else m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap @@ -129,10 +129,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation()); m_currentLValue.reset(); break; - case Token::ADD: // + + case Token::Add: // + // unary add, so basically no-op break; - case Token::SUB: // - + case Token::Sub: // - m_context << u256(0) << eth::Instruction::SUB; break; default: @@ -149,19 +149,19 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Type const& commonType = _binaryOperation.getCommonType(); Token::Value const c_op = _binaryOperation.getOperator(); - if (c_op == Token::AND || c_op == Token::OR) // special case: short-circuiting + if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - else if (commonType.getCategory() == Type::Category::INTEGER_CONSTANT) + else if (commonType.getCategory() == Type::Category::IntegerConstant) m_context << commonType.literalValue(nullptr); else { - bool cleanupNeeded = commonType.getCategory() == Type::Category::INTEGER && - (Token::isCompareOp(c_op) || c_op == Token::DIV || c_op == Token::MOD); + bool cleanupNeeded = commonType.getCategory() == Type::Category::Integer && + (Token::isCompareOp(c_op) || c_op == Token::Div || c_op == Token::Mod); // for commutative operators, push the literal as late as possible to allow improved optimization auto isLiteral = [](Expression const& _e) { - return dynamic_cast(&_e) || _e.getType()->getCategory() == Type::Category::INTEGER_CONSTANT; + return dynamic_cast(&_e) || _e.getType()->getCategory() == Type::Category::IntegerConstant; }; bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) @@ -206,7 +206,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) TypePointers const& parameterTypes = function.getParameterTypes(); vector> const& callArguments = _functionCall.getArguments(); vector> const& callArgumentNames = _functionCall.getNames(); - solAssert(callArguments.size() == parameterTypes.size(), ""); + if (!function.takesArbitraryParameters()) + solAssert(callArguments.size() == parameterTypes.size(), ""); vector> arguments; if (callArgumentNames.empty()) @@ -226,7 +227,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) switch (function.getLocation()) { - case Location::INTERNAL: + case Location::Internal: { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values @@ -252,12 +253,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]); break; } - case Location::EXTERNAL: - case Location::BARE: + case Location::External: + case Location::Bare: _functionCall.getExpression().accept(*this); - appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE); + appendExternalFunctionCall(function, arguments, function.getLocation() == Location::Bare); break; - case Location::CREATION: + case Location::Creation: { _functionCall.getExpression().accept(*this); solAssert(!function.gasSet(), "Gas limit set for contract creation."); @@ -274,7 +275,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(0) << eth::Instruction::CODECOPY; unsigned length = bytecode.size(); - length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length); + length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length); // size, offset, endowment m_context << u256(length) << u256(0); if (function.valueSet()) @@ -286,7 +287,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::swapInstruction(1) << eth::Instruction::POP; break; } - case Location::SET_GAS: + case Location::SetGas: { // stack layout: contract_address function_id [gas] [value] _functionCall.getExpression().accept(*this); @@ -301,7 +302,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::Instruction::POP; break; } - case Location::SET_VALUE: + case Location::SetValue: // stack layout: contract_address function_id [gas] [value] _functionCall.getExpression().accept(*this); // Note that function is not the original function, but the ".value" function. @@ -310,31 +311,33 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << eth::Instruction::POP; arguments.front()->accept(*this); break; - case Location::SEND: + case Location::Send: _functionCall.getExpression().accept(*this); m_context << u256(0); // 0 gas, we do not want to execute code arguments.front()->accept(*this); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, - Location::EXTERNAL, true, true), {}, true); + Location::External, false, true, true), {}, true); break; - case Location::SUICIDE: + case Location::Suicide: arguments.front()->accept(*this); appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; break; case Location::SHA3: - appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front()); - m_context << u256(32) << u256(0) << eth::Instruction::SHA3; + { + unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments()); + m_context << u256(length) << u256(0) << eth::Instruction::SHA3; break; - case Location::LOG0: - case Location::LOG1: - case Location::LOG2: - case Location::LOG3: - case Location::LOG4: + } + case Location::Log0: + case Location::Log1: + case Location::Log2: + case Location::Log3: + case Location::Log4: { - unsigned logNumber = int(function.getLocation()) - int(Location::LOG0); + unsigned logNumber = int(function.getLocation()) - int(Location::Log0); for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); @@ -346,7 +349,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(length) << u256(0) << eth::logInstruction(logNumber); break; } - case Location::EVENT: + case Location::Event: { _functionCall.getExpression().accept(*this); auto const& event = dynamic_cast(function.getDeclaration()); @@ -372,18 +375,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed); break; } - case Location::BLOCKHASH: + case Location::BlockHash: { arguments[0]->accept(*this); appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true); m_context << eth::Instruction::BLOCKHASH; break; } - case Location::ECRECOVER: + case Location::ECRecover: case Location::SHA256: case Location::RIPEMD160: { - static const map contractAddresses{{Location::ECRECOVER, 1}, + static const map contractAddresses{{Location::ECRecover, 1}, {Location::SHA256, 2}, {Location::RIPEMD160, 3}}; m_context << contractAddresses.find(function.getLocation())->second; @@ -408,7 +411,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ASTString const& member = _memberAccess.getMemberName(); switch (_memberAccess.getExpression().getType()->getCategory()) { - case Type::Category::CONTRACT: + case Type::Category::Contract: { bool alsoSearchInteger = false; ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); @@ -420,7 +423,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) u256 identifier = type.getFunctionIdentifier(member); if (identifier != Invalid256) { - appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); + appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true); m_context << identifier; } else @@ -430,24 +433,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) if (!alsoSearchInteger) break; } - case Type::Category::INTEGER: + case Type::Category::Integer: if (member == "balance") { appendTypeConversion(*_memberAccess.getExpression().getType(), - IntegerType(0, IntegerType::Modifier::ADDRESS), true); + IntegerType(0, IntegerType::Modifier::Address), true); m_context << eth::Instruction::BALANCE; } else if (member == "send" || member.substr(0, min(member.size(), 4)) == "call") appendTypeConversion(*_memberAccess.getExpression().getType(), - IntegerType(0, IntegerType::Modifier::ADDRESS), true); + IntegerType(0, IntegerType::Modifier::Address), true); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; - case Type::Category::FUNCTION: + case Type::Category::Function: solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member), "Invalid member access to function."); break; - case Type::Category::MAGIC: + case Type::Category::Magic: // we can ignore the kind of magic and only look at the name of the member if (member == "coinbase") m_context << eth::Instruction::COINBASE; @@ -472,15 +475,15 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member.")); break; - case Type::Category::STRUCT: + case Type::Category::Struct: { StructType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; - m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType()); + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_memberAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; } - case Type::Category::TYPE: + case Type::Category::TypeType: { TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); if (type.getMembers().getMemberType(member)) @@ -512,7 +515,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) length += CompilerUtils(m_context).storeInMemory(length); m_context << u256(length) << u256(0) << eth::Instruction::SHA3; - m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType()); + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); return false; @@ -523,7 +526,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { - if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) + if (magicVar->getType()->getCategory() == Type::Category::Contract) // "this" or "super" if (!dynamic_cast(*magicVar->getType()).isSuper()) m_context << eth::Instruction::ADDRESS; @@ -553,9 +556,9 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { switch (_literal.getType()->getCategory()) { - case Type::Category::INTEGER_CONSTANT: - case Type::Category::BOOL: - case Type::Category::STRING: + case Type::Category::IntegerConstant: + case Type::Category::Bool: + case Type::Category::String: m_context << _literal.getType()->literalValue(&_literal); break; default: @@ -566,11 +569,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { Token::Value const c_op = _binaryOperation.getOperator(); - solAssert(c_op == Token::OR || c_op == Token::AND, ""); + solAssert(c_op == Token::Or || c_op == Token::And, ""); _binaryOperation.getLeftExpression().accept(*this); m_context << eth::Instruction::DUP1; - if (c_op == Token::AND) + if (c_op == Token::And) m_context << eth::Instruction::ISZERO; eth::AssemblyItem endLabel = m_context.appendConditionalJump(); m_context << eth::Instruction::POP; @@ -580,10 +583,10 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) { - if (_operator == Token::EQ || _operator == Token::NE) + if (_operator == Token::Equal || _operator == Token::NotEqual) { m_context << eth::Instruction::EQ; - if (_operator == Token::NE) + if (_operator == Token::NotEqual) m_context << eth::Instruction::ISZERO; } else @@ -593,18 +596,18 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type switch (_operator) { - case Token::GTE: + case Token::GreaterThanOrEqual: m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT) << eth::Instruction::ISZERO; break; - case Token::LTE: + case Token::LessThanOrEqual: m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT) << eth::Instruction::ISZERO; break; - case Token::GT: + case Token::GreaterThan: m_context << (c_isSigned ? eth::Instruction::SGT : eth::Instruction::GT); break; - case Token::LT: + case Token::LessThan: m_context << (c_isSigned ? eth::Instruction::SLT : eth::Instruction::LT); break; default: @@ -632,19 +635,19 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty switch (_operator) { - case Token::ADD: + case Token::Add: m_context << eth::Instruction::ADD; break; - case Token::SUB: + case Token::Sub: m_context << eth::Instruction::SUB; break; - case Token::MUL: + case Token::Mul: m_context << eth::Instruction::MUL; break; - case Token::DIV: + case Token::Div: m_context << (c_isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); break; - case Token::MOD: + case Token::Mod: m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; default: @@ -656,13 +659,13 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) { switch (_operator) { - case Token::BIT_OR: + case Token::BitOr: m_context << eth::Instruction::OR; break; - case Token::BIT_AND: + case Token::BitAnd: m_context << eth::Instruction::AND; break; - case Token::BIT_XOR: + case Token::BitXor: m_context << eth::Instruction::XOR; break; default: @@ -695,29 +698,38 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::STRING) + if (stackTypeCategory == Type::Category::String) { - if (targetTypeCategory == Type::Category::INTEGER) + StaticStringType const& typeOnStack = dynamic_cast(_typeOnStack); + if (targetTypeCategory == Type::Category::Integer) { // conversion from string to hash. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - StaticStringType const& typeOnStack = dynamic_cast(_typeOnStack); solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed."); solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; } else { - solAssert(targetTypeCategory == Type::Category::STRING, "Invalid type conversion requested."); - // nothing to do, strings are high-order-bit-aligned - //@todo clear lower-order bytes if we allow explicit conversion to shorter strings + // clear lower-order bytes for conversion to shorter strings - we always clean + solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested."); + StaticStringType const& targetType = dynamic_cast(_targetType); + if (targetType.getNumBytes() < typeOnStack.getNumBytes()) + { + if (targetType.getNumBytes() == 0) + m_context << eth::Instruction::DUP1 << eth::Instruction::XOR; + else + m_context << (u256(1) << (256 - targetType.getNumBytes() * 8)) + << eth::Instruction::DUP1 << eth::Instruction::SWAP2 + << eth::Instruction::DIV << eth::Instruction::MUL; + } } } - else if (stackTypeCategory == Type::Category::INTEGER || stackTypeCategory == Type::Category::CONTRACT || - stackTypeCategory == Type::Category::INTEGER_CONSTANT) + else if (stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::Contract || + stackTypeCategory == Type::Category::IntegerConstant) { - if (targetTypeCategory == Type::Category::STRING && stackTypeCategory == Type::Category::INTEGER) + if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer) { // conversion from hash to string. no need to clean the high bit // only to shift left because of opposite alignment @@ -729,11 +741,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } else { - solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, ""); - IntegerType addressType(0, IntegerType::Modifier::ADDRESS); - IntegerType const& targetType = targetTypeCategory == Type::Category::INTEGER + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); + IntegerType addressType(0, IntegerType::Modifier::Address); + IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast(_targetType) : addressType; - if (stackTypeCategory == Type::Category::INTEGER_CONSTANT) + if (stackTypeCategory == Type::Category::IntegerConstant) { IntegerConstantType const& constType = dynamic_cast(_typeOnStack); // We know that the stack is clean, we only have to clean for a narrowing conversion @@ -743,7 +755,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } else { - IntegerType const& typeOnStack = stackTypeCategory == Type::Category::INTEGER + IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer ? dynamic_cast(_typeOnStack) : addressType; // Widening: clean up according to source type width // Non-widening and force: clean up according to target type bits @@ -773,7 +785,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio vector> const& _arguments, bool bare) { - solAssert(_arguments.size() == _functionType.getParameterTypes().size(), ""); + solAssert(_functionType.takesArbitraryParameters() || + _arguments.size() == _functionType.getParameterTypes().size(), ""); // Assumed stack content here: // @@ -797,7 +810,10 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio // reserve space for the function identifier unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; - dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset); + // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, + // do not pad it to 32 bytes. + dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset, + _functionType.padArguments(), bare); //@todo only return the first return value for now Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : @@ -828,33 +844,44 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio if (retSize > 0) { - bool const c_leftAligned = firstType->getCategory() == Type::Category::STRING; + bool const c_leftAligned = firstType->getCategory() == Type::Category::String; CompilerUtils(m_context).loadFromMemory(0, retSize, c_leftAligned, false, true); } } -unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types, - vector> const& _arguments, - unsigned _memoryOffset) +unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector> const& _arguments, + TypePointers const& _types, + unsigned _memoryOffset, + bool _padToWordBoundaries, + bool _padExceptionIfFourBytes) { + solAssert(_types.empty() || _types.size() == _arguments.size(), ""); unsigned length = 0; - for (unsigned i = 0; i < _arguments.size(); ++i) - length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length); + for (size_t i = 0; i < _arguments.size(); ++i) + { + _arguments[i]->accept(*this); + TypePointer const& expectedType = _types.empty() ? _arguments[i]->getType()->getRealType() : _types[i]; + appendTypeConversion(*_arguments[i]->getType(), *expectedType, true); + bool pad = _padToWordBoundaries; + // Do not pad if the first argument has exactly four bytes + if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4) + pad = false; + length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(), + _memoryOffset + length, pad); + } return length; } -unsigned ExpressionCompiler::appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type, - Location const& _location, unsigned _memoryOffset) +unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries) { - appendTypeConversion(_type, _expectedType, true); - unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize()); + unsigned const c_encodedSize = _type.getCalldataEncodedSize(); + unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize; if (c_numBytes == 0 || c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) - << errinfo_comment("Type " + _expectedType.toString() + " not yet supported.")); - bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING; - bool const c_padToWords = true; - return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords); + << errinfo_comment("Type " + _type.toString() + " not yet supported.")); + bool const c_leftAligned = _type.getCategory() == Type::Category::String; + return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries); } unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, @@ -862,39 +889,65 @@ unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedT unsigned _memoryOffset) { _expression.accept(*this); - return appendTypeConversionAndMoveToMemory(_expectedType, *_expression.getType(), _expression.getLocation(), _memoryOffset); + appendTypeConversion(*_expression.getType(), _expectedType, true); + return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset); } void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - FunctionType thisType(_varDecl); - solAssert(thisType.getReturnParameterTypes().size() == 1, ""); - TypePointer const& resultType = thisType.getReturnParameterTypes().front(); - unsigned sizeOnStack; + FunctionType accessorType(_varDecl); unsigned length = 0; - TypePointers const& params = thisType.getParameterTypes(); + TypePointers const& paramTypes = accessorType.getParameterTypes(); // move arguments to memory - for (TypePointer const& param: boost::adaptors::reverse(params)) - length += appendTypeConversionAndMoveToMemory(*param, *param, Location(), length); + for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes)) + length += appendTypeMoveToMemory(*paramType, Location(), length); - // retrieve the position of the mapping + // retrieve the position of the variable m_context << m_context.getStorageLocationOfVariable(_varDecl); + TypePointer returnType = _varDecl.getType(); - for (TypePointer const& param: params) + for (TypePointer const& paramType: paramTypes) { // move offset to memory CompilerUtils(m_context).storeInMemory(length); - unsigned argLen = CompilerUtils::getPaddedSize(param->getCalldataEncodedSize()); + unsigned argLen = CompilerUtils::getPaddedSize(paramType->getCalldataEncodedSize()); length -= argLen; m_context << u256(argLen + 32) << u256(length) << eth::Instruction::SHA3; + + returnType = dynamic_cast(*returnType).getValueType(); } - m_currentLValue = LValue(m_context, LValue::STORAGE, *resultType); - m_currentLValue.retrieveValue(resultType, Location(), true); - sizeOnStack = resultType->getSizeOnStack(); - solAssert(sizeOnStack <= 15, "Stack too deep."); - m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; + unsigned retSizeOnStack = 0; + solAssert(accessorType.getReturnParameterTypes().size() >= 1, ""); + if (StructType const* structType = dynamic_cast(returnType.get())) + { + auto const& names = accessorType.getReturnParameterNames(); + auto const& types = accessorType.getReturnParameterTypes(); + // struct + for (size_t i = 0; i < names.size(); ++i) + { + m_context << eth::Instruction::DUP1 + << structType->getStorageOffsetOfMember(names[i]) + << eth::Instruction::ADD; + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *types[i]); + m_currentLValue.retrieveValue(types[i], Location(), true); + solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); + m_context << eth::Instruction::SWAP1; + retSizeOnStack += types[i]->getSizeOnStack(); + } + m_context << eth::Instruction::POP; + } + else + { + // simple value + solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *returnType); + m_currentLValue.retrieveValue(returnType, Location(), true); + retSizeOnStack = returnType->getSizeOnStack(); + } + solAssert(retSizeOnStack <= 15, "Stack too deep."); + m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, @@ -903,7 +956,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType { //@todo change the type cast for arrays solAssert(_dataType.getStorageSize() <= numeric_limits::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned"); - if (m_type == STORAGE) + if (m_type == LValueType::Storage) m_size = unsigned(_dataType.getStorageSize()); else m_size = unsigned(_dataType.getSizeOnStack()); @@ -913,7 +966,7 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio { switch (m_type) { - case STACK: + case LValueType::Stack: { unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory @@ -923,10 +976,10 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio *m_context << eth::dupInstruction(stackPos + 1); break; } - case STORAGE: + case LValueType::Storage: retrieveValueFromStorage(_type, _remove); break; - case MEMORY: + case LValueType::Memory: if (!_type->isValueType()) break; // no distinction between value and reference for non-value types BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) @@ -962,7 +1015,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool { switch (m_type) { - case STACK: + case LValueType::Stack: { unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; if (stackDiff > 16) @@ -975,7 +1028,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool retrieveValue(_expression.getType(), _expression.getLocation()); break; } - case LValue::STORAGE: + case LValueType::Storage: if (!_expression.getType()->isValueType()) break; // no distinction between value and reference for non-value types // stack layout: value value ... value ref @@ -1000,7 +1053,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool << u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB; } break; - case LValue::MEMORY: + case LValueType::Memory: if (!_expression.getType()->isValueType()) break; // no distinction between value and reference for non-value types BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) @@ -1017,7 +1070,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const { switch (m_type) { - case STACK: + case LValueType::Stack: { unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); if (stackDiff > 16) @@ -1029,7 +1082,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const << eth::Instruction::POP; break; } - case LValue::STORAGE: + case LValueType::Storage: if (m_size == 0) *m_context << eth::Instruction::POP; for (unsigned i = 0; i < m_size; ++i) @@ -1041,7 +1094,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const << u256(1) << eth::Instruction::ADD; } break; - case LValue::MEMORY: + case LValueType::Memory: if (!_expression.getType()->isValueType()) break; // no distinction between value and reference for non-value types BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) @@ -1066,7 +1119,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type) { - m_type = STORAGE; + m_type = LValueType::Storage; solAssert(_type->getStorageSize() <= numeric_limits::max(), "The storage size of " + _type->toString() + " should fit in an unsigned"); *m_context << m_context->getStorageLocationOfVariable(_varDecl); m_size = unsigned(_type->getStorageSize()); @@ -1076,7 +1129,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D { if (m_context->isLocalVariable(&_declaration)) { - m_type = STACK; + m_type = LValueType::Stack; m_size = _identifier.getType()->getSizeOnStack(); m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration); } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 7de577e6c..3c94d74c2 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -92,17 +92,19 @@ private: /// Appends code to call a function of the given type with the given arguments. void appendExternalFunctionCall(FunctionType const& _functionType, std::vector> const& _arguments, bool bare = false); - /// Appends code that copies the given arguments to memory (with optional offset). - /// @returns the number of bytes copied to memory - unsigned appendArgumentCopyToMemory(TypePointers const& _types, - std::vector> const& _arguments, - unsigned _memoryOffset = 0); - /// Appends code that copies a type to memory. - /// @returns the number of bytes copied to memory - unsigned appendTypeConversionAndMoveToMemory(Type const& _expectedType, Type const& _type, - Location const& _location, unsigned _memoryOffset = 0); - /// Appends code that evaluates a single expression and copies it to memory (with optional offset). - /// @returns the number of bytes copied to memory + /// Appends code that evaluates the given arguments and moves the result to memory (with optional offset). + /// @returns the number of bytes moved to memory + unsigned appendArgumentsCopyToMemory(std::vector> const& _arguments, + TypePointers const& _types = {}, + unsigned _memoryOffset = 0, + bool _padToWordBoundaries = true, + bool _padExceptionIfFourBytes = false); + /// Appends code that moves a stack element of the given type to memory + /// @returns the number of bytes moved to memory + unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, + bool _padToWordBoundaries = true); + /// Appends code that evaluates a single expression and moves the result to memory (with optional offset). + /// @returns the number of bytes moved to memory unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression, unsigned _memoryOffset = 0); @@ -117,7 +119,7 @@ private: class LValue { public: - enum LValueType { NONE, STACK, MEMORY, STORAGE }; + enum class LValueType { None, Stack, Memory, Storage }; explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0); @@ -127,15 +129,15 @@ private: void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); /// Convenience function to set type for a state variable and retrieve the reference void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type); - void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; } + void reset() { m_type = LValueType::None; m_baseStackOffset = 0; m_size = 0; } - bool isValid() const { return m_type != NONE; } - bool isInOnStack() const { return m_type == STACK; } - bool isInMemory() const { return m_type == MEMORY; } - bool isInStorage() const { return m_type == STORAGE; } + bool isValid() const { return m_type != LValueType::None; } + bool isInOnStack() const { return m_type == LValueType::Stack; } + bool isInMemory() const { return m_type == LValueType::Memory; } + bool isInStorage() const { return m_type == LValueType::Storage; } /// @returns true if this lvalue reference type occupies a slot on the stack. - bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; } + bool storesReferenceOnStack() const { return m_type == LValueType::Storage || m_type == LValueType::Memory; } /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// also removes the reference from the stack (note that is does not reset the type to @a NONE). @@ -159,7 +161,7 @@ private: void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; CompilerContext* m_context; - LValueType m_type = NONE; + LValueType m_type = LValueType::None; /// If m_type is STACK, this is base stack offset (@see /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. unsigned m_baseStackOffset = 0; diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 687c9c9d4..60de5105f 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -34,29 +34,29 @@ namespace solidity { GlobalContext::GlobalContext(): -m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::BLOCK)), - make_shared("msg", make_shared(MagicType::Kind::MSG)), - make_shared("tx", make_shared(MagicType::Kind::TX)), +m_magicVariables(vector>{make_shared("block", make_shared(MagicType::Kind::Block)), + make_shared("msg", make_shared(MagicType::Kind::Message)), + make_shared("tx", make_shared(MagicType::Kind::Transaction)), make_shared("suicide", - make_shared(strings{"address"}, strings{}, FunctionType::Location::SUICIDE)), + make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", - make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA3)), + make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)), make_shared("log0", - make_shared(strings{"hash"},strings{}, FunctionType::Location::LOG0)), + make_shared(strings{"hash"},strings{}, FunctionType::Location::Log0)), make_shared("log1", - make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::LOG1)), + make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)), make_shared("log2", - make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::LOG2)), + make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)), make_shared("log3", - make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG3)), + make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)), make_shared("log4", - make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::LOG4)), + make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)), make_shared("sha256", - make_shared(strings{"hash"}, strings{"hash"}, FunctionType::Location::SHA256)), + make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)), make_shared("ecrecover", - make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRECOVER)), + make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", - make_shared(strings{"hash"}, strings{"hash160"}, FunctionType::Location::RIPEMD160))}) + make_shared(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))}) { } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 82486c5d1..7ecde8029 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -13,7 +13,7 @@ namespace solidity InterfaceHandler::InterfaceHandler() { - m_lastTag = DocTagType::NONE; + m_lastTag = DocTagType::None; } std::unique_ptr InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef, @@ -21,13 +21,13 @@ std::unique_ptr InterfaceHandler::getDocumentation(ContractDefiniti { switch(_type) { - case DocumentationType::NATSPEC_USER: + case DocumentationType::NatspecUser: return getUserDocumentation(_contractDef); - case DocumentationType::NATSPEC_DEV: + case DocumentationType::NatspecDev: return getDevDocumentation(_contractDef); - case DocumentationType::ABI_INTERFACE: + case DocumentationType::ABIInterface: return getABIInterface(_contractDef); - case DocumentationType::ABI_SOLIDITY_INTERFACE: + case DocumentationType::ABISolidityInterface: return getABISolidityInterface(_contractDef); } @@ -133,7 +133,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (strPtr) { resetUser(); - parseDocString(*strPtr, CommentOwner::FUNCTION); + parseDocString(*strPtr, CommentOwner::Function); if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); @@ -158,7 +158,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin { m_contractAuthor.clear(); m_title.clear(); - parseDocString(*contractDoc, CommentOwner::CONTRACT); + parseDocString(*contractDoc, CommentOwner::Contract); if (!m_contractAuthor.empty()) doc["author"] = m_contractAuthor; @@ -174,7 +174,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin if (strPtr) { resetDev(); - parseDocString(*strPtr, CommentOwner::FUNCTION); + parseDocString(*strPtr, CommentOwner::Function); if (!m_dev.empty()) method["details"] = Json::Value(m_dev); @@ -251,7 +251,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons auto paramDesc = std::string(currPos, nlPos); m_params.push_back(std::make_pair(paramName, paramDesc)); - m_lastTag = DocTagType::PARAM; + m_lastTag = DocTagType::Param; return skipLineOrEOS(nlPos, _end); } @@ -280,28 +280,28 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases. Also somehow automate list of acceptable tags for each // language construct since current way does not scale well. - if (m_lastTag == DocTagType::NONE || _tag != "") + if (m_lastTag == DocTagType::None || _tag != "") { if (_tag == "dev") - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false); + return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false); else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false); + return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false); else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false); + return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false); else if (_tag == "author") { - if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false); - else if (_owner == CommentOwner::FUNCTION) - return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false); + if (_owner == CommentOwner::Contract) + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false); + else if (_owner == CommentOwner::Function) + return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false); else // LTODO: for now this else makes no sense but later comments will go to more language constructs BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); } else if (_tag == "title") { - if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false); + if (_owner == CommentOwner::Contract) + return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, false); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); @@ -322,27 +322,27 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it { switch (m_lastTag) { - case DocTagType::DEV: - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true); - case DocTagType::NOTICE: - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true); - case DocTagType::RETURN: - return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true); - case DocTagType::AUTHOR: - if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true); - else if (_owner == CommentOwner::FUNCTION) - return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true); + case DocTagType::Dev: + return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true); + case DocTagType::Notice: + return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true); + case DocTagType::Return: + return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true); + case DocTagType::Author: + if (_owner == CommentOwner::Contract) + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true); + else if (_owner == CommentOwner::Function) + return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); - case DocTagType::TITLE: - if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true); + case DocTagType::Title: + if (_owner == CommentOwner::Contract) + return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); - case DocTagType::PARAM: + case DocTagType::Param: return appendDocTagParam(_pos, _end); default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); @@ -378,14 +378,14 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _ currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner); } - else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag + else if (m_lastTag != DocTagType::None) // continuation of the previous tag currPos = appendDocTag(currPos, end, _owner); else if (currPos != end) { // if it begins without a tag then consider it as @notice if (currPos == _string.begin()) { - currPos = parseDocTag(currPos, end, "notice", CommentOwner::FUNCTION); + currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function); continue; } else if (nlPos == end) //end of text diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 2b62cabdf..c6da63de0 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -41,19 +41,19 @@ enum class DocumentationType: uint8_t; enum class DocTagType: uint8_t { - NONE = 0, - DEV, - NOTICE, - PARAM, - RETURN, - AUTHOR, - TITLE + None = 0, + Dev, + Notice, + Param, + Return, + Author, + Title }; enum class CommentOwner { - CONTRACT, - FUNCTION + Contract, + Function }; class InterfaceHandler diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 0ad7bd7ca..f076df876 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -69,10 +69,10 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) { switch (m_scanner->getCurrentToken()) { - case Token::IMPORT: + case Token::Import: nodes.push_back(parseImportDirective()); break; - case Token::CONTRACT: + case Token::Contract: nodes.push_back(parseContractDefinition()); break; default: @@ -100,12 +100,12 @@ int Parser::getEndPosition() const ASTPointer Parser::parseImportDirective() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::IMPORT); - if (m_scanner->getCurrentToken() != Token::STRING_LITERAL) + expectToken(Token::Import); + if (m_scanner->getCurrentToken() != Token::StringLiteral) BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL).")); ASTPointer url = getLiteralAndAdvance(); nodeFactory.markEndPosition(); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); return nodeFactory.createNode(url); } @@ -115,7 +115,7 @@ ASTPointer Parser::parseContractDefinition() ASTPointer docString; if (m_scanner->getCurrentCommentLiteral() != "") docString = make_shared(m_scanner->getCurrentCommentLiteral()); - expectToken(Token::CONTRACT); + expectToken(Token::Contract); ASTPointer name = expectIdentifierToken(); vector> baseContracts; vector> structs; @@ -123,40 +123,40 @@ ASTPointer Parser::parseContractDefinition() vector> functions; vector> modifiers; vector> events; - if (m_scanner->getCurrentToken() == Token::IS) + if (m_scanner->getCurrentToken() == Token::Is) do { m_scanner->next(); baseContracts.push_back(parseInheritanceSpecifier()); } - while (m_scanner->getCurrentToken() == Token::COMMA); - expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() == Token::Comma); + expectToken(Token::LBrace); while (true) { Token::Value currentToken = m_scanner->getCurrentToken(); - if (currentToken == Token::RBRACE) + if (currentToken == Token::RBrace) break; - else if (currentToken == Token::FUNCTION) + else if (currentToken == Token::Function) functions.push_back(parseFunctionDefinition(name.get())); - else if (currentToken == Token::STRUCT) + else if (currentToken == Token::Struct) structs.push_back(parseStructDefinition()); - else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || + else if (currentToken == Token::Identifier || currentToken == Token::Mapping || Token::isElementaryTypeName(currentToken)) { VarDeclParserOptions options; options.isStateVariable = true; stateVariables.push_back(parseVariableDeclaration(options)); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); } - else if (currentToken == Token::MODIFIER) + else if (currentToken == Token::Modifier) modifiers.push_back(parseModifierDefinition()); - else if (currentToken == Token::EVENT) + else if (currentToken == Token::Event) events.push_back(parseEventDefinition()); else BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected.")); } nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); + expectToken(Token::RBrace); return nodeFactory.createNode(name, docString, baseContracts, structs, stateVariables, functions, modifiers, events); } @@ -166,12 +166,12 @@ ASTPointer Parser::parseInheritanceSpecifier() ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); vector> arguments; - if (m_scanner->getCurrentToken() == Token::LPAREN) + if (m_scanner->getCurrentToken() == Token::LParen) { m_scanner->next(); arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); @@ -180,13 +180,13 @@ ASTPointer Parser::parseInheritanceSpecifier() Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) { - Declaration::Visibility visibility; - if (_token == Token::PUBLIC) - visibility = Declaration::Visibility::PUBLIC; - else if (_token == Token::PROTECTED) - visibility = Declaration::Visibility::PROTECTED; - else if (_token == Token::PRIVATE) - visibility = Declaration::Visibility::PRIVATE; + Declaration::Visibility visibility(Declaration::Visibility::Default); + if (_token == Token::Public) + visibility = Declaration::Visibility::Public; + else if (_token == Token::Protected) + visibility = Declaration::Visibility::Protected; + else if (_token == Token::Private) + visibility = Declaration::Visibility::Private; else solAssert(false, "Invalid visibility specifier."); m_scanner->next(); @@ -200,29 +200,29 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* if (m_scanner->getCurrentCommentLiteral() != "") docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - expectToken(Token::FUNCTION); + expectToken(Token::Function); ASTPointer name; - if (m_scanner->getCurrentToken() == Token::LPAREN) + if (m_scanner->getCurrentToken() == Token::LParen) name = make_shared(); // anonymous function else name = expectIdentifierToken(); ASTPointer parameters(parseParameterList()); bool isDeclaredConst = false; - Declaration::Visibility visibility(Declaration::Visibility::DEFAULT); + Declaration::Visibility visibility(Declaration::Visibility::Default); vector> modifiers; while (true) { Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::CONST) + if (token == Token::Const) { isDeclaredConst = true; m_scanner->next(); } - else if (token == Token::IDENTIFIER) + else if (token == Token::Identifier) modifiers.push_back(parseModifierInvocation()); else if (Token::isVisibilitySpecifier(token)) { - if (visibility != Declaration::Visibility::DEFAULT) + if (visibility != Declaration::Visibility::Default) BOOST_THROW_EXCEPTION(createParserError("Multiple visibility specifiers.")); visibility = parseVisibilitySpecifier(token); } @@ -230,7 +230,7 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* break; } ASTPointer returnParameters; - if (m_scanner->getCurrentToken() == Token::RETURNS) + if (m_scanner->getCurrentToken() == Token::Returns) { bool const permitEmptyParameterList = false; m_scanner->next(); @@ -249,17 +249,17 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* ASTPointer Parser::parseStructDefinition() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::STRUCT); + expectToken(Token::Struct); ASTPointer name = expectIdentifierToken(); vector> members; - expectToken(Token::LBRACE); - while (m_scanner->getCurrentToken() != Token::RBRACE) + expectToken(Token::LBrace); + while (m_scanner->getCurrentToken() != Token::RBrace) { members.push_back(parseVariableDeclaration()); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); } nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); + expectToken(Token::RBrace); return nodeFactory.createNode(name, members); } @@ -267,20 +267,31 @@ ASTPointer Parser::parseVariableDeclaration(VarDeclParserOp { ASTNodeFactory nodeFactory(*this); ASTPointer type = parseTypeName(_options.allowVar); + if (type != nullptr) + nodeFactory.setEndPositionFromNode(type); bool isIndexed = false; + ASTPointer identifier; Token::Value token = m_scanner->getCurrentToken(); - if (_options.allowIndexed && token == Token::INDEXED) + Declaration::Visibility visibility(Declaration::Visibility::Default); + if (_options.isStateVariable && Token::isVisibilitySpecifier(token)) + visibility = parseVisibilitySpecifier(token); + if (_options.allowIndexed && token == Token::Indexed) { isIndexed = true; m_scanner->next(); } - Declaration::Visibility visibility(Declaration::Visibility::DEFAULT); - if (_options.isStateVariable && Token::isVisibilitySpecifier(token)) - visibility = parseVisibilitySpecifier(token); nodeFactory.markEndPosition(); - return nodeFactory.createNode(type, expectIdentifierToken(), - visibility, _options.isStateVariable, - isIndexed); + if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier) + { + identifier = make_shared(""); + solAssert(type != nullptr, ""); + nodeFactory.setEndPositionFromNode(type); + } + else + identifier = expectIdentifierToken(); + return nodeFactory.createNode(type, identifier, + visibility, _options.isStateVariable, + isIndexed); } ASTPointer Parser::parseModifierDefinition() @@ -293,10 +304,10 @@ ASTPointer Parser::parseModifierDefinition() if (m_scanner->getCurrentCommentLiteral() != "") docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - expectToken(Token::MODIFIER); + expectToken(Token::Modifier); ASTPointer name(expectIdentifierToken()); ASTPointer parameters; - if (m_scanner->getCurrentToken() == Token::LPAREN) + if (m_scanner->getCurrentToken() == Token::LParen) parameters = parseParameterList(); else parameters = createEmptyParameterList(); @@ -312,15 +323,15 @@ ASTPointer Parser::parseEventDefinition() if (m_scanner->getCurrentCommentLiteral() != "") docstring = make_shared(m_scanner->getCurrentCommentLiteral()); - expectToken(Token::EVENT); + expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); ASTPointer parameters; - if (m_scanner->getCurrentToken() == Token::LPAREN) + if (m_scanner->getCurrentToken() == Token::LParen) parameters = parseParameterList(true, true); else parameters = createEmptyParameterList(); nodeFactory.markEndPosition(); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); return nodeFactory.createNode(name, docstring, parameters); } @@ -329,12 +340,12 @@ ASTPointer Parser::parseModifierInvocation() ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); vector> arguments; - if (m_scanner->getCurrentToken() == Token::LPAREN) + if (m_scanner->getCurrentToken() == Token::LParen) { m_scanner->next(); arguments = parseFunctionCallListArguments(); nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); @@ -357,17 +368,17 @@ ASTPointer Parser::parseTypeName(bool _allowVar) type = ASTNodeFactory(*this).createNode(token); m_scanner->next(); } - else if (token == Token::VAR) + else if (token == Token::Var) { if (!_allowVar) BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name.")); m_scanner->next(); } - else if (token == Token::MAPPING) + else if (token == Token::Mapping) { type = parseMapping(); } - else if (token == Token::IDENTIFIER) + else if (token == Token::Identifier) { ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); @@ -381,18 +392,18 @@ ASTPointer Parser::parseTypeName(bool _allowVar) ASTPointer Parser::parseMapping() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::MAPPING); - expectToken(Token::LPAREN); + expectToken(Token::Mapping); + expectToken(Token::LParen); if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type")); ASTPointer keyType; keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); m_scanner->next(); - expectToken(Token::ARROW); + expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); return nodeFactory.createNode(keyType, valueType); } @@ -402,13 +413,14 @@ ASTPointer Parser::parseParameterList(bool _allowEmpty, bool _all vector> parameters; VarDeclParserOptions options; options.allowIndexed = _allowIndexed; - expectToken(Token::LPAREN); - if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) + options.allowEmptyName = true; + expectToken(Token::LParen); + if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen) { parameters.push_back(parseVariableDeclaration(options)); - while (m_scanner->getCurrentToken() != Token::RPAREN) + while (m_scanner->getCurrentToken() != Token::RParen) { - expectToken(Token::COMMA); + expectToken(Token::Comma); parameters.push_back(parseVariableDeclaration(options)); } } @@ -420,12 +432,12 @@ ASTPointer Parser::parseParameterList(bool _allowEmpty, bool _all ASTPointer Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::LBRACE); + expectToken(Token::LBrace); vector> statements; - while (m_scanner->getCurrentToken() != Token::RBRACE) + while (m_scanner->getCurrentToken() != Token::RBrace) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); + expectToken(Token::RBrace); return nodeFactory.createNode(statements); } @@ -434,28 +446,28 @@ ASTPointer Parser::parseStatement() ASTPointer statement; switch (m_scanner->getCurrentToken()) { - case Token::IF: + case Token::If: return parseIfStatement(); - case Token::WHILE: + case Token::While: return parseWhileStatement(); - case Token::FOR: + case Token::For: return parseForStatement(); - case Token::LBRACE: + case Token::LBrace: return parseBlock(); // starting from here, all statements must be terminated by a semicolon - case Token::CONTINUE: + case Token::Continue: statement = ASTNodeFactory(*this).createNode(); m_scanner->next(); break; - case Token::BREAK: + case Token::Break: statement = ASTNodeFactory(*this).createNode(); m_scanner->next(); break; - case Token::RETURN: + case Token::Return: { ASTNodeFactory nodeFactory(*this); ASTPointer expression; - if (m_scanner->next() != Token::SEMICOLON) + if (m_scanner->next() != Token::Semicolon) { expression = parseExpression(); nodeFactory.setEndPositionFromNode(expression); @@ -463,7 +475,7 @@ ASTPointer Parser::parseStatement() statement = nodeFactory.createNode(expression); break; } - case Token::IDENTIFIER: + case Token::Identifier: if (m_insideModifier && m_scanner->getCurrentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode(); @@ -474,20 +486,20 @@ ASTPointer Parser::parseStatement() default: statement = parseVarDefOrExprStmt(); } - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); return statement; } ASTPointer Parser::parseIfStatement() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::IF); - expectToken(Token::LPAREN); + expectToken(Token::If); + expectToken(Token::LParen); ASTPointer condition = parseExpression(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); ASTPointer trueBody = parseStatement(); ASTPointer falseBody; - if (m_scanner->getCurrentToken() == Token::ELSE) + if (m_scanner->getCurrentToken() == Token::Else) { m_scanner->next(); falseBody = parseStatement(); @@ -501,10 +513,10 @@ ASTPointer Parser::parseIfStatement() ASTPointer Parser::parseWhileStatement() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::WHILE); - expectToken(Token::LPAREN); + expectToken(Token::While); + expectToken(Token::LParen); ASTPointer condition = parseExpression(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode(condition, body); @@ -516,21 +528,21 @@ ASTPointer Parser::parseForStatement() ASTPointer initExpression; ASTPointer conditionExpression; ASTPointer loopExpression; - expectToken(Token::FOR); - expectToken(Token::LPAREN); + expectToken(Token::For); + expectToken(Token::LParen); - // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN? - if (m_scanner->getCurrentToken() != Token::SEMICOLON) + // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? + if (m_scanner->getCurrentToken() != Token::Semicolon) initExpression = parseVarDefOrExprStmt(); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); - if (m_scanner->getCurrentToken() != Token::SEMICOLON) + if (m_scanner->getCurrentToken() != Token::Semicolon) conditionExpression = parseExpression(); - expectToken(Token::SEMICOLON); + expectToken(Token::Semicolon); - if (m_scanner->getCurrentToken() != Token::RPAREN) + if (m_scanner->getCurrentToken() != Token::RParen) loopExpression = parseExpressionStatement(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); @@ -555,7 +567,7 @@ ASTPointer Parser::parseVariableDefinition() options.allowVar = true; ASTPointer variable = parseVariableDeclaration(options); ASTPointer value; - if (m_scanner->getCurrentToken() == Token::ASSIGN) + if (m_scanner->getCurrentToken() == Token::Assign) { m_scanner->next(); value = parseExpression(); @@ -632,9 +644,9 @@ ASTPointer Parser::parseLeftHandSideExpression() { ASTNodeFactory nodeFactory(*this); ASTPointer expression; - if (m_scanner->getCurrentToken() == Token::NEW) + if (m_scanner->getCurrentToken() == Token::New) { - expectToken(Token::NEW); + expectToken(Token::New); ASTPointer contractName(parseIdentifier()); nodeFactory.setEndPositionFromNode(contractName); expression = nodeFactory.createNode(contractName); @@ -646,30 +658,30 @@ ASTPointer Parser::parseLeftHandSideExpression() { switch (m_scanner->getCurrentToken()) { - case Token::LBRACK: + case Token::LBrack: { m_scanner->next(); ASTPointer index = parseExpression(); nodeFactory.markEndPosition(); - expectToken(Token::RBRACK); + expectToken(Token::RBrack); expression = nodeFactory.createNode(expression, index); } break; - case Token::PERIOD: + case Token::Period: { m_scanner->next(); nodeFactory.markEndPosition(); expression = nodeFactory.createNode(expression, expectIdentifierToken()); } break; - case Token::LPAREN: + case Token::LParen: { m_scanner->next(); vector> arguments; vector> names; std::tie(arguments, names) = parseFunctionCallArguments(); nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); expression = nodeFactory.createNode(expression, arguments, names); } break; @@ -686,24 +698,34 @@ ASTPointer Parser::parsePrimaryExpression() ASTPointer expression; switch (token) { - case Token::TRUE_LITERAL: - case Token::FALSE_LITERAL: + case Token::TrueLiteral: + case Token::FalseLiteral: expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; - case Token::NUMBER: - case Token::STRING_LITERAL: + case Token::Number: + if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) + { + ASTPointer literal = getLiteralAndAdvance(); + nodeFactory.markEndPosition(); + Literal::SubDenomination subdenomination = static_cast(m_scanner->getCurrentToken()); + m_scanner->next(); + expression = nodeFactory.createNode(token, literal, subdenomination); + break; + } + // fall-through + case Token::StringLiteral: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; - case Token::IDENTIFIER: + case Token::Identifier: nodeFactory.markEndPosition(); expression = nodeFactory.createNode(getLiteralAndAdvance()); break; - case Token::LPAREN: + case Token::LParen: { m_scanner->next(); ASTPointer expression = parseExpression(); - expectToken(Token::RPAREN); + expectToken(Token::RParen); return expression; } default: @@ -726,12 +748,12 @@ ASTPointer Parser::parsePrimaryExpression() vector> Parser::parseFunctionCallListArguments() { vector> arguments; - if (m_scanner->getCurrentToken() != Token::RPAREN) + if (m_scanner->getCurrentToken() != Token::RParen) { arguments.push_back(parseExpression()); - while (m_scanner->getCurrentToken() != Token::RPAREN) + while (m_scanner->getCurrentToken() != Token::RParen) { - expectToken(Token::COMMA); + expectToken(Token::Comma); arguments.push_back(parseExpression()); } } @@ -742,22 +764,22 @@ pair>, vector>> Parser::pars { pair>, vector>> ret; Token::Value token = m_scanner->getCurrentToken(); - if (token == Token::LBRACE) + if (token == Token::LBrace) { // call({arg1 : 1, arg2 : 2 }) - expectToken(Token::LBRACE); - while (m_scanner->getCurrentToken() != Token::RBRACE) + expectToken(Token::LBrace); + while (m_scanner->getCurrentToken() != Token::RBrace) { ret.second.push_back(expectIdentifierToken()); - expectToken(Token::COLON); + expectToken(Token::Colon); ret.first.push_back(parseExpression()); - if (m_scanner->getCurrentToken() == Token::COMMA) - expectToken(Token::COMMA); + if (m_scanner->getCurrentToken() == Token::Comma) + expectToken(Token::Comma); else break; } - expectToken(Token::RBRACE); + expectToken(Token::RBrace); } else ret.first = parseFunctionCallListArguments(); @@ -771,11 +793,11 @@ bool Parser::peekVariableDefinition() // (which include assignments to other expressions and pre-declared variables) // We have a variable definition if we get a keyword that specifies a type name, or // in the case of a user-defined type, we have two identifiers following each other. - return (m_scanner->getCurrentToken() == Token::MAPPING || - m_scanner->getCurrentToken() == Token::VAR || + return (m_scanner->getCurrentToken() == Token::Mapping || + m_scanner->getCurrentToken() == Token::Var || ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || - m_scanner->getCurrentToken() == Token::IDENTIFIER) && - m_scanner->peekNextToken() == Token::IDENTIFIER)); + m_scanner->getCurrentToken() == Token::Identifier) && + m_scanner->peekNextToken() == Token::Identifier)); } void Parser::expectToken(Token::Value _value) @@ -796,7 +818,7 @@ Token::Value Parser::expectAssignmentOperator() ASTPointer Parser::expectIdentifierToken() { - if (m_scanner->getCurrentToken() != Token::IDENTIFIER) + if (m_scanner->getCurrentToken() != Token::Identifier) BOOST_THROW_EXCEPTION(createParserError("Expected identifier")); return getLiteralAndAdvance(); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 19e0af1aa..5816fec40 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -50,6 +50,7 @@ private: bool allowVar = false; bool isStateVariable = false; bool allowIndexed = false; + bool allowEmptyName = false; }; ///@{ diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index b283ca10e..fc4bdb6b5 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -225,7 +225,7 @@ Token::Value Scanner::skipSingleLineComment() // separately by the lexical grammar and becomes part of the // stream of input elements for the syntactic grammar while (advance() && !isLineTerminator(m_char)) { }; - return Token::WHITESPACE; + return Token::Whitespace; } Token::Value Scanner::scanSingleLineDocComment() @@ -255,7 +255,7 @@ Token::Value Scanner::scanSingleLineDocComment() advance(); } literal.complete(); - return Token::COMMENT_LITERAL; + return Token::CommentLiteral; } Token::Value Scanner::skipMultiLineComment() @@ -272,11 +272,11 @@ Token::Value Scanner::skipMultiLineComment() if (ch == '*' && m_char == '/') { m_char = ' '; - return Token::WHITESPACE; + return Token::Whitespace; } } // Unterminated multi-line comment. - return Token::ILLEGAL; + return Token::Illegal; } Token::Value Scanner::scanMultiLineDocComment() @@ -319,9 +319,9 @@ Token::Value Scanner::scanMultiLineDocComment() } literal.complete(); if (!endFound) - return Token::ILLEGAL; + return Token::Illegal; else - return Token::COMMENT_LITERAL; + return Token::CommentLiteral; } Token::Value Scanner::scanSlash() @@ -331,7 +331,7 @@ Token::Value Scanner::scanSlash() if (m_char == '/') { if (!advance()) /* double slash comment directly before EOS */ - return Token::WHITESPACE; + return Token::Whitespace; else if (m_char == '/') { // doxygen style /// comment @@ -340,7 +340,7 @@ Token::Value Scanner::scanSlash() comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; - return Token::WHITESPACE; + return Token::Whitespace; } else return skipSingleLineComment(); @@ -349,7 +349,7 @@ Token::Value Scanner::scanSlash() { // doxygen style /** natspec comment if (!advance()) /* slash star comment before EOS */ - return Token::WHITESPACE; + return Token::Whitespace; else if (m_char == '*') { advance(); //consume the last '*' at /** @@ -366,15 +366,15 @@ Token::Value Scanner::scanSlash() m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; } - return Token::WHITESPACE; + return Token::Whitespace; } else return skipMultiLineComment(); } else if (m_char == '=') - return selectToken(Token::ASSIGN_DIV); + return selectToken(Token::AssignDiv); else - return Token::DIV; + return Token::Div; } void Scanner::scanToken() @@ -391,7 +391,7 @@ void Scanner::scanToken() case '\n': // fall-through case ' ': case '\t': - token = selectToken(Token::WHITESPACE); + token = selectToken(Token::Whitespace); break; case '"': case '\'': @@ -401,79 +401,76 @@ void Scanner::scanToken() // < <= << <<= advance(); if (m_char == '=') - token = selectToken(Token::LTE); + token = selectToken(Token::LessThanOrEqual); else if (m_char == '<') - token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); + token = selectToken('=', Token::AssignShl, Token::SHL); else - token = Token::LT; + token = Token::LessThan; break; case '>': // > >= >> >>= >>> >>>= advance(); if (m_char == '=') - token = selectToken(Token::GTE); + token = selectToken(Token::GreaterThanOrEqual); else if (m_char == '>') { // >> >>= >>> >>>= advance(); if (m_char == '=') - token = selectToken(Token::ASSIGN_SAR); + token = selectToken(Token::AssignSar); else if (m_char == '>') - token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); + token = selectToken('=', Token::AssignShr, Token::SHR); else token = Token::SAR; } else - token = Token::GT; + token = Token::GreaterThan; break; case '=': // = == => advance(); if (m_char == '=') - token = selectToken(Token::EQ); + token = selectToken(Token::Equal); else if (m_char == '>') - token = selectToken(Token::ARROW); + token = selectToken(Token::Arrow); else - token = Token::ASSIGN; + token = Token::Assign; break; case '!': // ! != advance(); if (m_char == '=') - token = selectToken(Token::NE); + token = selectToken(Token::NotEqual); else - token = Token::NOT; + token = Token::Not; break; case '+': // + ++ += advance(); if (m_char == '+') - token = selectToken(Token::INC); + token = selectToken(Token::Inc); else if (m_char == '=') - token = selectToken(Token::ASSIGN_ADD); + token = selectToken(Token::AssignAdd); else - token = Token::ADD; + token = Token::Add; break; case '-': // - -- -= advance(); if (m_char == '-') - { - advance(); - token = Token::DEC; - } + token = selectToken(Token::Dec); else if (m_char == '=') - token = selectToken(Token::ASSIGN_SUB); + token = selectToken(Token::AssignSub); else - token = Token::SUB; + token = Token::Sub; break; case '*': // * *= - token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); + token = selectToken('=', Token::AssignMul, Token::Mul); break; case '%': // % %= - token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); + token = selectToken('=', Token::AssignMod, Token::Mod); break; case '/': // / // /* /= @@ -483,25 +480,25 @@ void Scanner::scanToken() // & && &= advance(); if (m_char == '&') - token = selectToken(Token::AND); + token = selectToken(Token::And); else if (m_char == '=') - token = selectToken(Token::ASSIGN_BIT_AND); + token = selectToken(Token::AssignBitAnd); else - token = Token::BIT_AND; + token = Token::BitAnd; break; case '|': // | || |= advance(); if (m_char == '|') - token = selectToken(Token::OR); + token = selectToken(Token::Or); else if (m_char == '=') - token = selectToken(Token::ASSIGN_BIT_OR); + token = selectToken(Token::AssignBitOr); else - token = Token::BIT_OR; + token = Token::BitOr; break; case '^': // ^ ^= - token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + token = selectToken('=', Token::AssignBitXor, Token::BitXor); break; case '.': // . Number @@ -509,40 +506,40 @@ void Scanner::scanToken() if (isDecimalDigit(m_char)) token = scanNumber('.'); else - token = Token::PERIOD; + token = Token::Period; break; case ':': - token = selectToken(Token::COLON); + token = selectToken(Token::Colon); break; case ';': - token = selectToken(Token::SEMICOLON); + token = selectToken(Token::Semicolon); break; case ',': - token = selectToken(Token::COMMA); + token = selectToken(Token::Comma); break; case '(': - token = selectToken(Token::LPAREN); + token = selectToken(Token::LParen); break; case ')': - token = selectToken(Token::RPAREN); + token = selectToken(Token::RParen); break; case '[': - token = selectToken(Token::LBRACK); + token = selectToken(Token::LBrack); break; case ']': - token = selectToken(Token::RBRACK); + token = selectToken(Token::RBrack); break; case '{': - token = selectToken(Token::LBRACE); + token = selectToken(Token::LBrace); break; case '}': - token = selectToken(Token::RBRACE); + token = selectToken(Token::RBrace); break; case '?': - token = selectToken(Token::CONDITIONAL); + token = selectToken(Token::Conditional); break; case '~': - token = selectToken(Token::BIT_NOT); + token = selectToken(Token::BitNot); break; default: if (isIdentifierStart(m_char)) @@ -550,17 +547,17 @@ void Scanner::scanToken() else if (isDecimalDigit(m_char)) token = scanNumber(); else if (skipWhitespace()) - token = Token::WHITESPACE; + token = Token::Whitespace; else if (isSourcePastEndOfInput()) token = Token::EOS; else - token = selectToken(Token::ILLEGAL); + token = selectToken(Token::Illegal); break; } // Continue scanning for tokens as long as we're just skipping // whitespace. } - while (token == Token::WHITESPACE); + while (token == Token::Whitespace); m_nextToken.location.end = getSourcePos(); m_nextToken.token = token; } @@ -618,16 +615,16 @@ Token::Value Scanner::scanString() if (c == '\\') { if (isSourcePastEndOfInput() || !scanEscape()) - return Token::ILLEGAL; + return Token::Illegal; } else addLiteralChar(c); } if (m_char != quote) - return Token::ILLEGAL; + return Token::Illegal; literal.complete(); advance(); // consume quote - return Token::STRING_LITERAL; + return Token::StringLiteral; } void Scanner::scanDecimalDigits() @@ -660,7 +657,7 @@ Token::Value Scanner::scanNumber(char _charSeen) kind = HEX; addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) - return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X' + return Token::Illegal; // we must have at least one hex digit after 'x'/'X' while (isHexDigit(m_char)) addLiteralCharAndAdvance(); } @@ -681,13 +678,13 @@ Token::Value Scanner::scanNumber(char _charSeen) { solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) - return Token::ILLEGAL; + return Token::Illegal; // scan exponent addLiteralCharAndAdvance(); if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); if (!isDecimalDigit(m_char)) - return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E' + return Token::Illegal; // we must have at least one decimal digit after 'e'/'E' scanDecimalDigits(); } // The source character immediately following a numeric literal must @@ -695,9 +692,9 @@ Token::Value Scanner::scanNumber(char _charSeen) // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). if (isDecimalDigit(m_char) || isIdentifierStart(m_char)) - return Token::ILLEGAL; + return Token::Illegal; literal.complete(); - return Token::NUMBER; + return Token::Number; } Token::Value Scanner::scanIdentifierOrKeyword() diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 7dc56c327..d07d75021 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -80,7 +80,6 @@ char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; - Token::Value Token::fromIdentifierOrKeyword(const std::string& _name) { // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored @@ -91,7 +90,7 @@ Token::Value Token::fromIdentifierOrKeyword(const std::string& _name) #undef KEYWORD #undef TOKEN auto it = keywords.find(_name); - return it == keywords.end() ? Token::IDENTIFIER : it->second; + return it == keywords.end() ? Token::Identifier : it->second; } #undef KT diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 76e504499..b913f0cec 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -47,11 +47,6 @@ #include #include -#if defined(DELETE) -#undef DELETE -//#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes. -#endif - namespace dev { namespace solidity @@ -77,262 +72,267 @@ namespace solidity T(EOS, "EOS", 0) \ \ /* Punctuators (ECMA-262, section 7.7, page 15). */ \ - T(LPAREN, "(", 0) \ - T(RPAREN, ")", 0) \ - T(LBRACK, "[", 0) \ - T(RBRACK, "]", 0) \ - T(LBRACE, "{", 0) \ - T(RBRACE, "}", 0) \ - T(COLON, ":", 0) \ - T(SEMICOLON, ";", 0) \ - T(PERIOD, ".", 0) \ - T(CONDITIONAL, "?", 3) \ - T(ARROW, "=>", 0) \ + T(LParen, "(", 0) \ + T(RParen, ")", 0) \ + T(LBrack, "[", 0) \ + T(RBrack, "]", 0) \ + T(LBrace, "{", 0) \ + T(RBrace, "}", 0) \ + T(Colon, ":", 0) \ + T(Semicolon, ";", 0) \ + T(Period, ".", 0) \ + T(Conditional, "?", 3) \ + T(Arrow, "=>", 0) \ \ /* Assignment operators. */ \ /* IsAssignmentOp() relies on this block of enum values being */ \ /* contiguous and sorted in the same order!*/ \ - T(ASSIGN, "=", 2) \ + T(Assign, "=", 2) \ /* The following have to be in exactly the same order as the simple binary operators*/ \ - T(ASSIGN_BIT_OR, "|=", 2) \ - T(ASSIGN_BIT_XOR, "^=", 2) \ - T(ASSIGN_BIT_AND, "&=", 2) \ - T(ASSIGN_SHL, "<<=", 2) \ - T(ASSIGN_SAR, ">>=", 2) \ - T(ASSIGN_SHR, ">>>=", 2) \ - T(ASSIGN_ADD, "+=", 2) \ - T(ASSIGN_SUB, "-=", 2) \ - T(ASSIGN_MUL, "*=", 2) \ - T(ASSIGN_DIV, "/=", 2) \ - T(ASSIGN_MOD, "%=", 2) \ + T(AssignBitOr, "|=", 2) \ + T(AssignBitXor, "^=", 2) \ + T(AssignBitAnd, "&=", 2) \ + T(AssignShl, "<<=", 2) \ + T(AssignSar, ">>=", 2) \ + T(AssignShr, ">>>=", 2) \ + T(AssignAdd, "+=", 2) \ + T(AssignSub, "-=", 2) \ + T(AssignMul, "*=", 2) \ + T(AssignDiv, "/=", 2) \ + T(AssignMod, "%=", 2) \ \ /* Binary operators sorted by precedence. */ \ /* IsBinaryOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ - T(COMMA, ",", 1) \ - T(OR, "||", 4) \ - T(AND, "&&", 5) \ - T(BIT_OR, "|", 8) \ - T(BIT_XOR, "^", 9) \ - T(BIT_AND, "&", 10) \ + T(Comma, ",", 1) \ + T(Or, "||", 4) \ + T(And, "&&", 5) \ + T(BitOr, "|", 8) \ + T(BitXor, "^", 9) \ + T(BitAnd, "&", 10) \ T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ - T(ADD, "+", 12) \ - T(SUB, "-", 12) \ - T(MUL, "*", 13) \ - T(DIV, "/", 13) \ - T(MOD, "%", 13) \ + T(Add, "+", 12) \ + T(Sub, "-", 12) \ + T(Mul, "*", 13) \ + T(Div, "/", 13) \ + T(Mod, "%", 13) \ \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ - T(EQ, "==", 6) \ - T(NE, "!=", 6) \ - T(LT, "<", 7) \ - T(GT, ">", 7) \ - T(LTE, "<=", 7) \ - T(GTE, ">=", 7) \ - K(IN, "in", 7) \ + T(Equal, "==", 6) \ + T(NotEqual, "!=", 6) \ + T(LessThan, "<", 7) \ + T(GreaterThan, ">", 7) \ + T(LessThanOrEqual, "<=", 7) \ + T(GreaterThanOrEqual, ">=", 7) \ + K(In, "in", 7) \ \ /* Unary operators. */ \ /* IsUnaryOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ - T(NOT, "!", 0) \ - T(BIT_NOT, "~", 0) \ - T(INC, "++", 0) \ - T(DEC, "--", 0) \ - K(DELETE, "delete", 0) \ + T(Not, "!", 0) \ + T(BitNot, "~", 0) \ + T(Inc, "++", 0) \ + T(Dec, "--", 0) \ + K(Delete, "delete", 0) \ \ /* Keywords */ \ - K(BREAK, "break", 0) \ - K(CASE, "case", 0) \ - K(CONST, "constant", 0) \ - K(CONTINUE, "continue", 0) \ - K(CONTRACT, "contract", 0) \ - K(DEFAULT, "default", 0) \ - K(DO, "do", 0) \ - K(ELSE, "else", 0) \ - K(EVENT, "event", 0) \ - K(IS, "is", 0) \ - K(INDEXED, "indexed", 0) \ - K(FOR, "for", 0) \ - K(FUNCTION, "function", 0) \ - K(IF, "if", 0) \ - K(IMPORT, "import", 0) \ - K(MAPPING, "mapping", 0) \ - K(MODIFIER, "modifier", 0) \ - K(NEW, "new", 0) \ - K(PUBLIC, "public", 0) \ - K(PRIVATE, "private", 0) \ - K(PROTECTED, "protected", 0) \ - K(RETURN, "return", 0) \ - K(RETURNS, "returns", 0) \ - K(STRUCT, "struct", 0) \ - K(SWITCH, "switch", 0) \ - K(VAR, "var", 0) \ - K(WHILE, "while", 0) \ + K(Break, "break", 0) \ + K(Case, "case", 0) \ + K(Const, "constant", 0) \ + K(Continue, "continue", 0) \ + K(Contract, "contract", 0) \ + K(Default, "default", 0) \ + K(Do, "do", 0) \ + K(Else, "else", 0) \ + K(Event, "event", 0) \ + K(Is, "is", 0) \ + K(Indexed, "indexed", 0) \ + K(For, "for", 0) \ + K(Function, "function", 0) \ + K(If, "if", 0) \ + K(Import, "import", 0) \ + K(Mapping, "mapping", 0) \ + K(Modifier, "modifier", 0) \ + K(New, "new", 0) \ + K(Public, "public", 0) \ + K(Private, "private", 0) \ + K(Protected, "protected", 0) \ + K(Return, "return", 0) \ + K(Returns, "returns", 0) \ + K(Struct, "struct", 0) \ + K(Switch, "switch", 0) \ + K(Var, "var", 0) \ + K(While, "while", 0) \ \ \ + /* Ether subdenominations */ \ + K(SubWei, "wei", 0) \ + K(SubSzabo, "szabo", 0) \ + K(SubFinney, "finney", 0) \ + K(SubEther, "ether", 0) \ /* type keywords, keep them in this order, keep int as first keyword * the implementation in Types.cpp has to be synced to this here * TODO more to be added */ \ - K(INT, "int", 0) \ - K(INT8, "int8", 0) \ - K(INT16, "int16", 0) \ - K(INT24, "int24", 0) \ - K(INT32, "int32", 0) \ - K(INT40, "int40", 0) \ - K(INT48, "int48", 0) \ - K(INT56, "int56", 0) \ - K(INT64, "int64", 0) \ - K(INT72, "int72", 0) \ - K(INT80, "int80", 0) \ - K(INT88, "int88", 0) \ - K(INT96, "int96", 0) \ - K(INT104, "int104", 0) \ - K(INT112, "int112", 0) \ - K(INT120, "int120", 0) \ - K(INT128, "int128", 0) \ - K(INT136, "int136", 0) \ - K(INT144, "int144", 0) \ - K(INT152, "int152", 0) \ - K(INT160, "int160", 0) \ - K(INT168, "int168", 0) \ - K(INT176, "int178", 0) \ - K(INT184, "int184", 0) \ - K(INT192, "int192", 0) \ - K(INT200, "int200", 0) \ - K(INT208, "int208", 0) \ - K(INT216, "int216", 0) \ - K(INT224, "int224", 0) \ - K(INT232, "int232", 0) \ - K(INT240, "int240", 0) \ - K(INT248, "int248", 0) \ - K(INT256, "int256", 0) \ - K(UINT, "uint", 0) \ - K(UINT8, "uint8", 0) \ - K(UINT16, "uint16", 0) \ - K(UINT24, "uint24", 0) \ - K(UINT32, "uint32", 0) \ - K(UINT40, "uint40", 0) \ - K(UINT48, "uint48", 0) \ - K(UINT56, "uint56", 0) \ - K(UINT64, "uint64", 0) \ - K(UINT72, "uint72", 0) \ - K(UINT80, "uint80", 0) \ - K(UINT88, "uint88", 0) \ - K(UINT96, "uint96", 0) \ - K(UINT104, "uint104", 0) \ - K(UINT112, "uint112", 0) \ - K(UINT120, "uint120", 0) \ - K(UINT128, "uint128", 0) \ - K(UINT136, "uint136", 0) \ - K(UINT144, "uint144", 0) \ - K(UINT152, "uint152", 0) \ - K(UINT160, "uint160", 0) \ - K(UINT168, "uint168", 0) \ - K(UINT176, "uint178", 0) \ - K(UINT184, "uint184", 0) \ - K(UINT192, "uint192", 0) \ - K(UINT200, "uint200", 0) \ - K(UINT208, "uint208", 0) \ - K(UINT216, "uint216", 0) \ - K(UINT224, "uint224", 0) \ - K(UINT232, "uint232", 0) \ - K(UINT240, "uint240", 0) \ - K(UINT248, "uint248", 0) \ - K(UINT256, "uint256", 0) \ - K(HASH, "hash", 0) \ - K(HASH8, "hash8", 0) \ - K(HASH16, "hash16", 0) \ - K(HASH24, "hash24", 0) \ - K(HASH32, "hash32", 0) \ - K(HASH40, "hash40", 0) \ - K(HASH48, "hash48", 0) \ - K(HASH56, "hash56", 0) \ - K(HASH64, "hash64", 0) \ - K(HASH72, "hash72", 0) \ - K(HASH80, "hash80", 0) \ - K(HASH88, "hash88", 0) \ - K(HASH96, "hash96", 0) \ - K(HASH104, "hash104", 0) \ - K(HASH112, "hash112", 0) \ - K(HASH120, "hash120", 0) \ - K(HASH128, "hash128", 0) \ - K(HASH136, "hash136", 0) \ - K(HASH144, "hash144", 0) \ - K(HASH152, "hash152", 0) \ - K(HASH160, "hash160", 0) \ - K(HASH168, "hash168", 0) \ - K(HASH176, "hash178", 0) \ - K(HASH184, "hash184", 0) \ - K(HASH192, "hash192", 0) \ - K(HASH200, "hash200", 0) \ - K(HASH208, "hash208", 0) \ - K(HASH216, "hash216", 0) \ - K(HASH224, "hash224", 0) \ - K(HASH232, "hash232", 0) \ - K(HASH240, "hash240", 0) \ - K(HASH248, "hash248", 0) \ - K(HASH256, "hash256", 0) \ - K(ADDRESS, "address", 0) \ - K(BOOL, "bool", 0) \ - K(STRING_TYPE, "string", 0) \ - K(STRING0, "string0", 0) \ - K(STRING1, "string1", 0) \ - K(STRING2, "string2", 0) \ - K(STRING3, "string3", 0) \ - K(STRING4, "string4", 0) \ - K(STRING5, "string5", 0) \ - K(STRING6, "string6", 0) \ - K(STRING7, "string7", 0) \ - K(STRING8, "string8", 0) \ - K(STRING9, "string9", 0) \ - K(STRING10, "string10", 0) \ - K(STRING11, "string11", 0) \ - K(STRING12, "string12", 0) \ - K(STRING13, "string13", 0) \ - K(STRING14, "string14", 0) \ - K(STRING15, "string15", 0) \ - K(STRING16, "string16", 0) \ - K(STRING17, "string17", 0) \ - K(STRING18, "string18", 0) \ - K(STRING19, "string19", 0) \ - K(STRING20, "string20", 0) \ - K(STRING21, "string21", 0) \ - K(STRING22, "string22", 0) \ - K(STRING23, "string23", 0) \ - K(STRING24, "string24", 0) \ - K(STRING25, "string25", 0) \ - K(STRING26, "string26", 0) \ - K(STRING27, "string27", 0) \ - K(STRING28, "string28", 0) \ - K(STRING29, "string29", 0) \ - K(STRING30, "string30", 0) \ - K(STRING31, "string31", 0) \ - K(STRING32, "string32", 0) \ - K(TEXT, "text", 0) \ - K(REAL, "real", 0) \ - K(UREAL, "ureal", 0) \ - T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ + K(Int, "int", 0) \ + K(Int8, "int8", 0) \ + K(Int16, "int16", 0) \ + K(Int24, "int24", 0) \ + K(Int32, "int32", 0) \ + K(Int40, "int40", 0) \ + K(Int48, "int48", 0) \ + K(Int56, "int56", 0) \ + K(Int64, "int64", 0) \ + K(Int72, "int72", 0) \ + K(Int80, "int80", 0) \ + K(Int88, "int88", 0) \ + K(Int96, "int96", 0) \ + K(Int104, "int104", 0) \ + K(Int112, "int112", 0) \ + K(Int120, "int120", 0) \ + K(Int128, "int128", 0) \ + K(Int136, "int136", 0) \ + K(Int144, "int144", 0) \ + K(Int152, "int152", 0) \ + K(Int160, "int160", 0) \ + K(Int168, "int168", 0) \ + K(Int176, "int178", 0) \ + K(Int184, "int184", 0) \ + K(Int192, "int192", 0) \ + K(Int200, "int200", 0) \ + K(Int208, "int208", 0) \ + K(Int216, "int216", 0) \ + K(Int224, "int224", 0) \ + K(Int232, "int232", 0) \ + K(Int240, "int240", 0) \ + K(Int248, "int248", 0) \ + K(Int256, "int256", 0) \ + K(UInt, "uint", 0) \ + K(UInt8, "uint8", 0) \ + K(UInt16, "uint16", 0) \ + K(UInt24, "uint24", 0) \ + K(UInt32, "uint32", 0) \ + K(UInt40, "uint40", 0) \ + K(UInt48, "uint48", 0) \ + K(UInt56, "uint56", 0) \ + K(UInt64, "uint64", 0) \ + K(UInt72, "uint72", 0) \ + K(UInt80, "uint80", 0) \ + K(UInt88, "uint88", 0) \ + K(UInt96, "uint96", 0) \ + K(UInt104, "uint104", 0) \ + K(UInt112, "uint112", 0) \ + K(UInt120, "uint120", 0) \ + K(UInt128, "uint128", 0) \ + K(UInt136, "uint136", 0) \ + K(UInt144, "uint144", 0) \ + K(UInt152, "uint152", 0) \ + K(UInt160, "uint160", 0) \ + K(UInt168, "uint168", 0) \ + K(UInt176, "uint178", 0) \ + K(UInt184, "uint184", 0) \ + K(UInt192, "uint192", 0) \ + K(UInt200, "uint200", 0) \ + K(UInt208, "uint208", 0) \ + K(UInt216, "uint216", 0) \ + K(UInt224, "uint224", 0) \ + K(UInt232, "uint232", 0) \ + K(UInt240, "uint240", 0) \ + K(UInt248, "uint248", 0) \ + K(UInt256, "uint256", 0) \ + K(Hash, "hash", 0) \ + K(Hash8, "hash8", 0) \ + K(Hash16, "hash16", 0) \ + K(Hash24, "hash24", 0) \ + K(Hash32, "hash32", 0) \ + K(Hash40, "hash40", 0) \ + K(Hash48, "hash48", 0) \ + K(Hash56, "hash56", 0) \ + K(Hash64, "hash64", 0) \ + K(Hash72, "hash72", 0) \ + K(Hash80, "hash80", 0) \ + K(Hash88, "hash88", 0) \ + K(Hash96, "hash96", 0) \ + K(Hash104, "hash104", 0) \ + K(Hash112, "hash112", 0) \ + K(Hash120, "hash120", 0) \ + K(Hash128, "hash128", 0) \ + K(Hash136, "hash136", 0) \ + K(Hash144, "hash144", 0) \ + K(Hash152, "hash152", 0) \ + K(Hash160, "hash160", 0) \ + K(Hash168, "hash168", 0) \ + K(Hash176, "hash178", 0) \ + K(Hash184, "hash184", 0) \ + K(Hash192, "hash192", 0) \ + K(Hash200, "hash200", 0) \ + K(Hash208, "hash208", 0) \ + K(Hash216, "hash216", 0) \ + K(Hash224, "hash224", 0) \ + K(Hash232, "hash232", 0) \ + K(Hash240, "hash240", 0) \ + K(Hash248, "hash248", 0) \ + K(Hash256, "hash256", 0) \ + K(Address, "address", 0) \ + K(Bool, "bool", 0) \ + K(StringType, "string", 0) \ + K(String0, "string0", 0) \ + K(String1, "string1", 0) \ + K(String2, "string2", 0) \ + K(String3, "string3", 0) \ + K(String4, "string4", 0) \ + K(String5, "string5", 0) \ + K(String6, "string6", 0) \ + K(String7, "string7", 0) \ + K(String8, "string8", 0) \ + K(String9, "string9", 0) \ + K(String10, "string10", 0) \ + K(String11, "string11", 0) \ + K(String12, "string12", 0) \ + K(String13, "string13", 0) \ + K(String14, "string14", 0) \ + K(String15, "string15", 0) \ + K(String16, "string16", 0) \ + K(String17, "string17", 0) \ + K(String18, "string18", 0) \ + K(String19, "string19", 0) \ + K(String20, "string20", 0) \ + K(String21, "string21", 0) \ + K(String22, "string22", 0) \ + K(String23, "string23", 0) \ + K(String24, "string24", 0) \ + K(String25, "string25", 0) \ + K(String26, "string26", 0) \ + K(String27, "string27", 0) \ + K(String28, "string28", 0) \ + K(String29, "string29", 0) \ + K(String30, "string30", 0) \ + K(String31, "string31", 0) \ + K(String32, "string32", 0) \ + K(Text, "text", 0) \ + K(Real, "real", 0) \ + K(UReal, "ureal", 0) \ + T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ \ /* Literals */ \ - K(NULL_LITERAL, "null", 0) \ - K(TRUE_LITERAL, "true", 0) \ - K(FALSE_LITERAL, "false", 0) \ - T(NUMBER, NULL, 0) \ - T(STRING_LITERAL, NULL, 0) \ - T(COMMENT_LITERAL, NULL, 0) \ + K(NullLiteral, "null", 0) \ + K(TrueLiteral, "true", 0) \ + K(FalseLiteral, "false", 0) \ + T(Number, NULL, 0) \ + T(StringLiteral, NULL, 0) \ + T(CommentLiteral, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ - T(IDENTIFIER, NULL, 0) \ + T(Identifier, NULL, 0) \ \ /* Illegal token - not able to scan. */ \ - T(ILLEGAL, "ILLEGAL", 0) \ + T(Illegal, "ILLEGAL", 0) \ \ /* Scanner-internal use only. */ \ - T(WHITESPACE, NULL, 0) + T(Whitespace, NULL, 0) class Token @@ -359,25 +359,26 @@ public: } // Predicates - static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } - static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } - static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } - static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND || - op == ADD || op == MUL || op == EQ || op == NE; } - static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; } - static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } + static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } + static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } + static bool isBinaryOp(Value op) { return Comma <= op && op <= Mod; } + static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || + op == Add || op == Mul || op == Equal || op == NotEqual; } + static bool isArithmeticOp(Value op) { return Add <= op && op <= Mod; } + static bool isCompareOp(Value op) { return Equal <= op && op <= In; } static Value AssignmentToBinaryOp(Value op) { - solAssert(isAssignmentOp(op) && op != ASSIGN, ""); - return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); + solAssert(isAssignmentOp(op) && op != Assign, ""); + return Token::Value(op + (BitOr - AssignBitOr)); } - static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } - static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; } - static bool isCountOp(Value op) { return op == INC || op == DEC; } + static bool isBitOp(Value op) { return (BitOr <= op && op <= SHR) || op == BitNot; } + static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } + static bool isCountOp(Value op) { return op == Inc || op == Dec; } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } - static bool isVisibilitySpecifier(Value op) { return op == PUBLIC || op == PRIVATE || op == PROTECTED; } + static bool isVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Protected; } + static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 54e701c10..6fd9e8b4c 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -37,24 +37,24 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) + if (Token::Int <= _typeToken && _typeToken <= Token::Hash256) { - int offset = _typeToken - Token::INT; + int offset = _typeToken - Token::Int; int bytes = offset % 33; if (bytes == 0) bytes = 32; int modifier = offset / 33; return make_shared(bytes * 8, - modifier == 0 ? IntegerType::Modifier::SIGNED : - modifier == 1 ? IntegerType::Modifier::UNSIGNED : - IntegerType::Modifier::HASH); + modifier == 0 ? IntegerType::Modifier::Signed : + modifier == 1 ? IntegerType::Modifier::Unsigned : + IntegerType::Modifier::Hash); } - else if (_typeToken == Token::ADDRESS) - return make_shared(0, IntegerType::Modifier::ADDRESS); - else if (_typeToken == Token::BOOL) + else if (_typeToken == Token::Address) + return make_shared(0, IntegerType::Modifier::Address); + else if (_typeToken == Token::Bool) return make_shared(); - else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32) - return make_shared(int(_typeToken) - int(Token::STRING0)); + else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) + return make_shared(int(_typeToken) - int(Token::String0)); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -87,12 +87,12 @@ shared_ptr Type::forLiteral(Literal const& _literal) { switch (_literal.getToken()) { - case Token::TRUE_LITERAL: - case Token::FALSE_LITERAL: + case Token::TrueLiteral: + case Token::FalseLiteral: return make_shared(); - case Token::NUMBER: - return IntegerConstantType::fromLiteral(_literal.getValue()); - case Token::STRING_LITERAL: + case Token::Number: + return make_shared(_literal); + case Token::StringLiteral: //@todo put larger strings into dynamic strings return StaticStringType::smallestTypeForLiteral(_literal.getValue()); default: @@ -140,31 +140,31 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::STRING) + if (_convertTo.getCategory() == Category::String) { StaticStringType const& convertTo = dynamic_cast(_convertTo); return isHash() && (m_bits == convertTo.getNumBytes() * 8); } - return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT; + return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const { // "delete" is ok for all integer types - if (_operator == Token::DELETE) + if (_operator == Token::Delete) return make_shared(); // no further unary operators for addresses else if (isAddress()) return TypePointer(); // "~" is ok for all other types - else if (_operator == Token::BIT_NOT) + else if (_operator == Token::BitNot) return shared_from_this(); // nothing else for hashes else if (isHash()) return TypePointer(); // for non-hash integers, we allow +, -, ++ and -- - else if (_operator == Token::ADD || _operator == Token::SUB || - _operator == Token::INC || _operator == Token::DEC) + else if (_operator == Token::Add || _operator == Token::Sub || + _operator == Token::Inc || _operator == Token::Dec) return shared_from_this(); else return TypePointer(); @@ -188,7 +188,7 @@ string IntegerType::toString() const TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->getCategory() != Category::INTEGER_CONSTANT && _other->getCategory() != getCategory()) + if (_other->getCategory() != Category::IntegerConstant && _other->getCategory() != getCategory()) return TypePointer(); auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); @@ -210,15 +210,28 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe const MemberList IntegerType::AddressMemberList = MemberList({{"balance", make_shared(256)}, - {"callstring32", make_shared(strings{"string32"}, strings{}, - FunctionType::Location::BARE)}, - {"callstring32string32", make_shared(strings{"string32", "string32"}, - strings{}, FunctionType::Location::BARE)}, - {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::SEND)}}); + {"call", make_shared(strings(), strings(), FunctionType::Location::Bare, true)}, + {"send", make_shared(strings{"uint"}, strings{}, FunctionType::Location::Send)}}); -shared_ptr IntegerConstantType::fromLiteral(string const& _literal) +IntegerConstantType::IntegerConstantType(Literal const& _literal) { - return make_shared(bigint(_literal)); + m_value = bigint(_literal.getValue()); + + switch (_literal.getSubDenomination()) + { + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::None: + break; + case Literal::SubDenomination::Szabo: + m_value *= bigint("1000000000000"); + break; + case Literal::SubDenomination::Finney: + m_value *= bigint("1000000000000000"); + break; + case Literal::SubDenomination::Ether: + m_value *= bigint("1000000000000000000"); + break; + } } bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -238,13 +251,13 @@ TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) con bigint value; switch (_operator) { - case Token::BIT_NOT: + case Token::BitNot: value = ~m_value; break; - case Token::ADD: + case Token::Add: value = m_value; break; - case Token::SUB: + case Token::Sub: value = -m_value; break; default: @@ -255,7 +268,7 @@ TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) con TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { - if (_other->getCategory() == Category::INTEGER) + if (_other->getCategory() == Category::Integer) { shared_ptr integerType = getIntegerType(); if (!integerType) @@ -279,30 +292,30 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty bigint value; switch (_operator) { - case Token::BIT_OR: + case Token::BitOr: value = m_value | other.m_value; break; - case Token::BIT_XOR: + case Token::BitXor: value = m_value ^ other.m_value; break; - case Token::BIT_AND: + case Token::BitAnd: value = m_value & other.m_value; break; - case Token::ADD: + case Token::Add: value = m_value + other.m_value; break; - case Token::SUB: + case Token::Sub: value = m_value - other.m_value; break; - case Token::MUL: + case Token::Mul: value = m_value * other.m_value; break; - case Token::DIV: + case Token::Div: if (other.m_value == 0) return TypePointer(); value = m_value / other.m_value; break; - case Token::MOD: + case Token::Mod: if (other.m_value == 0) return TypePointer(); value = m_value % other.m_value; @@ -328,13 +341,24 @@ string IntegerConstantType::toString() const u256 IntegerConstantType::literalValue(Literal const*) const { + u256 value; // we ignore the literal and hope that the type was correctly determined solAssert(m_value <= u256(-1), "Integer constant too large."); solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + if (m_value >= 0) - return u256(m_value); + value = u256(m_value); else - return s2u(s256(m_value)); + value = s2u(s256(m_value)); + + return value; +} + +TypePointer IntegerConstantType::getRealType() const +{ + auto intType = getIntegerType(); + solAssert(!!intType, "getRealType called with invalid integer constant " + toString()); + return intType; } shared_ptr IntegerConstantType::getIntegerType() const @@ -347,8 +371,8 @@ shared_ptr IntegerConstantType::getIntegerType() const return shared_ptr(); else return make_shared(max(bytesRequired(value), 1u) * 8, - negative ? IntegerType::Modifier::SIGNED - : IntegerType::Modifier::UNSIGNED); + negative ? IntegerType::Modifier::Signed + : IntegerType::Modifier::Unsigned); } shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) @@ -374,13 +398,16 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::INTEGER) + if (_convertTo.getCategory() == getCategory()) + return true; + if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits())) return true; } - return isImplicitlyConvertibleTo(_convertTo); + + return false; } bool StaticStringType::operator==(Type const& _other) const @@ -416,9 +443,9 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const u256 BoolType::literalValue(Literal const* _literal) const { solAssert(_literal, ""); - if (_literal->getToken() == Token::TRUE_LITERAL) + if (_literal->getToken() == Token::TrueLiteral) return u256(1); - else if (_literal->getToken() == Token::FALSE_LITERAL) + else if (_literal->getToken() == Token::FalseLiteral) return u256(0); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); @@ -426,16 +453,16 @@ u256 BoolType::literalValue(Literal const* _literal) const TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const { - if (_operator == Token::DELETE) + if (_operator == Token::Delete) return make_shared(); - return (_operator == Token::NOT) ? shared_from_this() : TypePointer(); + return (_operator == Token::Not) ? shared_from_this() : TypePointer(); } TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { if (getCategory() != _other->getCategory()) return TypePointer(); - if (Token::isCompareOp(_operator) || _operator == Token::AND || _operator == Token::OR) + if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or) return _other; else return TypePointer(); @@ -445,9 +472,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (*this == _convertTo) return true; - if (_convertTo.getCategory() == Category::INTEGER) + if (_convertTo.getCategory() == Category::Integer) return dynamic_cast(_convertTo).isAddress(); - if (_convertTo.getCategory() == Category::CONTRACT) + if (_convertTo.getCategory() == Category::Contract) { auto const& bases = getContractDefinition().getLinearizedBaseContracts(); if (m_super && bases.size() <= 1) @@ -460,13 +487,13 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER || - _convertTo.getCategory() == Category::CONTRACT; + return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::Integer || + _convertTo.getCategory() == Category::Contract; } TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const { - return _operator == Token::DELETE ? make_shared() : TypePointer(); + return _operator == Token::Delete ? make_shared() : TypePointer(); } bool ContractType::operator==(Type const& _other) const @@ -530,7 +557,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const TypePointer StructType::unaryOperatorResult(Token::Value _operator) const { - return _operator == Token::DELETE ? make_shared() : TypePointer(); + return _operator == Token::Delete ? make_shared() : TypePointer(); } bool StructType::operator==(Type const& _other) const @@ -589,7 +616,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const } FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): - m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL), + m_location(_isInternal ? Location::Internal : Location::External), m_isConstant(_function.isDeclaredConst()), m_declaration(&_function) { @@ -619,26 +646,35 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) + m_location(Location::External), m_isConstant(true), m_declaration(&_varDecl) { TypePointers params; vector paramNames; - TypePointers retParams; - vector retParamNames; - TypePointer varDeclType = _varDecl.getType(); - auto mappingType = dynamic_cast(varDeclType.get()); - auto returnType = varDeclType; + auto returnType = _varDecl.getType(); - while (mappingType != nullptr) + while (auto mappingType = dynamic_cast(returnType.get())) { params.push_back(mappingType->getKeyType()); paramNames.push_back(""); returnType = mappingType->getValueType(); - mappingType = dynamic_cast(mappingType->getValueType().get()); } - retParams.push_back(returnType); - retParamNames.push_back(""); + TypePointers retParams; + vector retParamNames; + if (auto structType = dynamic_cast(returnType.get())) + { + for (pair const& member: structType->getMembers()) + if (member.second->canLiveOutsideStorage()) + { + retParamNames.push_back(member.first); + retParams.push_back(member.second); + } + } + else + { + retParams.push_back(returnType); + retParamNames.push_back(""); + } swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); @@ -647,7 +683,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): } FunctionType::FunctionType(const EventDefinition& _event): - m_location(Location::EVENT), m_declaration(&_event) + m_location(Location::Event), m_declaration(&_event) { TypePointers params; vector paramNames; @@ -704,9 +740,9 @@ string FunctionType::toString() const unsigned FunctionType::getSizeOnStack() const { unsigned size = 0; - if (m_location == Location::EXTERNAL) + if (m_location == Location::External) size = 2; - else if (m_location == Location::INTERNAL || m_location == Location::BARE) + else if (m_location == Location::Internal || m_location == Location::Bare) size = 1; if (m_gasSet) size++; @@ -719,22 +755,22 @@ MemberList const& FunctionType::getMembers() const { switch (m_location) { - case Location::EXTERNAL: - case Location::CREATION: - case Location::ECRECOVER: + case Location::External: + case Location::Creation: + case Location::ECRecover: case Location::SHA256: case Location::RIPEMD160: - case Location::BARE: + case Location::Bare: if (!m_members) { map members{ {"gas", make_shared(parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(true, false)}, - Location::SET_GAS, m_gasSet, m_valueSet)}, + Location::SetGas, false, m_gasSet, m_valueSet)}, {"value", make_shared(parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(false, true)}, - Location::SET_VALUE, m_gasSet, m_valueSet)}}; - if (m_location == Location::CREATION) + Location::SetValue, false, m_gasSet, m_valueSet)}}; + if (m_location == Location::Creation) members.erase("gas"); m_members.reset(new MemberList(members)); } @@ -772,6 +808,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const { return make_shared(m_parameterTypes, m_returnParameterTypes, m_location, + m_arbitraryParameters, m_gasSet || _setGas, m_valueSet || _setValue); } @@ -829,7 +866,7 @@ MemberList const& TypeType::getMembers() const if (!m_members) { map members; - if (m_actualType->getCategory() == Category::CONTRACT && m_currentContract != nullptr) + if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr) { ContractDefinition const& contract = dynamic_cast(*m_actualType).getContractDefinition(); vector currentBases = m_currentContract->getLinearizedBaseContracts(); @@ -883,21 +920,21 @@ MagicType::MagicType(MagicType::Kind _kind): { switch (m_kind) { - case Kind::BLOCK: - m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::ADDRESS)}, + case Kind::Block: + m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"hash"}, FunctionType::Location::BLOCKHASH)}, + {"blockhash", make_shared(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)}}); break; - case Kind::MSG: - m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::ADDRESS)}, + case Kind::Message: + m_members = MemberList({{"sender", make_shared(0, IntegerType::Modifier::Address)}, {"gas", make_shared(256)}, {"value", make_shared(256)}}); break; - case Kind::TX: - m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::ADDRESS)}, + case Kind::Transaction: + m_members = MemberList({{"origin", make_shared(0, IntegerType::Modifier::Address)}, {"gasprice", make_shared(256)}}); break; default: @@ -917,11 +954,11 @@ string MagicType::toString() const { switch (m_kind) { - case Kind::BLOCK: + case Kind::Block: return "block"; - case Kind::MSG: + case Kind::Message: return "msg"; - case Kind::TX: + case Kind::Transaction: return "tx"; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 1f4d27a25..05090c7a2 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -76,7 +76,9 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this fromLiteral(std::string const& _literal); + virtual Category getCategory() const override { return Category::IntegerConstant; } + explicit IntegerConstantType(Literal const& _literal); explicit IntegerConstantType(bigint _value): m_value(_value) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -214,6 +217,7 @@ public: virtual std::string toString() const override; virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer getRealType() const override; /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr getIntegerType() const; @@ -228,7 +232,7 @@ private: class StaticStringType: public Type { public: - virtual Category getCategory() const override { return Category::STRING; } + virtual Category getCategory() const override { return Category::String; } /// @returns the smallest string type for the given literal or an empty pointer /// if no type fits. @@ -259,7 +263,7 @@ class BoolType: public Type { public: BoolType() {} - virtual Category getCategory() const { return Category::BOOL; } + virtual Category getCategory() const { return Category::Bool; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -277,7 +281,7 @@ public: class ContractType: public Type { public: - virtual Category getCategory() const override { return Category::CONTRACT; } + virtual Category getCategory() const override { return Category::Contract; } explicit ContractType(ContractDefinition const& _contract, bool _super = false): m_contract(_contract), m_super(_super) {} /// Contracts can be implicitly converted to super classes and to addresses. @@ -319,7 +323,7 @@ private: class StructType: public Type { public: - virtual Category getCategory() const override { return Category::STRUCT; } + virtual Category getCategory() const override { return Category::Struct; } explicit StructType(StructDefinition const& _struct): m_struct(_struct) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; @@ -351,26 +355,27 @@ public: /// BARE: contract address (non-abi contract call) /// OTHERS: special virtual function, nothing on the stack /// @todo This documentation is outdated, and Location should rather be named "Type" - enum class Location { INTERNAL, EXTERNAL, CREATION, SEND, - SHA3, SUICIDE, - ECRECOVER, SHA256, RIPEMD160, - LOG0, LOG1, LOG2, LOG3, LOG4, EVENT, - SET_GAS, SET_VALUE, BLOCKHASH, - BARE }; - - virtual Category getCategory() const override { return Category::FUNCTION; } + enum class Location { Internal, External, Creation, Send, + SHA3, Suicide, + ECRecover, SHA256, RIPEMD160, + Log0, Log1, Log2, Log3, Log4, Event, + SetGas, SetValue, BlockHash, + Bare }; + + virtual Category getCategory() const override { return Category::Function; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); explicit FunctionType(EventDefinition const& _event); FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, - Location _location = Location::INTERNAL): + Location _location = Location::Internal, bool _arbitraryParameters = false): FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), - _location) {} + _location, _arbitraryParameters) {} FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, - Location _location = Location::INTERNAL, - bool _gasSet = false, bool _valueSet = false): + Location _location = Location::Internal, + bool _arbitraryParameters = false, bool _gasSet = false, bool _valueSet = false): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), - m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} + m_location(_location), + m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet) {} TypePointers const& getParameterTypes() const { return m_parameterTypes; } std::vector const& getParameterNames() const { return m_parameterNames; } @@ -403,6 +408,9 @@ public: /// Can contain a nullptr in which case indicates absence of documentation ASTPointer getDocumentation() const; + /// true iff arguments are to be padded to multiples of 32 bytes for external calls + bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); } + bool takesArbitraryParameters() const { return m_arbitraryParameters; } bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -418,6 +426,8 @@ private: std::vector m_parameterNames; std::vector m_returnParameterNames; Location const m_location; + /// true iff the function takes an arbitrary number of arguments of arbitrary types + bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool m_isConstant; @@ -431,7 +441,7 @@ private: class MappingType: public Type { public: - virtual Category getCategory() const override { return Category::MAPPING; } + virtual Category getCategory() const override { return Category::Mapping; } MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} @@ -454,7 +464,7 @@ private: class VoidType: public Type { public: - virtual Category getCategory() const override { return Category::VOID; } + virtual Category getCategory() const override { return Category::Void; } VoidType() {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } @@ -472,7 +482,7 @@ public: class TypeType: public Type { public: - virtual Category getCategory() const override { return Category::TYPE; } + virtual Category getCategory() const override { return Category::TypeType; } explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): m_actualType(_actualType), m_currentContract(_currentContract) {} TypePointer const& getActualType() const { return m_actualType; } @@ -501,7 +511,7 @@ private: class ModifierType: public Type { public: - virtual Category getCategory() const override { return Category::MODIFIER; } + virtual Category getCategory() const override { return Category::Modifier; } explicit ModifierType(ModifierDefinition const& _modifier); virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } @@ -524,8 +534,8 @@ private: class MagicType: public Type { public: - enum class Kind { BLOCK, MSG, TX }; - virtual Category getCategory() const override { return Category::MAGIC; } + enum class Kind { Block, Message, Transaction }; + virtual Category getCategory() const override { return Category::Magic; } explicit MagicType(Kind _kind); diff --git a/libsolidity/Utils.h b/libsolidity/Utils.h old mode 100755 new mode 100644 diff --git a/libweb3jsonrpc/CMakeLists.txt b/libweb3jsonrpc/CMakeLists.txt index f371024e6..e18a53cd8 100644 --- a/libweb3jsonrpc/CMakeLists.txt +++ b/libweb3jsonrpc/CMakeLists.txt @@ -32,6 +32,17 @@ target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} serpent) +if (ETH_JSON_RPC_STUB) + add_custom_target(jsonrpcstub) + add_custom_command(TARGET jsonrpcstub + POST_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${ETH_JSON_RPC_STUB} spec.json --cpp-server=AbstractWebThreeStubServer --cpp-client=WebThreeStubClient + COMMAND cmake -E rename webthreestubclient.h ../test/webthreestubclient.h + ) + add_dependencies(${EXECUTABLE} jsonrpcstub) +endif() + install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libweb3jsonrpc/WebThreeStubServer.h b/libweb3jsonrpc/WebThreeStubServer.h index d401c38fc..106842981 100644 --- a/libweb3jsonrpc/WebThreeStubServer.h +++ b/libweb3jsonrpc/WebThreeStubServer.h @@ -44,13 +44,13 @@ public: WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector const& _accounts); private: - dev::eth::Interface* client() override; - std::shared_ptr face() override; - dev::WebThreeNetworkFace* network() override; - dev::WebThreeStubDatabaseFace* db() override; + virtual dev::eth::Interface* client() override; + virtual std::shared_ptr face() override; + virtual dev::WebThreeNetworkFace* network() override; + virtual dev::WebThreeStubDatabaseFace* db() override; - std::string get(std::string const& _name, std::string const& _key) override; - void put(std::string const& _name, std::string const& _key, std::string const& _value) override; + virtual std::string get(std::string const& _name, std::string const& _key) override; + virtual void put(std::string const& _name, std::string const& _key, std::string const& _value) override; private: dev::WebThreeDirect& m_web3; diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index bcaa31036..7ad29ed1b 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -31,7 +31,9 @@ #include #include #include +#ifndef _MSC_VER #include +#endif #include "WebThreeStubServerBase.h" using namespace std; @@ -426,6 +428,20 @@ int WebThreeStubServerBase::eth_newFilterString(std::string const& _filter) return ret; } +Json::Value WebThreeStubServerBase::eth_getWork() +{ + Json::Value ret(Json::arrayValue); + auto r = client()->getWork(); + ret.append(toJS(r.first)); + ret.append(toJS(r.second)); + return ret; +} + +bool WebThreeStubServerBase::eth_submitWork(std::string const& _nonce) +{ + return client()->submitNonce(jsToFixed<32>(_nonce)); +} + std::string WebThreeStubServerBase::shh_newGroup(std::string const& _id, std::string const& _who) { (void)_id; @@ -446,7 +462,9 @@ Json::Value WebThreeStubServerBase::eth_compilers() Json::Value ret(Json::arrayValue); ret.append("lll"); ret.append("solidity"); +#ifndef _MSC_VER ret.append("serpent"); +#endif return ret; } @@ -462,6 +480,7 @@ std::string WebThreeStubServerBase::eth_lll(std::string const& _code) std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) { string res; +#ifndef _MSC_VER try { res = toJS(dev::asBytes(::compile(_code))); @@ -474,6 +493,7 @@ std::string WebThreeStubServerBase::eth_serpent(std::string const& _code) { cwarn << "Uncought serpent compilation exception"; } +#endif return res; } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.h b/libweb3jsonrpc/WebThreeStubServerBase.h index ffb9e1738..1190ce407 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.h +++ b/libweb3jsonrpc/WebThreeStubServerBase.h @@ -103,6 +103,9 @@ public: virtual Json::Value eth_uncleByNumber(int const& _number, int const& _i); virtual bool eth_uninstallFilter(int const& _id); + virtual Json::Value eth_getWork(); + virtual bool eth_submitWork(std::string const& _nonce); + virtual std::string db_get(std::string const& _name, std::string const& _key); virtual std::string db_getString(std::string const& _name, std::string const& _key); virtual bool db_put(std::string const& _name, std::string const& _key, std::string const& _value); diff --git a/libweb3jsonrpc/abstractwebthreestubserver.h b/libweb3jsonrpc/abstractwebthreestubserver.h index 0c25f9842..dda8def08 100644 --- a/libweb3jsonrpc/abstractwebthreestubserver.h +++ b/libweb3jsonrpc/abstractwebthreestubserver.h @@ -49,6 +49,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServerbindAndAddMethod(new jsonrpc::Procedure("eth_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_changedI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_filterLogs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::eth_filterLogsI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_logs", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_OBJECT, NULL), &AbstractWebThreeStubServer::eth_logsI); + this->bindAndAddMethod(new jsonrpc::Procedure("eth_getWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, NULL), &AbstractWebThreeStubServer::eth_getWorkI); + this->bindAndAddMethod(new jsonrpc::Procedure("eth_submitWork", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_submitWorkI); this->bindAndAddMethod(new jsonrpc::Procedure("db_put", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putI); this->bindAndAddMethod(new jsonrpc::Procedure("db_get", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_getI); this->bindAndAddMethod(new jsonrpc::Procedure("db_putString", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING,"param3",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::db_putStringI); @@ -211,6 +213,14 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServereth_logs(request[0u]); } + inline virtual void eth_getWorkI(const Json::Value &request, Json::Value &response) + { + response = this->eth_getWork(); + } + inline virtual void eth_submitWorkI(const Json::Value &request, Json::Value &response) + { + response = this->eth_submitWork(request[0u].asString()); + } inline virtual void db_putI(const Json::Value &request, Json::Value &response) { response = this->db_put(request[0u].asString(), request[1u].asString(), request[2u].asString()); @@ -296,6 +306,8 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType("HttpServer", 1, 0, "HttpServer"); m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); - QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); + QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); window->setIcon(QIcon(":/res/mix_256x256x32.png")); appLoaded(); } diff --git a/mix/AssemblyDebuggerControl.cpp b/mix/AssemblyDebuggerControl.cpp deleted file mode 100644 index 7bf177981..000000000 --- a/mix/AssemblyDebuggerControl.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file AssemblyDebuggerControl.cpp - * @author Yann yann@ethdev.com - * @date 2014 - * display opcode debugging. - */ - -#include -#include -#include -#include "ClientModel.h" -#include "AssemblyDebuggerControl.h" - -using namespace dev::mix; - -AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): - Extension(_context, ExtensionDisplayBehavior::RightView) -{ -} - -QString AssemblyDebuggerControl::contentUrl() const -{ - return QStringLiteral("qrc:/qml/Debugger.qml"); -} - -QString AssemblyDebuggerControl::title() const -{ - return QApplication::tr("Debugger"); -} - -void AssemblyDebuggerControl::start() const -{ -} diff --git a/mix/AssemblyDebuggerControl.h b/mix/AssemblyDebuggerControl.h deleted file mode 100644 index 701cbc5fd..000000000 --- a/mix/AssemblyDebuggerControl.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file AssemblyDebuggerControl.h - * @author Yann yann@ethdev.com - * @date 2014 - * Extension which display debugging steps in assembly code. - */ - -#pragma once - -#include -#include "Extension.h" - -namespace dev -{ -namespace mix -{ - -class AppContext; - -/** - * @brief Extension which display transaction creation or transaction call debugging. - */ -class AssemblyDebuggerControl: public Extension -{ - Q_OBJECT - -public: - AssemblyDebuggerControl(AppContext* _context); - ~AssemblyDebuggerControl() {} - void start() const override; - QString title() const override; - QString contentUrl() const override; -}; - -} -} diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index d5574667a..db9452061 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -46,7 +46,9 @@ target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} evmcore) diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 3a2b50999..10a60811f 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -82,7 +82,7 @@ ClientModel::ClientModel(AppContext* _context): qRegisterMetaType("QInstruction"); qRegisterMetaType("QCode"); qRegisterMetaType("QCallData"); - qRegisterMetaType("TransactionLogEntry"); + qRegisterMetaType("RecordLogEntry"); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); m_client.reset(new MixClient(QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString())); @@ -113,24 +113,27 @@ QString ClientModel::apiCall(QString const& _message) void ClientModel::mine() { - if (m_running) + if (m_running || m_mining) BOOST_THROW_EXCEPTION(ExecutionStateException()); - m_running = true; - emit runStarted(); - emit runStateChanged(); + m_mining = true; + emit miningStarted(); + emit miningStateChanged(); QtConcurrent::run([=]() { try { m_client->mine(); newBlock(); + m_mining = false; + emit miningComplete(); } catch (...) { + m_mining = false; std::cerr << boost::current_exception_diagnostic_information(); emit runFailed(QString::fromStdString(boost::current_exception_diagnostic_information())); } - m_running = false; + emit miningStateChanged(); }); } @@ -233,8 +236,8 @@ void ClientModel::executeSequence(std::vector const& _seque } if (!f) BOOST_THROW_EXCEPTION(FunctionNotFoundException() << FunctionName(transaction.functionId.toStdString())); - - encoder.encode(f); + if (!transaction.functionId.isEmpty()) + encoder.encode(f); for (int p = 0; p < transaction.parameterValues.size(); p++) { if (f->parametersList().at(p)->type() != transaction.parameterValues.at(p)->declaration()->type()) @@ -244,6 +247,8 @@ void ClientModel::executeSequence(std::vector const& _seque if (transaction.functionId.isEmpty()) { + bytes param = encoder.encodedData(); + contractCode.insert(contractCode.end(), param.begin(), param.end()); Address newAddress = deployContract(contractCode, transaction); if (newAddress != m_contractAddress) { @@ -308,10 +313,10 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) } -void ClientModel::debugTransaction(unsigned _block, unsigned _index) +void ClientModel::debugRecord(unsigned _index) { - auto const& t = m_client->execution(_block, _index); - showDebuggerForTransaction(t); + ExecutionResult const& e = m_client->executions().at(_index); + showDebuggerForTransaction(e); } void ClientModel::showDebugError(QString const& _error) @@ -341,9 +346,10 @@ void ClientModel::onStateReset() void ClientModel::onNewTransaction() { - unsigned block = m_client->number() + 1; - unsigned index = m_client->pendingExecutions().size() - 1; ExecutionResult const& tr = m_client->lastExecution(); + unsigned block = m_client->number() + 1; + unsigned recordIndex = m_client->executions().size() - 1; + QString transactionIndex = tr.isCall() ? QObject::tr("Call") : QString("%1:%2").arg(block).arg(tr.transactionIndex); QString address = QString::fromStdString(toJS(tr.address)); QString value = QString::fromStdString(dev::toString(tr.value)); QString contract = address; @@ -354,7 +360,7 @@ void ClientModel::onNewTransaction() //TODO: handle value transfer FixedHash<4> functionHash; - bool call = false; + bool abi = false; if (creation) { //contract creation @@ -369,12 +375,12 @@ void ClientModel::onNewTransaction() } else { - //call + //transaction/call if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4) { functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer); function = QString::fromStdString(toJS(functionHash)); - call = true; + abi = true; } else function = QObject::tr(""); @@ -388,7 +394,7 @@ void ClientModel::onNewTransaction() auto compilerRes = m_context->codeModel()->code(); QContractDefinition* def = compilerRes->contract(); contract = def->name(); - if (call) + if (abi) { QFunctionDefinition* funcDef = def->getFunction(functionHash); if (funcDef) @@ -402,9 +408,9 @@ void ClientModel::onNewTransaction() } } - TransactionLogEntry* log = new TransactionLogEntry(block, index, contract, function, value, address, returned); + RecordLogEntry* log = new RecordLogEntry(recordIndex, transactionIndex, contract, function, value, address, returned, tr.isCall()); QQmlEngine::setObjectOwnership(log, QQmlEngine::JavaScriptOwnership); - emit newTransaction(log); + emit newRecord(log); } } diff --git a/mix/ClientModel.h b/mix/ClientModel.h index 493290780..530dc50cf 100644 --- a/mix/ClientModel.h +++ b/mix/ClientModel.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "MachineStates.h" namespace dev @@ -68,13 +69,13 @@ struct TransactionSettings /// UI Transaction log record -class TransactionLogEntry: public QObject +class RecordLogEntry: public QObject { Q_OBJECT - /// Transaction block number - Q_PROPERTY(unsigned block MEMBER m_block CONSTANT) - /// Transaction index within the block - Q_PROPERTY(unsigned tindex MEMBER m_index CONSTANT) + /// Recording index + Q_PROPERTY(unsigned recordIndex MEMBER m_recordIndex CONSTANT) + /// Human readable transaction bloack and transaction index + Q_PROPERTY(QString transactionIndex MEMBER m_transactionIndex CONSTANT) /// Contract name if any Q_PROPERTY(QString contract MEMBER m_contract CONSTANT) /// Function name if any @@ -85,21 +86,25 @@ class TransactionLogEntry: public QObject Q_PROPERTY(QString address MEMBER m_address CONSTANT) /// Returned value or transaction address in case of creation Q_PROPERTY(QString returned MEMBER m_returned CONSTANT) + /// true if call, false if transaction + Q_PROPERTY(bool call MEMBER m_call CONSTANT) + public: - TransactionLogEntry(): - m_block(0), m_index(0) {} - TransactionLogEntry(int _block, int _index, QString _contract, QString _function, QString _value, QString _address, QString _returned): - m_block(_block), m_index(_index), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned) {} + RecordLogEntry(): + m_recordIndex(0), m_call(false) {} + RecordLogEntry(unsigned _recordIndex, QString _transactionIndex, QString _contract, QString _function, QString _value, QString _address, QString _returned, bool _call): + m_recordIndex(_recordIndex), m_transactionIndex(_transactionIndex), m_contract(_contract), m_function(_function), m_value(_value), m_address(_address), m_returned(_returned), m_call(_call) {} private: - unsigned m_block; - unsigned m_index; + unsigned m_recordIndex; + QString m_transactionIndex; QString m_contract; QString m_function; QString m_value; QString m_address; QString m_returned; + bool m_call; }; /** @@ -114,6 +119,8 @@ public: ~ClientModel(); /// @returns true if currently executing contract code Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged) + /// @returns true if currently mining + Q_PROPERTY(bool mining MEMBER m_mining NOTIFY miningStateChanged) /// @returns address of the last executed contract Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged) /// ethereum.js RPC request entry point @@ -130,8 +137,8 @@ public slots: /// Setup state, run transaction sequence, show debugger for the last transaction /// @param _state JS object with state configuration void setupState(QVariantMap _state); - /// Show the debugger for a specified transaction - Q_INVOKABLE void debugTransaction(unsigned _block, unsigned _index); + /// Show the debugger for a specified record + Q_INVOKABLE void debugRecord(unsigned _index); private slots: /// Update UI with machine states result. Display a modal dialog. @@ -144,6 +151,12 @@ signals: void runStarted(); /// Transaction execution completed successfully void runComplete(); + /// Mining has started + void miningStarted(); + /// Mined a new block + void miningComplete(); + /// Mining stopped or started + void miningStateChanged(); /// Transaction execution completed with error /// @param _message Error message void runFailed(QString const& _message); @@ -159,7 +172,7 @@ signals: /// @param _message RPC response in Json format void apiResponse(QString const& _message); /// New transaction log entry - void newTransaction(TransactionLogEntry* _tr); + void newRecord(RecordLogEntry* _r); /// State (transaction log) cleared void stateCleared(); @@ -174,6 +187,7 @@ private: AppContext* m_context; std::atomic m_running; + std::atomic m_mining; std::unique_ptr m_client; std::unique_ptr m_rpcConnector; std::unique_ptr m_web3Server; diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index c7528486a..97b808eb2 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -26,8 +26,6 @@ #include #include #include "StatusPane.h" -#include "AssemblyDebuggerControl.h" -#include "StateListView.h" #include "AppContext.h" #include "MixApplication.h" #include "CodeModel.h" @@ -56,13 +54,9 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) void CodeEditorExtensionManager::initExtensions() { std::shared_ptr output = std::make_shared(m_appContext); - std::shared_ptr debug = std::make_shared(m_appContext); - std::shared_ptr stateList = std::make_shared(m_appContext); QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); initExtension(output); - initExtension(debug); - initExtension(stateList); } void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) @@ -94,10 +88,10 @@ void CodeEditorExtensionManager::applyCodeHighlight() void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView) { m_rightView = _rightView; - initExtensions(); //TODO: move this to a proper place } void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) { m_headerView = _headerView; + initExtensions(); //TODO: move this to a proper place } diff --git a/mix/CodeHighlighter.cpp b/mix/CodeHighlighter.cpp index c1ef39d5d..d76d8b73e 100644 --- a/mix/CodeHighlighter.cpp +++ b/mix/CodeHighlighter.cpp @@ -76,14 +76,14 @@ void CodeHighlighter::processSource(std::string const& _source) solidity::Token::Value token = scanner.getCurrentToken(); while (token != Token::EOS) { - if ((token >= Token::BREAK && token < Token::TYPES_END) || - token == Token::IN || token == Token::DELETE || token == Token::NULL_LITERAL || token == Token::TRUE_LITERAL || token == Token::FALSE_LITERAL) + if ((token >= Token::Break && token < Token::TypesEnd) || + token == Token::In || token == Token::Delete || token == Token::NullLiteral || token == Token::TrueLiteral || token == Token::FalseLiteral) m_formats.push_back(FormatRange(CodeHighlighterSettings::Keyword, scanner.getCurrentLocation())); - else if (token == Token::STRING_LITERAL) + else if (token == Token::StringLiteral) m_formats.push_back(FormatRange(CodeHighlighterSettings::StringLiteral, scanner.getCurrentLocation())); - else if (token == Token::COMMENT_LITERAL) + else if (token == Token::CommentLiteral) m_formats.push_back(FormatRange(CodeHighlighterSettings::Comment, scanner.getCurrentLocation())); - else if (token == Token::NUMBER) + else if (token == Token::Number) m_formats.push_back(FormatRange(CodeHighlighterSettings::NumLiteral, scanner.getCurrentLocation())); token = scanner.next(); diff --git a/mix/CodeHighlighter.h b/mix/CodeHighlighter.h index 5083e6c82..3bdff5647 100644 --- a/mix/CodeHighlighter.h +++ b/mix/CodeHighlighter.h @@ -81,7 +81,7 @@ public: int start; int length; }; - typedef std::vector Formats; // Sorted by start position + using Formats = std::vector; // Sorted by start position public: /// Collect highligting information by lexing the source diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 29b43bc11..19ece2a4d 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -51,6 +51,8 @@ void ContractCallDataEncoder::push(bytes const& _b) QList ContractCallDataEncoder::decode(QList const& _returnParameters, bytes _value) { + bytesConstRef value(&_value); + bytes rawParam(32); QList r; for (int k = 0; k <_returnParameters.length(); k++) { @@ -69,11 +71,10 @@ QList ContractCallDataEncoder::decode(QListdecodeValue(rawParam); r.push_back(def); - if (_value.size() > 32) - _value = bytes(_value.begin() + 32, _value.end()); + value = value.cropped(32); qDebug() << "decoded return value : " << dec->type() << " " << def->value(); } return r; diff --git a/mix/Exceptions.h b/mix/Exceptions.h index 5403879f1..46178bf96 100644 --- a/mix/Exceptions.h +++ b/mix/Exceptions.h @@ -40,10 +40,10 @@ struct FunctionNotFoundException: virtual Exception {}; struct ExecutionStateException: virtual Exception {}; struct ParameterChangedException: virtual Exception {}; -typedef boost::error_info QmlErrorInfo; -typedef boost::error_info FileError; -typedef boost::error_info BlockIndex; -typedef boost::error_info FunctionName; +using QmlErrorInfo = boost::error_info; +using FileError = boost::error_info; +using BlockIndex = boost::error_info; +using FunctionName = boost::error_info; } } diff --git a/mix/MachineStates.h b/mix/MachineStates.h index d19fd9b74..9506dcf63 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -61,7 +61,7 @@ namespace mix */ struct ExecutionResult { - ExecutionResult() : receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {} + ExecutionResult(): transactionIndex(std::numeric_limits::max()) {} std::vector machineStates; std::vector transactionData; @@ -71,9 +71,11 @@ namespace mix dev::Address sender; dev::Address contractAddress; dev::u256 value; - dev::eth::TransactionReceipt receipt; + unsigned transactionIndex; + + bool isCall() const { return transactionIndex == std::numeric_limits::max(); } }; using ExecutionResults = std::vector; } -} \ No newline at end of file +} diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 2db5ecf5b..b2a367929 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -15,7 +15,6 @@ along with cpp-ethereum. If not, see . */ /** @file MixClient.cpp - * @author Yann yann@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com * @date 2015 * Ethereum IDE client. @@ -27,6 +26,7 @@ #include #include #include +#include #include #include "Exceptions.h" @@ -34,15 +34,37 @@ using namespace dev; using namespace dev::eth; -using namespace dev::mix; + +namespace dev +{ +namespace mix +{ const Secret c_userAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); +const u256 c_mixGenesisDifficulty = (u256) 1 << 4; + +class MixBlockChain: public dev::eth::BlockChain +{ +public: + MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true) + { + } + + static bytes createGenesisBlock(h256 _stateRoot) + { + RLPStream block(3); + block.appendList(14) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_mixGenesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << std::string() << sha3(bytes(1, 42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); + } +}; MixClient::MixClient(std::string const& _dbPath): - m_userAccount(c_userAccountSecret), m_bc(_dbPath, true), m_dbPath(_dbPath), m_minigThreads(0) + m_userAccount(c_userAccountSecret), m_dbPath(_dbPath), m_minigThreads(0) { - //TODO: put this into genesis block somehow - //resetState(10000000 * ether); + resetState(10000000 * ether); } MixClient::~MixClient() @@ -51,32 +73,34 @@ MixClient::~MixClient() void MixClient::resetState(u256 _balance) { - (void) _balance; - { - WriteGuard l(x_state); - Guard fl(m_filterLock); - m_filters.clear(); - m_watches.clear(); - m_bc.reopen(m_dbPath, true); - m_state = eth::State(); - m_stateDB = OverlayDB(); - m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::CanonGenesis); - m_state.sync(m_bc); - m_startState = m_state; - m_pendingExecutions.clear(); - } - mine(); + WriteGuard l(x_state); + Guard fl(m_filterLock); + m_filters.clear(); + m_watches.clear(); + + m_stateDB = OverlayDB(); + TrieDB accountState(&m_stateDB); + accountState.init(); + std::map genesisState = { std::make_pair(KeyPair(c_userAccountSecret).address(), Account(_balance, Account::NormalCreation)) }; + dev::eth::commit(genesisState, static_cast(m_stateDB), accountState); + h256 stateRoot = accountState.root(); + m_bc.reset(); + m_bc.reset(new MixBlockChain(m_dbPath, stateRoot)); + m_state = eth::State(m_userAccount.address(), m_stateDB, BaseState::Empty); + m_state.sync(bc()); + m_startState = m_state; + m_executions.clear(); } -void MixClient::executeTransaction(Transaction const& _t, State& _state) +void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _call) { bytes rlp = _t.rlp(); // do debugging run first LastHashes lastHashes(256); - lastHashes[0] = m_bc.numberHash(m_bc.number()); + lastHashes[0] = bc().numberHash(bc().number()); for (unsigned i = 1; i < 256; ++i) - lastHashes[i] = lastHashes[i - 1] ? m_bc.details(lastHashes[i - 1]).parent : h256(); + lastHashes[i] = lastHashes[i - 1] ? bc().details(lastHashes[i - 1]).parent : h256(); State execState = _state; Executive execution(execState, lastHashes, 0); @@ -145,64 +169,56 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state) d.value = _t.value(); if (_t.isCreation()) d.contractAddress = right160(sha3(rlpList(_t.sender(), _t.nonce()))); - d.receipt = TransactionReceipt(execState.rootHash(), execution.gasUsed(), execution.logs()); //TODO: track gas usage - m_pendingExecutions.emplace_back(std::move(d)); + if (!_call) + d.transactionIndex = m_state.pending().size(); + m_executions.emplace_back(std::move(d)); // execute on a state - _state.execute(lastHashes, rlp, nullptr, true); - - // collect watches - h256Set changed; - Guard l(m_filterLock); - for (std::pair& i: m_filters) - if ((unsigned)i.second.filter.latest() > m_bc.number()) - { - // acceptable number. - auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); - if (m.size()) + if (!_call) + { + _state.execute(lastHashes, rlp, nullptr, true); + // collect watches + h256Set changed; + Guard l(m_filterLock); + for (std::pair& i: m_filters) + if ((unsigned)i.second.filter.latest() > bc().number()) { - // filter catches them - for (LogEntry const& l: m) - i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1)); - changed.insert(i.first); + // acceptable number. + auto m = i.second.filter.matches(_state.receipt(_state.pending().size() - 1)); + if (m.size()) + { + // filter catches them + for (LogEntry const& l: m) + i.second.changes.push_back(LocalisedLogEntry(l, bc().number() + 1)); + changed.insert(i.first); + } } - } - changed.insert(dev::eth::PendingChangedFilter); - noteChanged(changed); + changed.insert(dev::eth::PendingChangedFilter); + noteChanged(changed); + } } void MixClient::mine() { WriteGuard l(x_state); - m_state.commitToMine(m_bc); + m_state.commitToMine(bc()); while (!m_state.mine(100, true).completed) {} m_state.completeMine(); - m_bc.import(m_state.blockData(), m_stateDB); - m_state.sync(m_bc); - //m_state.cleanup(true); + bc().import(m_state.blockData(), m_stateDB); + m_state.sync(bc()); m_startState = m_state; - m_executions.emplace_back(std::move(m_pendingExecutions)); h256Set changed { dev::eth::PendingChangedFilter, dev::eth::ChainChangedFilter }; noteChanged(changed); } -ExecutionResult const& MixClient::execution(unsigned _block, unsigned _transaction) const -{ - if (_block == m_bc.number() + 1) - return m_pendingExecutions.at(_transaction); - return m_executions.at(_block).at(_transaction); -} - ExecutionResult const& MixClient::lastExecution() const { - if (m_pendingExecutions.size() > 0) - return m_pendingExecutions.back(); - return m_executions.back().back(); + return m_executions.back(); } -ExecutionResults const& MixClient::pendingExecutions() const +ExecutionResults const& MixClient::executions() const { - return m_pendingExecutions; + return m_executions; } State MixClient::asOf(int _block) const @@ -213,25 +229,23 @@ State MixClient::asOf(int _block) const else if (_block == -1) return m_startState; else - return State(m_stateDB, m_bc, m_bc.numberHash(_block)); + return State(m_stateDB, bc(), bc().numberHash(_block)); } void MixClient::transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); - _gasPrice = 0; //TODO: remove after fixing setBalance Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - executeTransaction(t, m_state); + executeTransaction(t, m_state, false); } Address MixClient::transact(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) { WriteGuard l(x_state); u256 n = m_state.transactionsFrom(toAddress(_secret)); - _gasPrice = 0; //TODO: remove after fixing setBalance eth::Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - executeTransaction(t, m_state); + executeTransaction(t, m_state, false); Address address = right160(sha3(rlpList(t.sender(), t.nonce()))); return address; } @@ -240,7 +254,7 @@ void MixClient::inject(bytesConstRef _rlp) { WriteGuard l(x_state); eth::Transaction t(_rlp, CheckSignature::None); - executeTransaction(t, m_state); + executeTransaction(t, m_state, false); } void MixClient::flushTransactions() @@ -259,8 +273,8 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _ Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state - executeTransaction(t, temp); - return m_pendingExecutions.back().returnValue; + executeTransaction(t, temp, true); + return lastExecution().returnValue; } u256 MixClient::balanceAt(Address _a, int _block) const @@ -301,12 +315,12 @@ eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const { LocalisedLogEntries ret; - unsigned lastBlock = m_bc.number(); + unsigned lastBlock = bc().number(); unsigned block = std::min(lastBlock, (unsigned)_f.latest()); unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); unsigned skip = _f.skip(); // Pending transactions - if (block > m_bc.number()) + if (block > bc().number()) { ReadGuard l(x_state); for (unsigned i = 0; i < m_state.pending().size(); ++i) @@ -318,15 +332,15 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); skip -= std::min(skip, static_cast(logEntries.size())); } - block = m_bc.number(); + block = bc().number(); } // The rest - auto h = m_bc.numberHash(block); + auto h = bc().numberHash(block); for (; ret.size() != block && block != end; block--) { - if (_f.matches(m_bc.info(h).logBloom)) - for (TransactionReceipt receipt: m_bc.receipts(h).receipts) + if (_f.matches(bc().info(h).logBloom)) + for (TransactionReceipt receipt: bc().receipts(h).receipts) if (_f.matches(receipt.bloom())) { LogEntries logEntries = _f.matches(receipt); @@ -334,7 +348,7 @@ eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); skip -= std::min(skip, static_cast(logEntries.size())); } - h = m_bc.details(h).parent; + h = bc().details(h).parent; } return ret; } @@ -416,22 +430,22 @@ LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) h256 MixClient::hashFromNumber(unsigned _number) const { - return m_bc.numberHash(_number); + return bc().numberHash(_number); } eth::BlockInfo MixClient::blockInfo(h256 _hash) const { - return BlockInfo(m_bc.block(_hash)); + return BlockInfo(bc().block(_hash)); } eth::BlockDetails MixClient::blockDetails(h256 _hash) const { - return m_bc.details(_hash); + return bc().details(_hash); } eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const { - auto bl = m_bc.block(_blockHash); + auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[1].itemCount()) return Transaction(b[1][_i].data(), CheckSignature::Range); @@ -441,7 +455,7 @@ eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const { - auto bl = m_bc.block(_blockHash); + auto bl = bc().block(_blockHash); RLP b(bl); if (_i < b[2].itemCount()) return BlockInfo::fromHeader(b[2][_i].data()); @@ -451,7 +465,7 @@ eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const unsigned MixClient::number() const { - return m_bc.number(); + return bc().number(); } eth::Transactions MixClient::pending() const @@ -461,7 +475,7 @@ eth::Transactions MixClient::pending() const eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const { - State st(m_stateDB, m_bc, _block); + State st(m_stateDB, bc(), _block); return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); } @@ -526,3 +540,6 @@ eth::MineProgress MixClient::miningProgress() const { return eth::MineProgress(); } + +} +} diff --git a/mix/MixClient.h b/mix/MixClient.h index bd1002527..9ec5ff659 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -27,7 +27,6 @@ #include #include #include -#include #include "MachineStates.h" namespace dev @@ -35,6 +34,8 @@ namespace dev namespace mix { +class MixBlockChain; + class MixClient: public dev::eth::Interface { public: @@ -44,9 +45,8 @@ public: void resetState(u256 _balance); KeyPair const& userAccount() const { return m_userAccount; } void mine(); - ExecutionResult const& execution(unsigned _block, unsigned _transaction) const; ExecutionResult const& lastExecution() const; - ExecutionResults const& pendingExecutions() const; + ExecutionResults const& executions() const; //dev::eth::Interface void transact(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; @@ -85,23 +85,26 @@ public: void stopMining() override; bool isMining() override; eth::MineProgress miningProgress() const override; + std::pair getWork() override { return std::pair(); } + bool submitNonce(h256 const&) override { return false; } private: - void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state); + void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); void noteChanged(h256Set const& _filters); dev::eth::State asOf(int _block) const; + MixBlockChain& bc() { return *m_bc; } + MixBlockChain const& bc() const { return *m_bc; } KeyPair m_userAccount; eth::State m_state; eth::State m_startState; OverlayDB m_stateDB; - eth::CanonBlockChain m_bc; + std::auto_ptr m_bc; mutable boost::shared_mutex x_state; mutable std::mutex m_filterLock; std::map m_filters; std::map m_watches; - std::vector m_executions; - ExecutionResults m_pendingExecutions; + ExecutionResults m_executions; std::string m_dbPath; unsigned m_minigThreads; }; diff --git a/mix/StateListView.cpp b/mix/StateListView.cpp deleted file mode 100644 index 835c332aa..000000000 --- a/mix/StateListView.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file StateListView.cpp - * @author Arkadiy Paronyan arkadiy@ethdev.com - * @date 2014 - * Ethereum IDE client. - */ - -#include -#include -#include -#include -#include -#include "StateListView.h" - -using namespace dev::mix; - -StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightView) -{ -} - -QString StateListView::contentUrl() const -{ - return QStringLiteral("qrc:/qml/StateList.qml"); -} - -QString StateListView::title() const -{ - return QApplication::tr("States"); -} - -void StateListView::start() const -{ -} diff --git a/mix/StateListView.h b/mix/StateListView.h deleted file mode 100644 index 18d1ae879..000000000 --- a/mix/StateListView.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** @file StateListView.h - * @author Arkadiy Paronyan arkadiy@ethdev.com - * @date 2014 - * Ethereum IDE client. - */ - -#pragma once - -#include -#include -#include "Extension.h" - -namespace dev -{ -namespace mix -{ - -/// State list control -class StateListView: public Extension -{ - Q_OBJECT - -public: - StateListView(AppContext* _context); - void start() const override; - QString title() const override; - QString contentUrl() const override; -}; - -} - -} diff --git a/mix/qml/CallStack.qml b/mix/qml/CallStack.qml deleted file mode 100644 index 218c8c02e..000000000 --- a/mix/qml/CallStack.qml +++ /dev/null @@ -1,31 +0,0 @@ -import QtQuick 2.2 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Controls 1.1 -import QtQuick.Layouts 1.1 - -Item { - property alias model: callTable.model - signal frameActivated(int index) - ColumnLayout { - anchors.fill: parent - Text { - text: qsTr("Call Stack") - Layout.fillWidth: true - } - TableView { - id: callTable - Layout.fillWidth: true - Layout.fillHeight: true - headerDelegate: null - - TableViewColumn { - role: "modelData" - title: qsTr("Address") - width: parent.width - } - onActivated: { - frameActivated(row); - } - } - } -} diff --git a/mix/qml/DebugInfoList.qml b/mix/qml/DebugInfoList.qml index cad2a4c9e..c1ab11596 100644 --- a/mix/qml/DebugInfoList.qml +++ b/mix/qml/DebugInfoList.qml @@ -4,14 +4,19 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Styles 1.1 ColumnLayout { + id: root property string title property variant listModel; property bool collapsible; + property bool enableSelection; + property real storedHeight: 0; property Component itemDelegate + signal rowActivated(int index) spacing: 0 function collapse() { + storedHeight = childrenRect.height; storageContainer.state = "collapsed"; } @@ -32,7 +37,9 @@ ColumnLayout { Image { source: "qrc:/qml/img/opentriangleindicator.png" width: 15 + height: 15 sourceSize.width: 15 + sourceSize.height: 15 id: storageImgArrow } @@ -53,16 +60,20 @@ ColumnLayout { if (storageContainer.state == "collapsed") { storageContainer.state = ""; - storageContainer.parent.parent.height = storageContainer.parent.parent.Layout.maximumHeight; + storageContainer.parent.parent.height = storedHeight; } else + { + storedHeight = root.childrenRect.height; storageContainer.state = "collapsed"; + } } } } } Rectangle { + id: storageContainer border.width: 3 border.color: "#deddd9" Layout.fillWidth: true @@ -80,18 +91,35 @@ ColumnLayout { } } ] - id: storageContainer - ListView { + TableView { clip: true; + alternatingRowColors: false anchors.top: parent.top anchors.left: parent.left anchors.topMargin: 3 anchors.leftMargin: 3 width: parent.width - 3 height: parent.height - 6 - id: storageList model: listModel - delegate: itemDelegate + selectionMode: enableSelection ? SelectionMode.SingleSelection : SelectionMode.NoSelection + headerDelegate: null + itemDelegate: root.itemDelegate + onHeightChanged: { + if (height <= 0 && collapsible) { + if (storedHeight <= 0) + storedHeight = 200; + storageContainer.state = "collapsed"; + } + else if (height > 0 && storageContainer.state == "collapsed") { + //TODO: fix increasing size + //storageContainer.state = ""; + } + } + onActivated: rowActivated(row); + TableViewColumn { + role: "modelData" + width: parent.width + } } } } diff --git a/mix/qml/Debugger.qml b/mix/qml/Debugger.qml index 502f3242e..e9c718f70 100644 --- a/mix/qml/Debugger.qml +++ b/mix/qml/Debugger.qml @@ -9,8 +9,10 @@ import "js/ErrorLocationFormater.js" as ErrorLocationFormater Rectangle { id: debugPanel + + property alias transactionLog : transactionLog + objectName: "debugPanel" - anchors.fill: parent; color: "#ededed" clip: true @@ -22,7 +24,7 @@ Rectangle { function update(data, giveFocus) { - if (statusPane.result.successful) + if (statusPane && statusPane.result.successful) { Debugger.init(data); debugScrollArea.visible = true; @@ -55,6 +57,16 @@ Rectangle { onCompilationComplete: update(null, false); } + Settings { + id: splitSettings + property alias transactionLogHeight: transactionLog.height + property alias callStackHeight: callStackRect.height + property alias storageHeightSettings: storageRect.height + property alias memoryDumpHeightSettings: memoryRect.height + property alias callDataHeightSettings: callDataRect.height + property alias transactionLogVisible: transactionLog.visible + } + Rectangle { visible: false; @@ -104,49 +116,56 @@ Rectangle { } } - Flickable { - property int firstColumnWidth: 180 - property int secondColumnWidth: 250 + SplitView { id: debugScrollArea - flickableDirection: Flickable.VerticalFlick anchors.fill: parent - contentHeight: 4000 - contentWidth: parent.width - Rectangle - { + orientation: Qt.Vertical + handleDelegate: Rectangle { + height: machineStates.sideMargin color: "transparent" - anchors.fill: parent - ColumnLayout - { - property int sideMargin: 10 - id: machineStates + } + + TransactionLog { + id: transactionLog + Layout.fillWidth: true + Layout.minimumHeight: 60 + height: 250 + } + ScrollView + { + property int sideMargin: 10 + id: machineStates + Layout.fillWidth: true + Layout.fillHeight: true + function updateHeight() { + statesLayout.height = buttonRow.childrenRect.height + assemblyCodeRow.childrenRect.height + + callStackRect.childrenRect.height + storageRect.childrenRect.height + memoryRect.childrenRect.height + callDataRect.childrenRect.height + 120; + } + + Component.onCompleted: updateHeight(); + + ColumnLayout { + id: statesLayout anchors.top: parent.top anchors.topMargin: 15 anchors.left: parent.left; anchors.leftMargin: machineStates.sideMargin - anchors.right: parent.right; - anchors.rightMargin: machineStates.sideMargin - anchors.fill: parent - Layout.fillWidth: true - Layout.fillHeight: true - - TransactionLog { - Layout.fillWidth: true - height: 250 - } + width: debugScrollArea.width - machineStates.sideMargin * 2 - 20; + spacing: machineStates.sideMargin - RowLayout { + Rectangle { // step button + slider id: buttonRow - spacing: machineStates.sideMargin height: 27 Layout.fillWidth: true + color: "transparent" - Rectangle - { - height: parent.height + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left color: "transparent" - width: debugScrollArea.firstColumnWidth + width: stateListContainer.width RowLayout { anchors.horizontalCenter: parent.horizontalCenter id: jumpButtons @@ -226,9 +245,11 @@ Rectangle { } Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + width: debugInfoContainer.width color: "transparent" - Layout.fillWidth: true - height: parent.height Slider { id: statesSlider anchors.fill: parent @@ -255,83 +276,99 @@ Rectangle { } } - RowLayout { + Rectangle { // Assembly code id: assemblyCodeRow Layout.fillWidth: true height: 405 implicitHeight: 405 - spacing: machineStates.sideMargin + color: "transparent" Rectangle { id: stateListContainer - width: debugScrollArea.firstColumnWidth + anchors.top : parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: parent.width * 0.4 height: parent.height border.width: 3 border.color: "#deddd9" color: "white" - anchors.top: parent.top - ListView { + TableView { + id: statesList anchors.fill: parent anchors.leftMargin: 3 anchors.rightMargin: 3 anchors.topMargin: 3 anchors.bottomMargin: 3 clip: true - id: statesList - delegate: renderDelegate - highlight: highlightBar - //highlightFollowsCurrentItem: false + headerDelegate: null + itemDelegate: renderDelegate model: ListModel {} + TableViewColumn { + role: "line" + width: parent.width - 10 + } + } Component { id: highlightBar Rectangle { radius: 4 - height: statesList.currentItem.height - width: statesList.currentItem.width; + anchors.fill: parent y: statesList.currentItem.y color: "#4A90E2" - //Behavior on y { - // PropertyAnimation { properties: "y"; easing.type: Easing.InOutQuad; duration: 50} - //} } } Component { id: renderDelegate - RowLayout { - id: wrapperItem - height: 20 - width: parent.width - spacing: 5 - Text { - anchors.left: parent.left - anchors.leftMargin: 10 - width: 15 - color: "#b2b3ae" - text: line.split(' ')[0] - font.family: "monospace" - font.pointSize: 9 - id: id - wrapMode: Text.NoWrap + Item { + Rectangle { + radius: 4 + anchors.fill: parent + color: "#4A90E2" + visible: styleData.selected; } - Text { - wrapMode: Text.NoWrap - color: parent.ListView.isCurrentItem ? "white" : "black" - font.family: "monospace" - text: line.replace(line.split(' ')[0], '') - anchors.left: id.right - font.pointSize: 9 + + RowLayout { + id: wrapperItem + anchors.fill: parent + spacing: 5 + + + Text { + anchors.left: parent.left + anchors.leftMargin: 10 + width: 15 + color: "#b2b3ae" + text: styleData.value.split(' ')[0] + font.family: "monospace" + font.pointSize: 9 + wrapMode: Text.NoWrap + id: id + } + Text { + anchors.left: id.right; + wrapMode: Text.NoWrap + color: styleData.selected ? "white" : "black" + font.family: "monospace" + text: styleData.value.replace(styleData.value.split(' ')[0], '') + font.pointSize: 9 + } } } } } Rectangle { - Layout.fillWidth: true + id: debugInfoContainer + width: parent.width * 0.6 - machineStates.sideMargin + anchors.top : parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right height: parent.height //- 2 * stateListContainer.border.width color: "transparent" ColumnLayout @@ -372,7 +409,7 @@ Rectangle { title : qsTr("Stack") itemDelegate: Item { id: renderedItem - height: 25 + //height: 25 width: parent.width RowLayout { @@ -391,7 +428,7 @@ Rectangle { anchors.leftMargin: 5 font.family: "monospace" color: "#4a4a4a" - text: model.index; + text: styleData.row; font.pointSize: 9 } } @@ -402,7 +439,6 @@ Rectangle { Layout.fillWidth: true Layout.minimumWidth: 15 Layout.preferredWidth: 15 - Layout.maximumWidth: 60 Layout.minimumHeight: parent.height Text { anchors.left: parent.left @@ -410,7 +446,7 @@ Rectangle { font.family: "monospace" anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" - text: modelData + text: styleData.value font.pointSize: 9 } } @@ -434,42 +470,95 @@ Rectangle { id: splitInfoList Layout.fillHeight: true Layout.fillWidth: true - - Settings { - id: splitSettings - property alias storageHeightSettings: storageRect.height - property alias memoryDumpHeightSettings: memoryRect.height - property alias callDataHeightSettings: callDataRect.height - } - orientation: Qt.Vertical - width: debugPanel.width - 2 * machineStates.sideMargin - Rectangle { id: callStackRect; color: "transparent" - height: 120 - width: parent.width - Layout.minimumHeight: 120 - Layout.maximumHeight: 400 - CallStack { - anchors.fill: parent + Layout.minimumHeight: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); + DebugInfoList + { id: callStack - onFrameActivated: Debugger.displayFrame(index); + collapsible: true + anchors.fill: parent + title : qsTr("Call Stack") + enableSelection: true + onRowActivated: Debugger.displayFrame(index); + itemDelegate: + Item { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "#4A90E2" + visible: styleData.selected; + } + + RowLayout + { + id: row + anchors.fill: parent + Rectangle + { + color: "#f7f7f7" + Layout.fillWidth: true + Layout.minimumWidth: 30 + Layout.maximumWidth: 30 + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + font.family: "monospace" + anchors.leftMargin: 5 + color: "#4a4a4a" + text: styleData.row; + font.pointSize: 9 + width: parent.width - 5 + elide: Text.ElideRight + } + } + Rectangle + { + color: "transparent" + Layout.fillWidth: true + Layout.minimumWidth: parent.width - 30 + Layout.maximumWidth: parent.width - 30 + Text { + anchors.leftMargin: 5 + width: parent.width - 5 + wrapMode: Text.Wrap + anchors.left: parent.left + font.family: "monospace" + anchors.verticalCenter: parent.verticalCenter + color: "#4a4a4a" + text: styleData.value; + elide: Text.ElideRight + font.pointSize: 9 + } + } + } + + Rectangle { + anchors.top: row.bottom + width: parent.width; + height: 1; + color: "#cccccc" + anchors.bottom: parent.bottom + } + } } } - Rectangle { id: storageRect color: "transparent" width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 - height: 25 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: storage @@ -478,29 +567,24 @@ Rectangle { title : qsTr("Storage") itemDelegate: Item { - height: 27 - width: parent.width; + anchors.fill: parent RowLayout { id: row - width: parent.width - height: 26 + anchors.fill: parent Rectangle { color: "#f7f7f7" Layout.fillWidth: true Layout.minimumWidth: parent.width / 2 - Layout.preferredWidth: parent.width / 2 Layout.maximumWidth: parent.width / 2 - Layout.minimumHeight: parent.height - Layout.maximumHeight: parent.height Text { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left font.family: "monospace" anchors.leftMargin: 5 color: "#4a4a4a" - text: modelData.split('\t')[0]; + text: styleData.value.split('\t')[0]; font.pointSize: 9 width: parent.width - 5 elide: Text.ElideRight @@ -511,10 +595,7 @@ Rectangle { color: "transparent" Layout.fillWidth: true Layout.minimumWidth: parent.width / 2 - Layout.preferredWidth: parent.width / 2 Layout.maximumWidth: parent.width / 2 - Layout.minimumHeight: parent.height - Layout.maximumHeight: parent.height Text { anchors.leftMargin: 5 width: parent.width - 5 @@ -523,7 +604,7 @@ Rectangle { font.family: "monospace" anchors.verticalCenter: parent.verticalCenter color: "#4a4a4a" - text: modelData.split('\t')[1]; + text: styleData.value.split('\t')[1]; elide: Text.ElideRight font.pointSize: 9 } @@ -545,10 +626,10 @@ Rectangle { { id: memoryRect; color: "transparent" - height: 25 width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: memoryDump anchors.fill: parent @@ -567,10 +648,10 @@ Rectangle { { id: callDataRect color: "transparent" - height: 25 width: parent.width Layout.minimumHeight: 25 - Layout.maximumHeight: 223 + Layout.maximumHeight: 800 + onHeightChanged: machineStates.updateHeight(); DebugInfoList { id: callDataDump anchors.fill: parent @@ -586,8 +667,9 @@ Rectangle { } Rectangle { + id: bottomRect; width: parent.width - Layout.minimumHeight: 25 + Layout.minimumHeight: 20 color: "transparent" } } diff --git a/mix/qml/FilesSection.qml b/mix/qml/FilesSection.qml new file mode 100644 index 000000000..32202f839 --- /dev/null +++ b/mix/qml/FilesSection.qml @@ -0,0 +1,250 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.3 +import "." + + +ColumnLayout { + id: wrapperItem + signal documentSelected(string doc, string groupName) + property alias model: filesList.model + property string sectionName; + property variant selManager; + Layout.fillWidth: true + Layout.minimumHeight: hiddenHeightTopLevel() + height: hiddenHeightTopLevel() + Layout.maximumHeight: hiddenHeightTopLevel() + spacing: 0 + + function hiddenHeightTopLevel() + { + return section.state === "hidden" ? Style.documentsList.height : Style.documentsList.fileNameHeight * model.count + Style.documentsList.height; + } + + function hiddenHeightRepeater() + { + return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight * wrapperItem.model.count; + } + + function hiddenHeightElement() + { + return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight; + } + + function getDocumentIndex(documentId) + { + for (var i = 0; i < model.count; i++) + if (model.get(i).documentId === documentId) + return i; + return -1; + } + + function removeDocument(documentId) + { + var i = getDocumentIndex(documentId); + if (i !== -1) + model.remove(i); + } + + FontLoader + { + id: fileNameFont + source: "qrc:/qml/fonts/SourceSansPro-Regular.ttf" + } + + FontLoader + { + id: boldFont + source: "qrc:/qml/fonts/SourceSansPro-Bold.ttf" + } + + RowLayout + { + anchors.top: parent.top + id: rowCol + width: parent.width + height: Style.documentsList.height + + Image { + source: "qrc:/qml/img/opentriangleindicator_filesproject.png" + width: 15 + sourceSize.width: 12 + id: imgArrow + anchors.right: section.left + anchors.rightMargin: 8 + anchors.top: parent.top + anchors.topMargin: 6 + } + + Text + { + id: section + text: sectionName + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + color: Style.documentsList.sectionColor + font.family: boldFont.name + font.pointSize: Style.documentsList.sectionFontSize + states: [ + State { + name: "hidden" + PropertyChanges { target: filesList; visible: false; } + PropertyChanges { target: rowCol; Layout.minimumHeight: Style.documentsList.height; Layout.maximumHeight: Style.documentsList.height; height: Style.documentsList.height; } + PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } + } + ] + } + + MouseArea { + id: titleMouseArea + anchors.fill: parent + hoverEnabled: true + z: 2 + onClicked: { + if (section.state === "hidden") + section.state = ""; + else + section.state = "hidden"; + } + } + } + + ColumnLayout { + height: wrapperItem.hiddenHeightRepeater() + Layout.minimumHeight: wrapperItem.hiddenHeightRepeater() + Layout.preferredHeight: wrapperItem.hiddenHeightRepeater() + Layout.maximumHeight: wrapperItem.hiddenHeightRepeater() + width: parent.width + visible: section.state !== "hidden" + spacing: 0 + Repeater + { + id: filesList + visible: section.state !== "hidden" + Rectangle + { + visible: section.state !== "hidden" + id: rootItem + Layout.fillWidth: true + Layout.minimumHeight: wrapperItem.hiddenHeightElement() + Layout.preferredHeight: wrapperItem.hiddenHeightElement() + Layout.maximumHeight: wrapperItem.hiddenHeightElement() + height: wrapperItem.hiddenHeightElement() + color: isSelected ? Style.documentsList.highlightColor : Style.documentsList.background + property bool isSelected + property bool renameMode + Text { + id: nameText + height: parent.height + visible: !renameMode + color: rootItem.isSelected ? Style.documentsList.selectedColor : Style.documentsList.color + text: name; + font.family: fileNameFont.name + font.pointSize: Style.documentsList.fontSize + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + 2 + width: parent.width + Connections + { + target: selManager + onSelected: { + if (groupName != sectionName) + rootItem.isSelected = false; + else if (doc === documentId) + rootItem.isSelected = true; + else + rootItem.isSelected = false; + + if (rootItem.isSelected && section.state === "hidden") + section.state = ""; + } + } + } + + TextInput { + id: textInput + text: nameText.text + visible: renameMode + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + MouseArea { + id: textMouseArea + anchors.fill: parent + hoverEnabled: true + z: 2 + onClicked: { + textInput.forceActiveFocus(); + } + } + + onVisibleChanged: { + if (visible) { + selectAll(); + forceActiveFocus(); + } + } + + onAccepted: close(true); + onCursorVisibleChanged: { + if (!cursorVisible) + close(false); + } + onFocusChanged: { + if (!focus) + close(false); + } + function close(accept) { + rootItem.renameMode = false; + if (accept) + { + var i = getDocumentIndex(documentId); + projectModel.renameDocument(documentId, textInput.text); + wrapperItem.model.set(i, projectModel.getDocument(documentId)); + } + } + } + + MouseArea { + id: mouseArea + z: 1 + hoverEnabled: false + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked:{ + if (mouse.button === Qt.RightButton && !isContract) + contextMenu.popup(); + else if (mouse.button === Qt.LeftButton) + { + rootItem.isSelected = true; + projectModel.openDocument(documentId); + documentSelected(documentId, groupName); + } + } + } + + Menu { + id: contextMenu + MenuItem { + text: qsTr("Rename") + onTriggered: { + rootItem.renameMode = true; + } + } + MenuItem { + text: qsTr("Delete") + onTriggered: { + projectModel.removeDocument(documentId); + wrapperItem.removeDocument(documentId); + } + } + } + } + } + } +} + diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index e6c636514..d984e2753 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -7,9 +7,9 @@ import Qt.labs.settings 1.0 import org.ethereum.qml.QEther 1.0 import "js/QEtherHelper.js" as QEtherHelper import "js/TransactionHelper.js" as TransactionHelper +import "." Rectangle { - objectName: "mainContent" signal keyPressed(variant event) focus: true @@ -21,11 +21,12 @@ Rectangle { anchors.fill: parent id: root - property alias rightViewVisible : rightView.visible - property alias webViewVisible : webPreview.visible - property alias projectViewVisible : projectList.visible - property alias runOnProjectLoad : mainSettings.runOnProjectLoad - property bool webViewHorizontal : codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally + property alias rightViewVisible: rightView.visible + property alias webViewVisible: webPreview.visible + property alias projectViewVisible: projectList.visible + property alias runOnProjectLoad: mainSettings.runOnProjectLoad + property alias rightPane: rightView + property bool webViewHorizontal: codeWebSplitter.orientation === Qt.Vertical //vertical splitter positions elements vertically, splits screen horizontally property bool firstCompile: true Connections { @@ -76,7 +77,6 @@ Rectangle { CodeEditorExtensionManager { headerView: headerPaneTabs; - rightView: rightPaneTabs; } Settings { @@ -121,6 +121,12 @@ Rectangle { } } + Rectangle{ + Layout.fillWidth: true + height: 1 + color: "#8c8c8c" + } + Rectangle { Layout.fillWidth: true Layout.preferredHeight: root.height - headerView.height; @@ -136,16 +142,16 @@ Rectangle { { anchors.fill: parent handleDelegate: Rectangle { - width: 4 - height: 4 - color: "#cccccc" + width: 1 + height: 1 + color: "#8c8c8c" } orientation: Qt.Horizontal ProjectList { id: projectList - width: 200 - Layout.minimumWidth: 180 + width: 350 + Layout.minimumWidth: 250 Layout.fillHeight: true } Rectangle { @@ -154,9 +160,9 @@ Rectangle { Layout.fillWidth: true SplitView { handleDelegate: Rectangle { - width: 4 - height: 4 - color: "#cccccc" + width: 1 + height: 1 + color: "#8c8c8c" } id: codeWebSplitter anchors.fill: parent @@ -178,46 +184,13 @@ Rectangle { } } - Rectangle { + Debugger { visible: false; id: rightView; Layout.fillHeight: true Keys.onEscapePressed: visible = false - height: parent.height; - width: 515 Layout.minimumWidth: 515 anchors.right: parent.right - Rectangle { - anchors.fill: parent; - id: rightPaneView - TabView { - id: rightPaneTabs - tabsVisible: true - antialiasing: true - anchors.fill: parent - style: TabViewStyle { - frameOverlap: 1 - tabBar: - Rectangle { - color: "#ededed" - id: background - } - tab: Rectangle { - color: "#ededed" - implicitWidth: 80 - implicitHeight: 20 - radius: 2 - Text { - anchors.centerIn: parent - text: styleData.title - color: styleData.selected ? "#7da4cd" : "#202020" - } - } - frame: Rectangle { - } - } - } - } } } } diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 30f945706..138e86fe9 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -2,138 +2,166 @@ import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.3 +import Qt.labs.settings 1.0 +import "." Item { property bool renameMode: false; ColumnLayout { anchors.fill: parent - Text { - Layout.fillWidth: true - color: "blue" - text: projectModel.projectTitle - horizontalAlignment: Text.AlignHCenter - visible: !projectModel.isEmpty; + id: filesCol + spacing: 0 + FontLoader + { + id: srcSansProLight + source: "qrc:/qml/fonts/SourceSansPro-Light.ttf" } - ListView { - id: projectList - Layout.fillWidth: true - Layout.fillHeight: true - model: projectModel.listModel + Rectangle + { + color: Style.title.background + height: Style.title.height + Layout.fillWidth: true + Image { + id: projectIcon + source: "qrc:/qml/img/projecticon.png" + sourceSize.height: 30 + anchors.right: projectTitle.left + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 6 + } - delegate: renderDelegate - highlight: Rectangle { - color: "lightsteelblue"; + Text + { + id: projectTitle + color: Style.title.color + text: projectModel.projectTitle + anchors.verticalCenter: parent.verticalCenter + visible: !projectModel.isEmpty; + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + font.family: srcSansProLight.name + font.pointSize: Style.title.pointSize + font.weight: Font.Light } - highlightFollowsCurrentItem: true - focus: true - clip: true - onCurrentIndexChanged: { - if (currentIndex >= 0 && currentIndex < projectModel.listModel.count) - projectModel.openDocument(projectModel.listModel.get(currentIndex).documentId); + Text + { + text: "-" + anchors.right: parent.right + anchors.rightMargin: 15 + font.family: srcSansProLight.name + font.pointSize: Style.title.pointSize + anchors.verticalCenter: parent.verticalCenter + font.weight: Font.Light } } - Menu { - id: contextMenu - MenuItem { - text: qsTr("Rename") - onTriggered: { - renameMode = true; - } - } - MenuItem { - text: qsTr("Delete") - onTriggered: { - projectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId); - } - } + + Rectangle + { + Layout.fillWidth: true + height: 10 + color: Style.documentsList.background } - } - Component { - id: renderDelegate - Item { - id: wrapperItem - height: 20 - width: parent.width - RowLayout { - anchors.fill: parent - visible: !(index === projectList.currentIndex) || !renameMode - Text { - id: nameText - Layout.fillWidth: true - Layout.fillHeight: true - text: name - font.pointSize: 12 - verticalAlignment: Text.AlignBottom - } - } - TextInput { - id: textInput - text: nameText.text - visible: (index === projectList.currentIndex) && renameMode - MouseArea { - id: textMouseArea - anchors.fill: parent - hoverEnabled: true - z:2 - onClicked: { - textInput.forceActiveFocus(); - } - } - onVisibleChanged: { - if (visible) { - selectAll(); - forceActiveFocus(); - } - } - onAccepted: close(true); - onCursorVisibleChanged: { - if (!cursorVisible) - close(false); - } - onFocusChanged: { - if (!focus) - close(false); - } - function close(accept) { - renameMode = false; - if (accept) - projectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text); - } - } - MouseArea { - id: mouseArea - z: 1 - hoverEnabled: false - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked:{ - projectList.currentIndex = index; - if (mouse.button === Qt.RightButton && !projectList.model.get(index).isContract) - contextMenu.popup(); - } - } - } - } - Connections { - target: projectModel - onProjectLoaded: { - projectList.currentIndex = 0; - if (projectList.currentIndex >= 0 && projectList.currentIndex < projectModel.listModel.count) - projectModel.openDocument(projectModel.listModel.get(projectList.currentIndex).documentId); + Rectangle + { + Layout.fillWidth: true + Layout.fillHeight: true + color: Style.documentsList.background - } - onProjectClosed: { - projectList.currentIndex = -1; - } - onDocumentOpened: { - if (projectList.currentItem.documentId !== document.documentId) - projectList.currentIndex = projectModel.getDocumentIndex(document.documentId); + ColumnLayout + { + anchors.top: parent.top + width: parent.width + spacing: 0 + + Repeater { + model: [qsTr("Contracts"), qsTr("Javascript"), qsTr("Web Pages"), qsTr("Styles"), qsTr("Images"), qsTr("Misc")]; + signal selected(string doc, string groupName) + id: sectionRepeater + FilesSection + { + sectionName: modelData + model: sectionModel + selManager: sectionRepeater + + onDocumentSelected: { + selManager.selected(doc, groupName); + } + + ListModel + { + id: sectionModel + } + Connections { + target: codeModel + onCompilationComplete: { + if (modelData === "Contracts") + { + var ctr = projectModel.listModel.get(0); + if (codeModel.code.contract.name !== ctr.name) + { + ctr.name = codeModel.code.contract.name; + projectModel.listModel.set(0, ctr); + sectionModel.set(0, ctr); + } + } + } + } + + Connections { + id: projectModelConnection + target: projectModel + + function addDocToSubModel() + { + for (var k = 0; k < projectModel.listModel.count; k++) + { + var item = projectModel.listModel.get(k); + if (item.groupName === modelData) + sectionModel.append(item); + } + } + + onDocumentOpened: { + if (document.groupName === modelData) + sectionRepeater.selected(document.documentId, modelData); + } + + onNewProject: { + sectionModel.clear(); + } + + onProjectLoaded: { + addDocToSubModel(); + if (modelData === "Contracts") + { + var selItem = projectModel.listModel.get(0); + projectModel.openDocument(selItem.documentId); + sectionRepeater.selected(selItem.documentId, modelData); + } + } + + onDocumentAdded: + { + var newDoc = projectModel.getDocument(documentId); + if (newDoc.groupName === modelData) + { + sectionModel.append(newDoc); + projectModel.openDocument(newDoc.documentId); + sectionRepeater.selected(newDoc.documentId, modelData); + } + + } + } + } + } + } } } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 49e63e184..42bf91d9d 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -41,6 +41,7 @@ Item { function addExistingFile() { ProjectModelCode.addExistingFile(); } function newHtmlFile() { ProjectModelCode.newHtmlFile(); } function newJsFile() { ProjectModelCode.newJsFile(); } + function newCssFile() { ProjectModelCode.newCssFile(); } //function newContract() { ProjectModelCode.newContract(); } function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } function openNextDocument() { ProjectModelCode.openNextDocument(); } diff --git a/mix/qml/StateList.qml b/mix/qml/StateList.qml index 3674e0dbc..059b35bc2 100644 --- a/mix/qml/StateList.qml +++ b/mix/qml/StateList.qml @@ -3,60 +3,66 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Controls 1.1 import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 +import QtQuick.Window 2.0 -Rectangle { - color: "#ededed" +Window { id: stateListContainer - focus: true - anchors.topMargin: 10 - anchors.left: parent.left - height: parent.height - width: parent.width + modality: Qt.WindowModal - ListView { - id: list - anchors.top: parent.top - height: parent.height - width: parent.width - model: projectModel.stateListModel - delegate: renderDelegate - } + width: 640 + height: 480 - Button { - anchors.bottom: parent.bottom - action: addStateAction + visible: false + ColumnLayout + { + anchors.fill: parent + TableView { + id: list + Layout.fillHeight: true + Layout.fillWidth: true + model: projectModel.stateListModel + itemDelegate: renderDelegate + headerDelegate: null + TableViewColumn { + role: "title" + title: qsTr("State") + width: list.width + } + } + + Button { + anchors.bottom: parent.bottom + action: addStateAction + } } Component { id: renderDelegate Item { - id: wrapperItem - height: 20 - width: parent.width RowLayout { anchors.fill: parent Text { Layout.fillWidth: true Layout.fillHeight: true - text: title + text: styleData.value font.pointSize: 12 verticalAlignment: Text.AlignBottom } ToolButton { text: qsTr("Edit"); Layout.fillHeight: true - onClicked: list.model.editState(index); + onClicked: list.model.editState(styleData.row); } ToolButton { - visible: list.model.count - 1 != index + visible: list.model.defaultStateIndex !== styleData.row text: qsTr("Delete"); Layout.fillHeight: true - onClicked: list.model.deleteState(index); + onClicked: list.model.deleteState(styleData.row); } ToolButton { text: qsTr("Run"); Layout.fillHeight: true - onClicked: list.model.runState(index); + onClicked: list.model.runState(styleData.row); } } } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index a1b24fe0f..e87a96f76 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -8,7 +8,6 @@ import "js/QEtherHelper.js" as QEtherHelper Item { - property int defaultStateIndex: 0 property alias model: stateListModel property var stateList: [] @@ -111,7 +110,7 @@ Item { for(var i = 0; i < stateListModel.count; i++) { projectData.states.push(toPlainStateItem(stateList[i])); } - projectData.defaultStateIndex = defaultStateIndex; + projectData.defaultStateIndex = stateListModel.defaultStateIndex; } onNewProject: { var state = toPlainStateItem(stateListModel.createDefaultState()); @@ -127,12 +126,12 @@ Item { var item = stateDialog.getItem(); if (stateDialog.stateIndex < stateListModel.count) { if (stateDialog.isDefault) - defaultStateIndex = stateIndex; + stateListModel.defaultStateIndex = stateIndex; stateList[stateDialog.stateIndex] = item; stateListModel.set(stateDialog.stateIndex, item); } else { if (stateDialog.isDefault) - defaultStateIndex = 0; + stateListModel.defaultStateIndex = 0; stateList.push(item); stateListModel.append(item); } @@ -149,8 +148,10 @@ Item { ListModel { id: stateListModel + property int defaultStateIndex: 0 signal defaultStateChanged; signal stateListModelReady; + signal stateRun(int index) function defaultTransactionItem() { return { @@ -205,6 +206,7 @@ Item { function runState(index) { var item = stateList[index]; clientModel.setupState(item); + stateRun(index); } function deleteState(index) { diff --git a/mix/qml/Style.qml b/mix/qml/Style.qml new file mode 100644 index 000000000..348d8c4d1 --- /dev/null +++ b/mix/qml/Style.qml @@ -0,0 +1,30 @@ +pragma Singleton +import QtQuick 2.0 + +/* + * Project Files + */ +QtObject { + property QtObject general: QtObject { + property int leftMargin: 45 + } + + property QtObject title: QtObject { + property string color: "#808080" + property string background: "#f0f0f0" + property int height: 55 + property int pointSize: 18 + } + + property QtObject documentsList: QtObject { + property string background: "#f7f7f7" + property string color: "#4d4d4d" + property string sectionColor: "#808080" + property string selectedColor: "white" + property string highlightColor: "#4a90e2" + property int height: 25 + property int fileNameHeight: 30 + property int fontSize: 13 + property int sectionFontSize: 13 + } +} diff --git a/mix/qml/TransactionDialog.qml b/mix/qml/TransactionDialog.qml index 4d50db7b1..3e6cf0236 100644 --- a/mix/qml/TransactionDialog.qml +++ b/mix/qml/TransactionDialog.qml @@ -60,15 +60,39 @@ Window { else { var parameters = codeModel.code.contract.constructor.parameters; - for (var p = 0; p < parameters.length; p++) { - var pname = parameters[p].name; - paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname].value() : "" }); - } + for (var p = 0; p < parameters.length; p++) + loadParameter(parameters[p]); } visible = true; valueField.focus = true; } + function loadParameter(parameter) + { + var type = parameter.type; + var pname = parameter.name; + var varComponent; + + if (type.indexOf("int") !== -1) + varComponent = Qt.createComponent("qrc:/qml/QIntType.qml"); + else if (type.indexOf("real") !== -1) + varComponent = Qt.createComponent("qrc:/qml/QRealType.qml"); + else if (type.indexOf("string") !== -1 || type.indexOf("text") !== -1) + varComponent = Qt.createComponent("qrc:/qml/QStringType.qml"); + else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1) + varComponent = Qt.createComponent("qrc:/qml/QHashType.qml"); + else if (type.indexOf("bool") !== -1) + varComponent = Qt.createComponent("qrc:/qml/QBoolType.qml"); + + var param = varComponent.createObject(modalTransactionDialog); + var value = itemParams[pname] !== undefined ? itemParams[pname] : ""; + + param.setValue(value); + param.setDeclaration(parameter); + qType.push({ name: pname, value: param }); + paramsModel.append({ name: pname, type: type, value: value }); + } + function loadParameters() { paramsModel.clear(); if (!paramsModel) @@ -76,30 +100,8 @@ Window { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { var func = codeModel.code.contract.functions[functionComboBox.currentIndex]; var parameters = func.parameters; - for (var p = 0; p < parameters.length; p++) { - var pname = parameters[p].name; - var varComponent; - var type = parameters[p].type; - - if (type.indexOf("int") !== -1) - varComponent = Qt.createComponent("qrc:/qml/QIntType.qml"); - else if (type.indexOf("real") !== -1) - varComponent = Qt.createComponent("qrc:/qml/QRealType.qml"); - else if (type.indexOf("string") !== -1 || type.indexOf("text") !== -1) - varComponent = Qt.createComponent("qrc:/qml/QStringType.qml"); - else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1) - varComponent = Qt.createComponent("qrc:/qml/QHashType.qml"); - else if (type.indexOf("bool") !== -1) - varComponent = Qt.createComponent("qrc:/qml/QBoolType.qml"); - - var param = varComponent.createObject(modalTransactionDialog); - var value = itemParams[pname] !== undefined ? itemParams[pname] : ""; - - param.setValue(value); - param.setDeclaration(parameters[p]); - qType.push({ name: pname, value: param }); - paramsModel.append({ name: pname, type: parameters[p].type, value: value }); - } + for (var p = 0; p < parameters.length; p++) + loadParameter(parameters[p]); } } diff --git a/mix/qml/TransactionLog.qml b/mix/qml/TransactionLog.qml index c8e87f025..b31956898 100644 --- a/mix/qml/TransactionLog.qml +++ b/mix/qml/TransactionLog.qml @@ -5,33 +5,93 @@ import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 Item { + + property bool showLogs: true + property ListModel fullModel: ListModel{} + property ListModel transactionModel: ListModel{} + onShowLogsChanged: { + logTable.model = showLogs ? fullModel : transactionModel + } + + Action { + id: addStateAction + text: "Add State" + shortcut: "Ctrl+Alt+T" + enabled: codeModel.hasContract && !clientModel.running; + onTriggered: projectModel.stateListModel.addState(); + } + Action { + id: editStateAction + text: "Edit State" + shortcut: "Ctrl+Alt+T" + enabled: codeModel.hasContract && !clientModel.running && statesCombo.currentIndex >= 0 && projectModel.stateListModel.count > 0; + onTriggered: projectModel.stateListModel.editState(statesCombo.currentIndex); + } + ColumnLayout { anchors.fill: parent - CheckBox { - id: recording - text: qsTr("Record transactions"); - checked: true - Layout.fillWidth: true + RowLayout { + + ComboBox { + id: statesCombo + model: projectModel.stateListModel + width: 150 + editable: false + textRole: "title" + onActivated: { + model.runState(index); + } + Connections { + target: projectModel.stateListModel + onStateRun: { + if (statesCombo.currentIndex !== index) + statesCombo.currentIndex = index; + } + } + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: editStateAction + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: addStateAction + } + Button + { + anchors.rightMargin: 9 + anchors.verticalCenter: parent.verticalCenter + action: mineAction + } + + CheckBox { + id: recording + text: qsTr("Record transactions"); + checked: true + Layout.fillWidth: true + + + } } TableView { + id: logTable Layout.fillWidth: true Layout.fillHeight: true - model: logModel + model: fullModel TableViewColumn { - role: "block" - title: qsTr("Block") - width: 40 - } - TableViewColumn { - role: "tindex" + role: "transactionIndex" title: qsTr("Index") width: 40 } TableViewColumn { role: "contract" title: qsTr("Contract") - width: 120 + width: 100 } TableViewColumn { role: "function" @@ -41,7 +101,7 @@ Item { TableViewColumn { role: "value" title: qsTr("Value") - width: 120 + width: 60 } TableViewColumn { role: "address" @@ -54,30 +114,31 @@ Item { width: 120 } onActivated: { - var item = logModel.get(row); - clientModel.debugTransaction(item.block, item.tindex); + var item = logTable.model.get(row); + clientModel.debugRecord(item.recordIndex); } Keys.onPressed: { - if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logModel.count) { - var item = logModel.get(currentRow); + if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { + var item = logTable.model.get(currentRow); appContext.toClipboard(item.returned); } } } } - ListModel { - id: logModel - } - Connections { target: clientModel onStateCleared: { - logModel.clear(); + fullModel.clear(); + transactionModel.clear(); } - onNewTransaction: { + onNewRecord: { if (recording.checked) - logModel.append(_tr); + { + fullModel.append(_r); + if (!_r.call) + transactionModel.append(_r); + } } } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index e95a85fde..f4ddca84e 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -114,7 +114,8 @@ Item { onClientConnected: { //filter polling spam //TODO: do it properly - var log = _request.content.indexOf("eth_changed") < 0; + //var log = _request.content.indexOf("eth_changed") < 0; + var log = true; if (log) console.log(_request.content); var response = clientModel.apiCall(_request.content); diff --git a/mix/qml/fonts/SourceSansPro-Black.ttf b/mix/qml/fonts/SourceSansPro-Black.ttf new file mode 100644 index 000000000..be1a3108e Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Black.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-BlackIt.ttf b/mix/qml/fonts/SourceSansPro-BlackIt.ttf new file mode 100644 index 000000000..ac5c4ef7c Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-BlackIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Bold.ttf b/mix/qml/fonts/SourceSansPro-Bold.ttf new file mode 100644 index 000000000..f47161c6b Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Bold.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-BoldIt.ttf b/mix/qml/fonts/SourceSansPro-BoldIt.ttf new file mode 100644 index 000000000..6b3db698b Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-BoldIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-ExtraLight.ttf b/mix/qml/fonts/SourceSansPro-ExtraLight.ttf new file mode 100644 index 000000000..0a3e51fdb Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-ExtraLight.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf b/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf new file mode 100644 index 000000000..a0eb86aca Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-It.ttf b/mix/qml/fonts/SourceSansPro-It.ttf new file mode 100644 index 000000000..fcc95fc7c Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-It.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Light.ttf b/mix/qml/fonts/SourceSansPro-Light.ttf new file mode 100644 index 000000000..9cae13c97 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Light.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-LightIt.ttf b/mix/qml/fonts/SourceSansPro-LightIt.ttf new file mode 100644 index 000000000..2ed784284 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-LightIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Regular.ttf b/mix/qml/fonts/SourceSansPro-Regular.ttf new file mode 100644 index 000000000..8e8255e17 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Regular.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Semibold.ttf b/mix/qml/fonts/SourceSansPro-Semibold.ttf new file mode 100644 index 000000000..121ee9bbf Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Semibold.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf b/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf new file mode 100644 index 000000000..6ceaa885f Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Bold.ttf b/mix/qml/fonts/SourceSerifPro-Bold.ttf new file mode 100644 index 000000000..ac7837fd9 Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Bold.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Regular.ttf b/mix/qml/fonts/SourceSerifPro-Regular.ttf new file mode 100644 index 000000000..7201a8890 Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Regular.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Semibold.ttf b/mix/qml/fonts/SourceSerifPro-Semibold.ttf new file mode 100644 index 000000000..db2fc804b Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Semibold.ttf differ diff --git a/mix/qml/img/closedtriangleindicator_filesproject.png b/mix/qml/img/closedtriangleindicator_filesproject.png new file mode 100644 index 000000000..840f500e4 Binary files /dev/null and b/mix/qml/img/closedtriangleindicator_filesproject.png differ diff --git a/mix/qml/img/opentriangleindicator_filesproject.png b/mix/qml/img/opentriangleindicator_filesproject.png new file mode 100644 index 000000000..ee351b05c Binary files /dev/null and b/mix/qml/img/opentriangleindicator_filesproject.png differ diff --git a/mix/qml/img/projecticon.png b/mix/qml/img/projecticon.png new file mode 100644 index 000000000..58b8b6f60 Binary files /dev/null and b/mix/qml/img/projecticon.png differ diff --git a/mix/qml/js/Debugger.js b/mix/qml/js/Debugger.js index de1f87256..872d0aa36 100644 --- a/mix/qml/js/Debugger.js +++ b/mix/qml/js/Debugger.js @@ -107,7 +107,7 @@ function select(stateIndex) callStackData.push(address); } callStackData.push(debugData.states[0].address); - callStack.model = callStackData; + callStack.listModel = callStackData; } function codeStr(stateIndex) @@ -118,8 +118,9 @@ function codeStr(stateIndex) function highlightSelection(index) { - statesList.currentIndex = index; - statesList.positionViewAtIndex(index, ListView.Center); + statesList.positionViewAtRow(index, ListView.Center); + statesList.selection.clear(); + statesList.selection.select(index); } function completeCtxInformation(state) diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 4d501ffca..ac44dffa9 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -88,7 +88,9 @@ function addFile(fileName) { var isHtml = extension === ".html"; var isCss = extension === ".css"; var isJs = extension === ".js"; + var isImg = extension === ".png" || extension === ".gif" || extension === ".jpg" || extension === ".svg"; var syntaxMode = isContract ? "solidity" : isJs ? "javascript" : isHtml ? "htmlmixed" : isCss ? "css" : ""; + var groupName = isContract ? qsTr("Contracts") : isJs ? qsTr("Javascript") : isHtml ? qsTr("Web Pages") : isCss ? qsTr("Styles") : isImg ? qsTr("Images") : qsTr("Misc"); var docData = { contract: false, path: p, @@ -99,6 +101,7 @@ function addFile(fileName) { isText: isContract || isHtml || isCss || isJs, isContract: isContract, isHtml: isHtml, + groupName: groupName }; projectListModel.append(docData); @@ -153,6 +156,7 @@ function doCloseProject() { console.log("closing project"); projectListModel.clear(); projectPath = ""; + currentDocumentId = ""; projectClosed(); } @@ -172,8 +176,8 @@ function doCreateProject(title, path) { files: [ contractsFile, indexFile ] }; //TODO: copy from template - fileIo.writeFile(dirPath + indexFile, "\n\n\n\n\n\n\n"); - fileIo.writeFile(dirPath + contractsFile, "contract Contract {\n}\n"); + fileIo.writeFile(dirPath + indexFile, "\n\n\n\n\n\n\n"); + fileIo.writeFile(dirPath + contractsFile, "contract Contract {\n}\n"); newProject(projectData); var json = JSON.stringify(projectData, null, "\t"); fileIo.writeFile(projectFile, json); @@ -224,6 +228,10 @@ function newHtmlFile() { createAndAddFile("page", "html", "\n"); } +function newCssFile() { + createAndAddFile("style", "css", "body {\n}\n"); +} + function newJsFile() { createAndAddFile("script", "js", "function foo() {\n}\n"); } diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 49d00311f..ea5d6dd04 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -15,7 +15,7 @@ ApplicationWindow { height: 800 minimumWidth: 400 minimumHeight: 300 - title: qsTr("mix") + title: qsTr("Mix") menuBar: MenuBar { Menu { @@ -28,6 +28,7 @@ ApplicationWindow { MenuItem { action: addExistingFileAction } MenuItem { action: addNewJsFileAction } MenuItem { action: addNewHtmlFileAction } + MenuItem { action: addNewCssFileAction } MenuSeparator {} //MenuItem { action: addNewContractAction } MenuItem { action: closeProjectAction } @@ -39,6 +40,8 @@ ApplicationWindow { MenuItem { action: debugRunAction } MenuItem { action: mineAction } MenuSeparator {} + MenuItem { action: editStatesAction } + MenuSeparator {} MenuItem { action: toggleRunOnLoadAction } } Menu { @@ -48,8 +51,10 @@ ApplicationWindow { MenuSeparator {} MenuItem { action: toggleProjectNavigatorAction } MenuItem { action: showHideRightPanelAction } + MenuItem { action: toggleTransactionLogAction } MenuItem { action: toggleWebPreviewAction } MenuItem { action: toggleWebPreviewOrientationAction } + MenuItem { action: toggleCallsInLog } } } @@ -88,7 +93,18 @@ ApplicationWindow { text: qsTr("Mine") shortcut: "Ctrl+M" onTriggered: clientModel.mine(); - enabled: codeModel.hasContract && !clientModel.running + enabled: codeModel.hasContract && !clientModel.running &&!clientModel.mining + } + + StateList { + id: stateList + } + + Action { + id: editStatesAction + text: qsTr("Edit States") + shortcut: "Ctrl+Alt+E" + onTriggered: stateList.show(); } Connections { @@ -120,6 +136,15 @@ ApplicationWindow { onTriggered: mainContent.toggleWebPreview(); } + Action { + id: toggleTransactionLogAction + text: qsTr("Show States and Transactions") + shortcut: "Alt+1" + checkable: true + checked: mainContent.rightPane.transactionLog.visible + onTriggered: mainContent.rightPane.transactionLog.visible = !mainContent.rightPane.transactionLog.visible + } + Action { id: toggleProjectNavigatorAction text: qsTr("Show Project Navigator") @@ -138,6 +163,15 @@ ApplicationWindow { onTriggered: mainContent.toggleWebPreviewOrientation(); } + Action { + id: toggleCallsInLog + text: qsTr("Show Calls in Transaction Log") + shortcut: "" + checkable: true + checked: mainContent.rightPane.transactionLog.showLogs + onTriggered: mainContent.rightPane.transactionLog.showLogs = !mainContent.rightPane.transactionLog.showLogs + } + Action { id: toggleRunOnLoadAction text: qsTr("Load State on Startup") @@ -188,6 +222,14 @@ ApplicationWindow { onTriggered: projectModel.newHtmlFile(); } + Action { + id: addNewCssFileAction + text: qsTr("New CSS File") + shortcut: "Ctrl+Alt+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newCssFile(); + } + Action { id: addNewContractAction text: qsTr("New Contract") diff --git a/mix/qml/qmldir b/mix/qml/qmldir new file mode 100644 index 000000000..819842274 --- /dev/null +++ b/mix/qml/qmldir @@ -0,0 +1 @@ +singleton Style 1.0 Style.qml diff --git a/mix/res.qrc b/mix/res.qrc index d73635f9d..a33323870 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -61,7 +61,27 @@ stdc/std.sol qml/TransactionLog.qml res/mix_256x256x32.png - qml/CallStack.qml qml/QVariableDeclaration.qml + qml/Style.qml + qml/qmldir + qml/FilesSection.qml + qml/fonts/SourceSansPro-Black.ttf + qml/fonts/SourceSansPro-BlackIt.ttf + qml/fonts/SourceSansPro-Bold.ttf + qml/fonts/SourceSansPro-BoldIt.ttf + qml/fonts/SourceSansPro-ExtraLight.ttf + qml/fonts/SourceSansPro-ExtraLightIt.ttf + qml/fonts/SourceSansPro-It.ttf + qml/fonts/SourceSansPro-Light.ttf + qml/fonts/SourceSansPro-LightIt.ttf + qml/fonts/SourceSansPro-Regular.ttf + qml/fonts/SourceSansPro-Semibold.ttf + qml/fonts/SourceSansPro-SemiboldIt.ttf + qml/fonts/SourceSerifPro-Bold.ttf + qml/fonts/SourceSerifPro-Regular.ttf + qml/fonts/SourceSerifPro-Semibold.ttf + qml/img/closedtriangleindicator_filesproject.png + qml/img/opentriangleindicator_filesproject.png + qml/img/projecticon.png diff --git a/pysol/MANIFEST.in b/pysol/MANIFEST.in new file mode 100644 index 000000000..a3edb5ce1 --- /dev/null +++ b/pysol/MANIFEST.in @@ -0,0 +1,13 @@ +include pysol/*.cpp +include *.py +include libdevcore/*cpp +include libdevcore/*h +include libdevcrypto/*cpp +include libdevcrypto/*h +include libethcore/*cpp +include libethcore/*h +include libsolidity/*cpp +include libsolidity/*h +include libevmcore/*cpp +include libevmcore/*h +include pysol/README.md diff --git a/pysol/README.md b/pysol/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/pysol/pysolidity.cpp b/pysol/pysolidity.cpp new file mode 100644 index 000000000..a2214c67e --- /dev/null +++ b/pysol/pysolidity.cpp @@ -0,0 +1,115 @@ +#include +#include "structmember.h" +#include +#include +#include +#include + +#include "../libdevcore/CommonData.h" + + +#include +#include +#include +#include + + +std::string compile(std::string src) { + dev::solidity::CompilerStack compiler; + try + { + std::vector m_data = compiler.compile(src, false); + return std::string(m_data.begin(), m_data.end()); + } + catch (dev::Exception const& exception) + { + std::ostringstream error; + dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + std::string e = error.str(); + throw(e); + } +} + + +std::string mk_full_signature(std::string src) { + dev::solidity::CompilerStack compiler; + try + { + compiler.compile(src); + return compiler.getInterface(""); + } + catch (dev::Exception const& exception) + { + std::ostringstream error; + dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); + std::string e = error.str(); + throw(e); + } +} + +std::string bob(std::string src) { return src + src; } + +#define PYMETHOD(name, FROM, method, TO) \ + static PyObject * name(PyObject *, PyObject *args) { \ + try { \ + FROM(med) \ + return TO(method(med)); \ + } \ + catch (std::string e) { \ + PyErr_SetString(PyExc_Exception, e.c_str()); \ + return NULL; \ + } \ + } + +#define FROMSTR(v) \ + const char *command; \ + int len; \ + if (!PyArg_ParseTuple(args, "s#", &command, &len)) \ + return NULL; \ + std::string v = std::string(command, len); \ + +// Convert string into python wrapper form +PyObject* pyifyString(std::string s) { + return Py_BuildValue("s#", s.c_str(), s.length()); +} + +// Convert integer into python wrapper form +PyObject* pyifyInteger(unsigned int i) { + return Py_BuildValue("i", i); +} + +// Convert pyobject int into normal form +int cppifyInt(PyObject* o) { + int out; + if (!PyArg_Parse(o, "i", &out)) + throw("Argument should be integer"); + return out; +} + +// Convert pyobject string into normal form +std::string cppifyString(PyObject* o) { + const char *command; + if (!PyArg_Parse(o, "s", &command)) + throw("Argument should be string"); + return std::string(command); +} + +int fh(std::string i) { + return dev::fromHex(i[0]); +} + +PYMETHOD(ps_compile, FROMSTR, compile, pyifyString) +PYMETHOD(ps_mk_full_signature, FROMSTR, mk_full_signature, pyifyString) + +static PyMethodDef PyextMethods[] = { + {"compile", ps_compile, METH_VARARGS, + "Compile code."}, + {"mk_full_signature", ps_mk_full_signature, METH_VARARGS, + "Get the signature of a piece of code."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC initsolidity(void) +{ + Py_InitModule( "solidity", PyextMethods ); +} diff --git a/pysol/setup.py b/pysol/setup.py new file mode 100755 index 000000000..9aa13c248 --- /dev/null +++ b/pysol/setup.py @@ -0,0 +1,41 @@ +import os +os.chdir('..') + +from setuptools import setup, Extension + +from distutils.sysconfig import get_config_vars + +(opt,) = get_config_vars('OPT') +os.environ['OPT'] = " ".join( + flag for flag in opt.split() if flag != '-Wstrict-prototypes' +) + +setup( + # Name of this package + name="ethereum-solidity", + + # Package version + version='1.8.0', + + description='Solidity compiler python wrapper', + maintainer='Vitalik Buterin', + maintainer_email='v@buterin.com', + license='WTFPL', + url='http://www.ethereum.org/', + + # Describes how to build the actual extension module from C source files. + ext_modules=[ + Extension( + 'solidity', # Python name of the module + sources= ['libdevcore/Common.cpp', 'libdevcore/CommonData.cpp', 'libdevcore/CommonIO.cpp', 'libdevcore/FixedHash.cpp', 'libdevcore/Guards.cpp', 'libdevcore/Log.cpp', 'libdevcore/RangeMask.cpp', 'libdevcore/RLP.cpp', 'libdevcore/Worker.cpp', 'libdevcrypto/AES.cpp', 'libdevcrypto/Common.cpp', 'libdevcrypto/CryptoPP.cpp', 'libdevcrypto/ECDHE.cpp', 'libdevcrypto/FileSystem.cpp', 'libdevcrypto/MemoryDB.cpp', 'libdevcrypto/OverlayDB.cpp', 'libdevcrypto/SHA3.cpp', 'libdevcrypto/TrieCommon.cpp', 'libdevcrypto/TrieDB.cpp', 'libethcore/CommonEth.cpp', 'libethcore/CommonJS.cpp', 'libethcore/Exceptions.cpp', 'libsolidity/AST.cpp', 'libsolidity/ASTJsonConverter.cpp', 'libsolidity/ASTPrinter.cpp', 'libsolidity/CompilerContext.cpp', 'libsolidity/Compiler.cpp', 'libsolidity/CompilerStack.cpp', 'libsolidity/CompilerUtils.cpp', 'libsolidity/DeclarationContainer.cpp', 'libsolidity/ExpressionCompiler.cpp', 'libsolidity/GlobalContext.cpp', 'libsolidity/InterfaceHandler.cpp', 'libsolidity/NameAndTypeResolver.cpp', 'libsolidity/Parser.cpp', 'libsolidity/Scanner.cpp', 'libsolidity/SourceReferenceFormatter.cpp', 'libsolidity/Token.cpp', 'libsolidity/Types.cpp', 'libevmcore/Assembly.cpp', 'libevmcore/Instruction.cpp', 'pysol/pysolidity.cpp'], + libraries=['boost_python', 'boost_filesystem', 'boost_chrono', 'boost_thread', 'cryptopp', 'leveldb', 'jsoncpp'], + include_dirs=['/usr/include/boost', '..', '../..', '.'], + extra_compile_args=['--std=c++11', '-Wno-unknown-pragmas'] + )], + py_modules=[ + ], + scripts=[ + ], + entry_points={ + } + ), diff --git a/sc/CMakeLists.txt b/sc/CMakeLists.txt index 2adde060e..ee638bb52 100644 --- a/sc/CMakeLists.txt +++ b/sc/CMakeLists.txt @@ -8,7 +8,9 @@ set(EXECUTABLE sc) add_executable(${EXECUTABLE} ${SRC_LIST}) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index aa651eb41..db12231a7 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -161,22 +161,22 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co std::string title; switch(_type) { - case DocumentationType::ABI_INTERFACE: + case DocumentationType::ABIInterface: argName = g_argAbiStr; suffix = ".abi"; title = "Contract JSON ABI"; break; - case DocumentationType::ABI_SOLIDITY_INTERFACE: + case DocumentationType::ABISolidityInterface: argName = g_argSolAbiStr; suffix = ".sol"; title = "Contract Solidity ABI"; break; - case DocumentationType::NATSPEC_USER: + case DocumentationType::NatspecUser: argName = g_argNatspecUserStr; suffix = ".docuser"; title = "User Documentation"; break; - case DocumentationType::NATSPEC_DEV: + case DocumentationType::NatspecDev: argName = g_argNatspecDevStr; suffix = ".docdev"; title = "Developer Documentation"; @@ -436,10 +436,10 @@ void CommandLineInterface::actOnInput() } handleBytecode(contract); - handleMeta(DocumentationType::ABI_INTERFACE, contract); - handleMeta(DocumentationType::ABI_SOLIDITY_INTERFACE, contract); - handleMeta(DocumentationType::NATSPEC_DEV, contract); - handleMeta(DocumentationType::NATSPEC_USER, contract); + handleMeta(DocumentationType::ABIInterface, contract); + handleMeta(DocumentationType::ABISolidityInterface, contract); + handleMeta(DocumentationType::NatspecDev, contract); + handleMeta(DocumentationType::NatspecUser, contract); } // end of contracts iteration } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 764bf928e..ab8afcd70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,8 +21,10 @@ target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) target_link_libraries(testeth solidity) -target_link_libraries(testeth webthree) - +if (NOT HEADLESS) + target_link_libraries(testeth webthree) + target_link_libraries(testeth natspec) +endif() if (JSONRPC) target_link_libraries(testeth web3jsonrpc) target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES}) diff --git a/test/SolidityABIJSON.cpp b/test/SolidityABIJSON.cpp index 13e65761f..242a88e77 100644 --- a/test/SolidityABIJSON.cpp +++ b/test/SolidityABIJSON.cpp @@ -48,7 +48,7 @@ public: auto msg = std::string("Parsing contract failed with: ") + boost::diagnostic_information(_e); BOOST_FAIL(msg); } - std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABI_INTERFACE); + std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABIInterface); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; @@ -409,7 +409,78 @@ BOOST_AUTO_TEST_CASE(inherited) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) +{ + char const* sourceCode = R"( + contract test { + function f(uint, uint k) returns(uint ret_k, uint ret_g){ + uint g = 8; + ret_k = k; + ret_g = g; + } + })"; + + char const* interface = R"([ + { + "name": "f", + "constant": false, + "type": "function", + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "k", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "ret_k", + "type": "uint256" + }, + { + "name": "ret_g", + "type": "uint256" + } + ] + } + ])"; + checkInterface(sourceCode, interface); +} + +BOOST_AUTO_TEST_CASE(empty_name_return_parameter) +{ + char const* sourceCode = R"( + contract test { + function f(uint k) returns(uint){ + return k; + } + })"; + + char const* interface = R"([ + { + "name": "f", + "constant": false, + "type": "function", + "inputs": [ + { + "name": "k", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + } + ])"; + checkInterface(sourceCode, interface); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index f248a5a07..31b80894e 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -576,7 +576,7 @@ BOOST_AUTO_TEST_CASE(simple_mapping) " }\n" "}"; compileAndRun(sourceCode); - + BOOST_CHECK(callContractFunction("get(uint8)", byte(0)) == encodeArgs(byte(0x00))); BOOST_CHECK(callContractFunction("get(uint8)", byte(0x01)) == encodeArgs(byte(0x00))); BOOST_CHECK(callContractFunction("get(uint8)", byte(0xa7)) == encodeArgs(byte(0x00))); @@ -933,7 +933,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("data()") == encodeArgs(8)); BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); - BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(toBigEndian(u256(123))))); + BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes({0x7b})))); BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337)))); BOOST_CHECK(callContractFunction("super_secret_data()") == bytes()); } @@ -959,6 +959,24 @@ BOOST_AUTO_TEST_CASE(complex_accessors) BOOST_CHECK(callContractFunction("to_multiple_map(uint256,uint256)", 42, 23) == encodeArgs(31)); } +BOOST_AUTO_TEST_CASE(struct_accessor) +{ + char const* sourceCode = R"( + contract test { + struct Data { uint a; uint8 b; mapping(uint => uint) c; bool d; } + mapping(uint => Data) public data; + function test() { + data[7].a = 1; + data[7].b = 2; + data[7].c[0] = 3; + data[7].d = true; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("data(uint256)", 7) == encodeArgs(1, 2, true)); +} + BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n" @@ -2095,6 +2113,117 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,hash256,uint256,bool)"))); } +BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) +{ + char const* sourceCode = R"( + contract test { + function f(uint, uint k) returns(uint ret_k, uint ret_g){ + uint g = 8; + ret_k = k; + ret_g = g; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); + BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) == encodeArgs(9, 8)); +} + +BOOST_AUTO_TEST_CASE(empty_name_return_parameter) +{ + char const* sourceCode = R"( + contract test { + function f(uint k) returns(uint){ + return k; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", 9) == encodeArgs(9)); +} + +BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) +{ + char const* sourceCode = R"( + contract c { + function foo(uint a, uint b, uint c) returns (hash d) + { + d = sha3(a, b, c); + } + })"; + compileAndRun(sourceCode); + + BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs( + dev::sha3( + toBigEndian(u256(10)) + + toBigEndian(u256(12)) + + toBigEndian(u256(13))))); +} + +BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals) +{ + char const* sourceCode = R"( + contract c { + function foo(uint a, uint16 b) returns (hash d) + { + d = sha3(a, b, 145); + } + })"; + compileAndRun(sourceCode); + + BOOST_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12) == encodeArgs( + dev::sha3( + toBigEndian(u256(10)) + + bytes({0x0, 0xc}) + + bytes({0x91})))); +} + +BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) +{ + char const* sourceCode = R"( + contract c { + function foo() returns (hash d) + { + d = sha3("foo"); + } + function bar(uint a, uint16 b) returns (hash d) + { + d = sha3(a, b, 145, "foo"); + } + })"; + compileAndRun(sourceCode); + + BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::sha3("foo"))); + + BOOST_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12) == encodeArgs( + dev::sha3( + toBigEndian(u256(10)) + + bytes({0x0, 0xc}) + + bytes({0x91}) + + bytes({0x66, 0x6f, 0x6f})))); +} + +BOOST_AUTO_TEST_CASE(generic_call) +{ + char const* sourceCode = R"**( + contract receiver { + uint public received; + function receive(uint256 x) { received = x; } + } + contract sender { + function doSend(address rec) returns (uint d) + { + string4 signature = string4(string32(sha3("receive(uint256)"))); + rec.call.value(2)(signature, 23); + return receiver(rec).received(); + } + } + )**"; + compileAndRun(sourceCode, 0, "receiver"); + u160 const c_receiverAddress = m_contractAddress; + compileAndRun(sourceCode, 50, "sender"); + BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23)); + BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp index a0cca3a3a..9cd13dcfd 100644 --- a/test/SolidityExpressionCompiler.cpp +++ b/test/SolidityExpressionCompiler.cpp @@ -91,7 +91,15 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ { Parser parser; ASTPointer sourceUnit; - BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); + try + { + sourceUnit = parser.parse(make_shared(CharStream(_sourceCode))); + } + catch(boost::exception const& _e) + { + auto msg = std::string("Parsing source code failed with: \n") + boost::diagnostic_information(_e); + BOOST_FAIL(msg); + } vector declarations; declarations.reserve(_globalDeclarations.size() + 1); @@ -177,6 +185,66 @@ BOOST_AUTO_TEST_CASE(int_literal) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 wei; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH1), 0x1}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 szabo; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 finney; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + +BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () + { + var x = 1 ether; + } + })"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({byte(eth::Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_CASE(comparison) { char const* sourceCode = "contract test {\n" @@ -409,7 +477,7 @@ BOOST_AUTO_TEST_CASE(blockhash) " }\n" "}\n"; bytes code = compileFirstExpression(sourceCode, {}, {}, - {make_shared("block", make_shared(MagicType::Kind::BLOCK))}); + {make_shared("block", make_shared(MagicType::Kind::Block))}); bytes expectation({byte(eth::Instruction::PUSH1), 0x03, byte(eth::Instruction::BLOCKHASH)}); diff --git a/test/SolidityInterface.cpp b/test/SolidityInterface.cpp index 3b7d26ec4..78de2356a 100644 --- a/test/SolidityInterface.cpp +++ b/test/SolidityInterface.cpp @@ -43,7 +43,7 @@ public: { m_code = _code; BOOST_REQUIRE_NO_THROW(m_compilerStack.parse(_code)); - m_interface = m_compilerStack.getMetadata("", DocumentationType::ABI_SOLIDITY_INTERFACE); + m_interface = m_compilerStack.getMetadata("", DocumentationType::ABISolidityInterface); BOOST_REQUIRE_NO_THROW(m_reCompiler.parse(m_interface)); return m_reCompiler.getContractDefinition(_contractName); } diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index ae6c374b4..f4be31f4b 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -904,6 +904,76 @@ BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(empty_name_input_parameter) +{ + char const* text = R"( + contract test { + function f(uint){ + } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(empty_name_return_parameter) +{ + char const* text = R"( + contract test { + function f() returns(bool){ + } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) +{ + char const* text = R"( + contract test { + function f(uint, uint k) returns(uint ret_k){ + return k; + } + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) +{ + char const* text = R"( + contract test { + function f() returns(uint ret_k, uint){ + return 5; + } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) +{ + char const* sourceCode = "contract c { function f() { var x = f(); } }"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) +{ + char const* sourceCodeFine = R"( + contract c { + function c () + { + a = 115792089237316195423570985008687907853269984665640564039458; + } + uint256 a; + })"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCodeFine)); + char const* sourceCode = R"( + contract c { + function c () + { + a = 115792089237316195423570985008687907853269984665640564039458 ether; + } + uint256 a; + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp index 911820ddd..b652ad10e 100644 --- a/test/SolidityNatspecJSON.cpp +++ b/test/SolidityNatspecJSON.cpp @@ -54,9 +54,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_USER); + generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NatspecUser); else - generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_DEV); + generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NatspecDev); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 9ba38a4a1..7af99567b 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -660,6 +660,38 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) BOOST_CHECK_THROW(parseText(text), ParserError); } +BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) +{ + char const* text = R"( + contract c { + function c () + { + a = 1 wei; + b = 2 szabo; + c = 3 finney; + b = 4 ether; + } + uint256 a; + uint256 b; + uint256 c; + uint256 d; + })"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + +BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations_in_expressions) +{ + char const* text = R"( + contract c { + function c () + { + a = 1 wei * 100 wei + 7 szabo - 3; + } + uint256 a; + })"; + BOOST_CHECK_NO_THROW(parseTextExplainError(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/SolidityScanner.cpp b/test/SolidityScanner.cpp index 7dc9ef482..2e4e5db08 100644 --- a/test/SolidityScanner.cpp +++ b/test/SolidityScanner.cpp @@ -41,17 +41,17 @@ BOOST_AUTO_TEST_CASE(test_empty) BOOST_AUTO_TEST_CASE(smoke_test) { Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION); - BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Break); + BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); - BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } @@ -59,85 +59,85 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(string_escapes) { Scanner scanner(CharStream(" { \"a\\x61\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); } BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); } BOOST_AUTO_TEST_CASE(string_escape_illegal) { Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); // TODO recovery from illegal tokens should be improved - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(hex_numbers) { Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "0x765432536763762734623472346"); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(negative_numbers) { Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9;")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); - BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ".2"); - BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Add); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "0x78"); - BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Add); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "7.3"); - BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Add); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "8.9"); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(locations) { Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 0); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 19); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 20); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 23); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); - BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 50); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); @@ -147,13 +147,13 @@ BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE); - BOOST_CHECK_EQUAL(scanner.next(), Token::LT); - BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::INC); - BOOST_CHECK_EQUAL(scanner.next(), Token::ARROW); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LessThanOrEqual); + BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Add); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssignAdd); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Inc); + BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); } @@ -174,9 +174,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) BOOST_AUTO_TEST_CASE(documentation_comments_parsed) { Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } @@ -186,9 +186,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) Scanner scanner(CharStream("some other tokens /**\n" "* Send $(value / 1000) chocolates to the user\n" "*/")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } @@ -198,9 +198,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) Scanner scanner(CharStream("some other tokens /**\n" " Send $(value / 1000) chocolates to the user\n" "*/")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } @@ -210,9 +210,9 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) Scanner scanner(CharStream("some other tokens /** \t \r \n" "\t \r * Send $(value / 1000) chocolates to the user\n" "*/")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } @@ -250,11 +250,20 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) Scanner scanner(CharStream("hello_world ///documentation comment \n" "//simple comment \n" "<<")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "documentation comment "); } +BOOST_AUTO_TEST_CASE(ether_subdenominations) +{ + Scanner scanner(CharStream("wei szabo finney ether")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::SubWei); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney); + BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/natspec.cpp b/test/natspec.cpp new file mode 100644 index 000000000..8ba660418 --- /dev/null +++ b/test/natspec.cpp @@ -0,0 +1,116 @@ +/* + 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 . + */ +/** @file natspec.cpp + * @author Marek Kotewicz + * @date 2015 + */ + +#if !ETH_HEADLESS + +#include +#include +#include + +using namespace std; + +BOOST_AUTO_TEST_SUITE(natspec) + +BOOST_AUTO_TEST_CASE(natspec_eval_function_exists) +{ + // given + NatspecExpressionEvaluator e; + // when + string result = e.evalExpression("`typeof evaluateExpression`").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "function"); +} + +BOOST_AUTO_TEST_CASE(natspec_js_eval) +{ + // given + NatspecExpressionEvaluator e; + // when + string result = e.evalExpression("`1 + 2`").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "3"); +} + +BOOST_AUTO_TEST_CASE(natspec_create_custom_function) +{ + // given + NatspecExpressionEvaluator e; + // when + auto x = e.evalExpression("`test = function (x) { return x + 'ok'; }`"); // ommit var, make it global + string result = e.evalExpression("`test(5)`").toStdString(); + string result2 = e.evalExpression("`typeof test`").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "5ok"); + BOOST_CHECK_EQUAL(result2, "function"); +} + +BOOST_AUTO_TEST_CASE(natspec_js_eval_separated_expressions) +{ + // given + NatspecExpressionEvaluator e; + // when + string result = e.evalExpression("`x = 1` + `y = 2` will be equal `x + y`").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "1 + 2 will be equal 3"); +} + +BOOST_AUTO_TEST_CASE(natspec_js_eval_input_params) +{ + // given + char const* abi = R"([ + { + "name": "f", + "constant": false, + "type": "function", + "inputs": [ + { + "name": "a", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + } + ])"; + NatspecExpressionEvaluator e(abi, "'f'", "[4]"); + // when + string result = e.evalExpression("Will multiply `a` by 7 and return `a * 7`.").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "Will multiply 4 by 7 and return 28."); +} + +BOOST_AUTO_TEST_CASE(natspec_js_eval_error) +{ + // given + NatspecExpressionEvaluator e; + // when + string result = e.evalExpression("`test(`").toStdString(); + // then + BOOST_CHECK_EQUAL(result, "`test(`"); +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif diff --git a/test/stRefundTestFiller.json b/test/stRefundTestFiller.json index 6b2b2fc15..6749fd3dd 100644 --- a/test/stRefundTestFiller.json +++ b/test/stRefundTestFiller.json @@ -303,6 +303,34 @@ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "data" : "" } - } + }, + "RefundOverflow" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "400", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "", + "gasLimit" : "5789604461865809771178549250434395392663499233282028201972879200395656482016", + "gasPrice" : "20", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "" + } + } } diff --git a/test/stSystemOperationsTestFiller.json b/test/stSystemOperationsTestFiller.json index 253ba0a8a..6a4b83e34 100644 --- a/test/stSystemOperationsTestFiller.json +++ b/test/stSystemOperationsTestFiller.json @@ -33,6 +33,76 @@ } }, + "testRandomTest": { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "currentGasLimit" : "1000000", + "currentNumber" : "300", + "currentTimestamp" : "2", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x424443444243434383f0155af055", + "nonce" : "0", + "storage" : { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "code" : "0x", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : { + "data" : "", + "gasLimit" : "10000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "value" : "100000" + } + }, + + "createWithInvalidOpcode": { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "currentGasLimit" : "1000000", + "currentNumber" : "300", + "currentTimestamp" : "2", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : { + "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "0x444242424245434253f0", + "storage": {} + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000000000000000", + "nonce" : 0, + "code" : "", + "storage": {} + } + }, + "transaction" : { + "nonce" : "0", + "gasPrice" : "1", + "gasLimit" : "10000", + "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : "100000", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "data" : "" + } + }, + "createNameRegistratorOOG_MemExpansionInsufficientBalance": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index cd42af795..a5092b60b 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -169,7 +169,7 @@ }, "pre" : { - "b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "1000", "code" : "", "nonce" : "0", @@ -189,6 +189,68 @@ } }, + "TransactionFromCoinbaseHittingBlockGasLimit" : { + "env" : { + "currentCoinbase" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1100", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : + { + "data" : "", + "gasLimit" : "1100", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "TransactionFromCoinbaseHittingBlockGasLimit1" : { + "env" : { + "currentCoinbase" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1100", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : + { + "data" : "", + "gasLimit" : "1101", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + "ContractStoreClearsSuccess" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", diff --git a/test/vmSha3TestFiller.json b/test/vmSha3TestFiller.json index 7ed729520..851b7aab9 100644 --- a/test/vmSha3TestFiller.json +++ b/test/vmSha3TestFiller.json @@ -193,5 +193,89 @@ "gasPrice" : "100000000000000", "gas" : "10000" } + }, + + "sha3_bigSize": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SHA3 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "data" : "", + "gasPrice" : "1", + "gas" : "0x10000000000" + } + }, + + "sha3_bigOffset": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SHA3 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 2)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "data" : "", + "gasPrice" : "1", + "gas" : "0x10000000000" + } + }, + + "sha3_bigOffset2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : 1, + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "nonce" : 0, + "code" : "{ [[ 0 ]] (SHA3 0x1000000 2)}", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "data" : "", + "gasPrice" : "1", + "gas" : "0x100000000" + } } } diff --git a/test/webthreestubclient.h b/test/webthreestubclient.h index 6a82263d0..1836f6506 100644 --- a/test/webthreestubclient.h +++ b/test/webthreestubclient.h @@ -213,6 +213,16 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } + bool eth_flush() throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p = Json::nullValue; + Json::Value result = this->CallMethod("eth_flush",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } Json::Value eth_blockByHash(const std::string& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; @@ -347,13 +357,13 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - bool eth_changed(const int& param1) throw (jsonrpc::JsonRpcException) + Json::Value eth_changed(const int& param1) throw (jsonrpc::JsonRpcException) { Json::Value p; p.append(param1); Json::Value result = this->CallMethod("eth_changed",p); - if (result.isBool()) - return result.asBool(); + if (result.isArray()) + return result; else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } @@ -377,6 +387,26 @@ class WebThreeStubClient : public jsonrpc::Client else throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } + Json::Value eth_getWork() throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p = Json::nullValue; + Json::Value result = this->CallMethod("eth_getWork",p); + if (result.isArray()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + bool eth_submitWork(const std::string& param1) throw (jsonrpc::JsonRpcException) + { + Json::Value p; + p.append(param1); + Json::Value result = this->CallMethod("eth_submitWork",p); + if (result.isBool()) + return result.asBool(); + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } bool db_put(const std::string& param1, const std::string& param2, const std::string& param3) throw (jsonrpc::JsonRpcException) { Json::Value p; diff --git a/third/CMakeLists.txt b/third/CMakeLists.txt index 4b83b4fa5..71f1e70f2 100644 --- a/third/CMakeLists.txt +++ b/third/CMakeLists.txt @@ -38,7 +38,9 @@ target_link_libraries(${EXECUTABLE} ethereum) target_link_libraries(${EXECUTABLE} evm) target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} secp256k1) -target_link_libraries(${EXECUTABLE} serpent) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +target_link_libraries(${EXECUTABLE} serpent) +endif() target_link_libraries(${EXECUTABLE} lll) target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore)