diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index fb134a82d..d9e8dff6f 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -51,7 +51,9 @@ target_link_libraries(${EXECUTABLE} ethcore) target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} secp256k1) target_link_libraries(${EXECUTABLE} lll) -target_link_libraries(${EXECUTABLE} solidity) +if (SOLIDITY) + target_link_libraries(${EXECUTABLE} solidity) +endif () target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} web3jsonrpc) diff --git a/alethzero/NatspecHandler.cpp b/alethzero/NatspecHandler.cpp index 27cf00341..ffab6db58 100644 --- a/alethzero/NatspecHandler.cpp +++ b/alethzero/NatspecHandler.cpp @@ -51,7 +51,7 @@ NatspecHandler::~NatspecHandler() void NatspecHandler::add(dev::h256 const& _contractHash, string const& _doc) { m_db->Put(m_writeOptions, _contractHash.ref(), _doc); - cdebug << "Registering NatSpec: " << _contractHash.abridged() << _doc; + cdebug << "Registering NatSpec: " << _contractHash << _doc; } string NatspecHandler::retrieve(dev::h256 const& _contractHash) const diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1ebdf9e23..d3e5650bf 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -168,6 +168,7 @@ static std::string toString(TransactionException _te) } } +#if ETH_SOLIDITY static string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName) { string ret = ""; @@ -182,6 +183,7 @@ static string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, s } return ret; } +#endif static tuple, bytes, string> userInputToCode(string const& _user, bool _opt) { @@ -197,6 +199,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, boost::replace_all_copy(u, " ", ""); data = fromHex(u); } +#if ETH_SOLIDITY else if (sourceIsSolidity(_user)) { dev::solidity::CompilerStack compiler(true); @@ -220,6 +223,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, errors.push_back("Solidity: Uncaught exception"); } } +#endif #if ETH_SERPENT else if (sourceIsSerpent(_user)) { @@ -394,6 +398,7 @@ void Transact::on_send_clicked() // If execution is a contract creation, add Natspec to // a local Natspec LEVELDB ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice()); +#if ETH_SOLIDITY string src = ui->data->toPlainText().toStdString(); if (sourceIsSolidity(src)) try @@ -407,6 +412,7 @@ void Transact::on_send_clicked() } } catch (...) {} +#endif } else ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); diff --git a/eth/main.cpp b/eth/main.cpp index da8943e46..2d79f4954 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -136,6 +136,7 @@ void help() << " -G,--opencl When mining use the GPU via OpenCL." << endl << " --opencl-platform When mining using -G/--opencl use OpenCL platform n (default: 0)." << endl << " --opencl-device When mining using -G/--opencl use OpenCL device n (default: 0)." << endl + << " -t, --mining-threads Limit number of CPU/GPU miners to n (default: use everything available on selected platform)" << endl << "Client networking:" << endl << " --client-name Add a name to your client's version string (default: blank)." << endl << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl @@ -370,7 +371,7 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) for (unsigned i = 0; !completed; ++i) { if (current) - cnote << "Mining on PoWhash" << current.headerHash.abridged() << ": " << f.miningProgress(); + cnote << "Mining on PoWhash" << current.headerHash << ": " << f.miningProgress(); else cnote << "Getting work package..."; Json::Value v = rpc.eth_getWork(); @@ -380,12 +381,12 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod) current.headerHash = hh; current.seedHash = h256(v[1].asString()); current.boundary = h256(fromHex(v[2].asString()), h256::AlignRight); - cnote << "Got work package:" << current.headerHash.abridged() << " < " << current.boundary; + cnote << "Got work package:" << current.headerHash << " < " << current.boundary; f.setWork(current); } this_thread::sleep_for(chrono::milliseconds(_recheckPeriod)); } - cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash.abridged() << "," << solution.mixHash.abridged() << "] to" << _remote << "..."; + cnote << "Solution found; submitting [" << solution.nonce << "," << current.headerHash << "," << solution.mixHash << "] to" << _remote << "..."; bool ok = rpc.eth_submitWork("0x" + toString(solution.nonce), "0x" + toString(current.headerHash), "0x" + toString(solution.mixHash)); if (ok) clog(HappyChannel) << "Submitted and accepted."; @@ -482,6 +483,7 @@ int main(int argc, char** argv) MinerType minerType = MinerType::CPU; unsigned openclPlatform = 0; unsigned openclDevice = 0; + unsigned miningThreads = UINT_MAX; /// File name for import/export. string filename; @@ -600,13 +602,13 @@ int main(int argc, char** argv) } else if (arg == "--opencl-platform" && i + 1 < argc) try { - openclPlatform= stol(argv[++i]); - } - catch (...) - { - cerr << "Bad " << arg << " option: " << argv[i] << endl; - return -1; - } + openclPlatform = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } else if (arg == "--opencl-device" && i + 1 < argc) try { openclDevice = stol(argv[++i]); @@ -850,6 +852,17 @@ int main(int argc, char** argv) return -1; } } + else if ((arg == "-t" || arg == "--mining-threads") && i + 1 < argc) + { + try { + miningThreads = stol(argv[++i]); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << argv[i] << endl; + return -1; + } + } else if (arg == "-b" || arg == "--bootstrap") bootstrap = true; else if (arg == "-f" || arg == "--force-mining") @@ -905,8 +918,16 @@ int main(int argc, char** argv) if (sessionSecret) sigKey = KeyPair(sessionSecret); - ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); - ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); + + + if (minerType == MinerType::CPU) + ProofOfWork::CPUMiner::setNumInstances(miningThreads); + else if (minerType == MinerType::GPU) + { + ProofOfWork::GPUMiner::setDefaultPlatform(openclPlatform); + ProofOfWork::GPUMiner::setDefaultDevice(openclDevice); + ProofOfWork::GPUMiner::setNumInstances(miningThreads); + } // Two codepaths is necessary since named block require database, but numbered // blocks are superuseful to have when database is already open in another process. diff --git a/libdevcore/Log.cpp b/libdevcore/Log.cpp index c4cacad26..983431b88 100644 --- a/libdevcore/Log.cpp +++ b/libdevcore/Log.cpp @@ -34,14 +34,25 @@ using namespace dev; int dev::g_logVerbosity = 5; map dev::g_logOverride; +#ifdef _WIN32 +const char* LogChannel::name() { return EthGray "..."; } +const char* LeftChannel::name() { return EthNavy "<--"; } +const char* RightChannel::name() { return EthGreen "-->"; } +const char* WarnChannel::name() { return EthOnRed EthBlackBold " X"; } +const char* NoteChannel::name() { return EthBlue " i"; } +const char* DebugChannel::name() { return EthWhite " D"; } +#else const char* LogChannel::name() { return EthGray "···"; } const char* LeftChannel::name() { return EthNavy "◀▬▬"; } const char* RightChannel::name() { return EthGreen "▬▬▶"; } const char* WarnChannel::name() { return EthOnRed EthBlackBold " ✘"; } const char* NoteChannel::name() { return EthBlue " ℹ"; } const char* DebugChannel::name() { return EthWhite " ◇"; } +#endif -LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v) +LogOutputStreamBase::LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v, bool _autospacing): + m_autospacing(_autospacing), + m_verbosity(_v) { auto it = g_logOverride.find(_info); if ((it != g_logOverride.end() && it->second == true) || (it == g_logOverride.end() && (int)_v <= g_logVerbosity)) diff --git a/libdevcore/Log.h b/libdevcore/Log.h index c8924724b..71c2b3450 100644 --- a/libdevcore/Log.h +++ b/libdevcore/Log.h @@ -26,8 +26,12 @@ #include #include #include +#include #include "vector_ref.h" +#include "Common.h" #include "CommonIO.h" +#include "CommonData.h" +#include "FixedHash.h" #include "Terminal.h" namespace dev @@ -76,35 +80,128 @@ std::string getThreadName(); /// The default logging channels. Each has an associated verbosity and three-letter prefix (name() ). /// Channels should inherit from LogChannel and define name() and verbosity. struct LogChannel { static const char* name(); static const int verbosity = 1; }; -struct LeftChannel: public LogChannel { static const char* name(); }; +struct LeftChannel: public LogChannel { static const char* name(); }; struct RightChannel: public LogChannel { static const char* name(); }; -struct WarnChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; -struct NoteChannel: public LogChannel { static const char* name(); }; +struct WarnChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; +struct NoteChannel: public LogChannel { static const char* name(); }; struct DebugChannel: public LogChannel { static const char* name(); static const int verbosity = 0; }; +enum LogTag +{ + None, + url, + error +}; + class LogOutputStreamBase { public: - LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v); + LogOutputStreamBase(char const* _id, std::type_info const* _info, unsigned _v, bool _autospacing); + + void comment(std::string const& _t) + { + switch (m_logTag) + { + case url: m_sstr << EthNavyUnder; break; + case error: m_sstr << EthRedBold; break; + default:; + } + m_sstr << _t << EthReset; + m_logTag = None; + } + + void append(unsigned long _t) { m_sstr << EthBlue << _t << EthReset; } + void append(long _t) { m_sstr << EthBlue << _t << EthReset; } + void append(unsigned int _t) { m_sstr << EthBlue << _t << EthReset; } + void append(int _t) { m_sstr << EthBlue << _t << EthReset; } + void append(bigint const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(u256 const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(u160 const& _t) { m_sstr << EthNavy << _t << EthReset; } + void append(double _t) { m_sstr << EthBlue << _t << EthReset; } + template void append(FixedHash const& _t) { m_sstr << EthTeal "#" << _t.abridged() << EthReset; } + void append(h160 const& _t) { m_sstr << EthRed "@" << _t.abridged() << EthReset; } + void append(h256 const& _t) { m_sstr << EthCyan "#" << _t.abridged() << EthReset; } + void append(h512 const& _t) { m_sstr << EthTeal "##" << _t.abridged() << EthReset; } + void append(std::string const& _t) { m_sstr << EthGreen "\"" + _t + "\"" EthReset; } + void append(bytes const& _t) { m_sstr << EthYellow "%" << toHex(_t) << EthReset; } + void append(bytesConstRef _t) { m_sstr << EthYellow "%" << toHex(_t) << EthReset; } + template void append(std::vector const& _t) + { + m_sstr << EthWhite "[" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthWhite ", " EthReset : ""); + append(i); + } + m_sstr << EthWhite "]" EthReset; + } + template void append(std::set const& _t) + { + m_sstr << EthYellow "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthYellow ", " EthReset : ""); + append(i); + } + m_sstr << EthYellow "}" EthReset; + } + template void append(std::map const& _t) + { + m_sstr << EthLime "{" EthReset; + int n = 0; + for (auto const& i: _t) + { + m_sstr << (n++ ? EthLime ", " EthReset : ""); + append(i.first); + m_sstr << (n++ ? EthLime ": " EthReset : ""); + append(i.second); + } + m_sstr << EthLime "}" EthReset; + } + template void append(std::pair const& _t) + { + m_sstr << EthPurple "(" EthReset; + append(_t.first); + m_sstr << EthPurple ", " EthReset; + append(_t.second); + m_sstr << EthPurple ")" EthReset; + } + template void append(T const& _t) + { + m_sstr << toString(_t); + } + template void append(boost::asio::ip::tcp::endpoint const& _t) + { + m_sstr << EthNavyUnder "tcp://" << _t << EthReset; + } protected: + bool m_autospacing = false; + unsigned m_verbosity = 0; std::stringstream m_sstr; ///< The accrued log entry. + LogTag m_logTag; }; /// Logging class, iostream-like, that can be shifted to. template -class LogOutputStream: private LogOutputStreamBase +class LogOutputStream: LogOutputStreamBase { public: /// Construct a new object. /// If _term is true the the prefix info is terminated with a ']' character; if not it ends only with a '|' character. - LogOutputStream(): LogOutputStreamBase(Id::name(), &typeid(Id), Id::verbosity) {} + LogOutputStream(): LogOutputStreamBase(Id::name(), &typeid(Id), Id::verbosity, _AutoSpacing) {} /// Destructor. Posts the accrued log entry to the g_logPost function. ~LogOutputStream() { if (Id::verbosity <= g_logVerbosity) g_logPost(m_sstr.str(), Id::name()); } + LogOutputStream& operator<<(std::string const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; comment(_t); } return *this; } + + LogOutputStream& operator<<(LogTag _t) { m_logTag = _t; return *this; } + /// Shift arbitrary data to the log. Spaces will be added between items as required. - template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; m_sstr << _t; } return *this; } + template LogOutputStream& operator<<(T const& _t) { if (Id::verbosity <= g_logVerbosity) { if (_AutoSpacing && m_sstr.str().size() && m_sstr.str().back() != ' ') m_sstr << " "; append(_t); } return *this; } }; // Simple cout-like stream objects for accessing common log channels. diff --git a/libdevcore/Terminal.h b/libdevcore/Terminal.h index f4e31e7e4..a30a527f2 100644 --- a/libdevcore/Terminal.h +++ b/libdevcore/Terminal.h @@ -9,75 +9,70 @@ namespace con #define EthReset "" // Text Reset -// Regular Colors -#define EthBlack "" // Black -#define EthRed "" // Red -#define EthGreen "" // Green -#define EthYellow "" // Yellow -#define EthBlue "" // Blue -#define EthPurple "" // Purple -#define EthCyan "" // Cyan -#define EthWhite "" // White - -// Bold -#define EthBlackB "" // Black -#define EthRedB "" // Red -#define EthGreenB "" // Green -#define EthYellowB "" // Yellow -#define EthBlueB "" // Blue -#define EthPurpleB "" // Purple -#define EthCyanB "" // Cyan -#define EthWhiteB "" // White - -// Underline -#define EthBlackU "" // Black -#define EthRedU "" // Red -#define EthGreenU "" // Green -#define EthYellowU "" // Yellow -#define EthBlueU "" // Blue -#define EthPurpleU "" // Purple -#define EthCyanU "" // Cyan -#define EthWhiteU "" // White +#define EthReset "" // Text Reset -// Background -#define EthBlackOn "" // Black -#define EthRedOn "" // Red -#define EthGreenOn "" // Green -#define EthYellowOn "" // Yellow -#define EthBlueOn "" // Blue -#define EthPurpleOn "" // Purple -#define EthCyanOn "" // Cyan -#define EthWhiteOn "" // White - -// High Intensity -#define EthCoal "" // Black -#define EthRedI "" // Red -#define EthLime "" // Green -#define EthYellowI "" // Yellow -#define EthBlueI "" // Blue -#define EthPurpleI "" // Purple -#define EthCyanI "" // Cyan -#define EthWhiteI "" // White - -// Bold High Intensity -#define EthBlackBI "" // Black -#define EthRedBI "" // Red -#define EthGreenBI "" // Green -#define EthYellowBI "" // Yellow -#define EthBlueBI "" // Blue -#define EthPurpleBI "" // Purple -#define EthCyanBI "" // Cyan -#define EthWhiteBI "" // White - -// High Intensity backgrounds -#define EthBlackOnI "" // Black -#define EthRedOnI "" // Red -#define EthGreenOnI "" // Green -#define EthYellowOnI "" // Yellow -#define EthBlueOnI "" // Blue -#define EthPurpleOnI "" // Purple -#define EthCyanOnI "" // Cyan -#define EthWhiteOnI "" // White + // Regular Colors +#define EthBlack "" // Black +#define EthCoal "" // Black +#define EthGray "" // White +#define EthWhite "" // White +#define EthMaroon "" // Red +#define EthRed "" // Red +#define EthGreen "" // Green +#define EthLime "" // Green +#define EthOrange "" // Yellow +#define EthYellow "" // Yellow +#define EthNavy "" // Blue +#define EthBlue "" // Blue +#define EthViolet "" // Purple +#define EthPurple "" // Purple +#define EthTeal "" // Cyan +#define EthCyan "" // Cyan + +#define EthBlackBold "" // Black +#define EthCoalBold "" // Black +#define EthGrayBold "" // White +#define EthWhiteBold "" // White +#define EthMaroonBold "" // Red +#define EthRedBold "" // Red +#define EthGreenBold "" // Green +#define EthLimeBold "" // Green +#define EthOrangeBold "" // Yellow +#define EthYellowBold "" // Yellow +#define EthNavyBold "" // Blue +#define EthBlueBold "" // Blue +#define EthVioletBold "" // Purple +#define EthPurpleBold "" // Purple +#define EthTealBold "" // Cyan +#define EthCyanBold "" // Cyan + + // Background +#define EthOnBlack "" // Black +#define EthOnCoal "" // Black +#define EthOnGray "" // White +#define EthOnWhite "" // White +#define EthOnMaroon "" // Red +#define EthOnRed "" // Red +#define EthOnGreen "" // Green +#define EthOnLime "" // Green +#define EthOnOrange "" // Yellow +#define EthOnYellow "" // Yellow +#define EthOnNavy "" // Blue +#define EthOnBlue "" // Blue +#define EthOnViolet "" // Purple +#define EthOnPurple "" // Purple +#define EthOnTeal "" // Cyan +#define EthOnCyan "" // Cyan + + // Underline +#define EthBlackUnder "" // Black +#define EthGrayUnder "" // White +#define EthMaroonUnder "" // Red +#define EthGreenUnder "" // Green +#define EthOrangeUnder "" // Yellow +#define EthNavyUnder "" // Blue +#define EthVioletUnder "" // Purple +#define EthTealUnder "" // Cyan #else diff --git a/libdevcrypto/MemoryDB.cpp b/libdevcrypto/MemoryDB.cpp index ab74173c4..a207bb7d6 100644 --- a/libdevcrypto/MemoryDB.cpp +++ b/libdevcrypto/MemoryDB.cpp @@ -49,7 +49,7 @@ std::string MemoryDB::lookup(h256 _h) const if (!m_enforceRefs || (m_refCount.count(it->first) && m_refCount.at(it->first))) return it->second; // else if (m_enforceRefs && m_refCount.count(it->first) && !m_refCount.at(it->first)) -// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h.abridged(); +// cnote << "Lookup required for value with no refs. Let's hope it's in the DB." << _h; } return std::string(); } @@ -67,7 +67,7 @@ void MemoryDB::insert(h256 _h, bytesConstRef _v) m_over[_h] = _v.toString(); m_refCount[_h]++; #if ETH_PARANOIA - dbdebug << "INST" << _h.abridged() << "=>" << m_refCount[_h]; + dbdebug << "INST" << _h << "=>" << m_refCount[_h]; #endif } @@ -82,15 +82,15 @@ bool MemoryDB::kill(h256 _h) { // If we get to this point, then there was probably a node in the level DB which we need to remove and which we have previously // used as part of the memory-based MemoryDB. Nothing to be worried about *as long as the node exists in the DB*. - dbdebug << "NOKILL-WAS" << _h.abridged(); + dbdebug << "NOKILL-WAS" << _h; return false; } - dbdebug << "KILL" << _h.abridged() << "=>" << m_refCount[_h]; + dbdebug << "KILL" << _h << "=>" << m_refCount[_h]; return true; } else { - dbdebug << "NOKILL" << _h.abridged(); + dbdebug << "NOKILL" << _h; return false; } #else diff --git a/libdevcrypto/OverlayDB.cpp b/libdevcrypto/OverlayDB.cpp index 5f8aea667..91f73ad49 100644 --- a/libdevcrypto/OverlayDB.cpp +++ b/libdevcrypto/OverlayDB.cpp @@ -103,7 +103,7 @@ void OverlayDB::kill(h256 _h) if (m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)_h.data(), 32), &ret); if (ret.empty()) - cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h.abridged(); + cnote << "Decreasing DB node ref count below zero with no DB node. Probably have a corrupt Trie." << _h; } #else MemoryDB::kill(_h); diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h index 68a2c248d..a707c30f0 100644 --- a/libdevcrypto/TrieDB.h +++ b/libdevcrypto/TrieDB.h @@ -120,14 +120,14 @@ public: if (_r.isList() && _r.itemCount() == 2 && (!_wasExt || _out)) { if (_out) - (*_out) << std::string(_indent * 2, ' ') << (_wasExt ? "!2 " : "2 ") << sha3(_r.data()).abridged() << ": " << _r << "\n"; + (*_out) << std::string(_indent * 2, ' ') << (_wasExt ? "!2 " : "2 ") << sha3(_r.data()) << ": " << _r << "\n"; if (!isLeaf(_r)) // don't go down leaves descendEntry(_r[1], _keyMask, true, _out, _indent + 1); } else if (_r.isList() && _r.itemCount() == 17) { if (_out) - (*_out) << std::string(_indent * 2, ' ') << "17 " << sha3(_r.data()).abridged() << ": " << _r << "\n"; + (*_out) << std::string(_indent * 2, ' ') << "17 " << sha3(_r.data()) << ": " << _r << "\n"; for (unsigned i = 0; i < 16; ++i) if (!_r[i].isEmpty()) // 16 branches are allowed to be empty descendEntry(_r[i], _keyMask, false, _out, _indent + 1); @@ -779,7 +779,7 @@ template std::string GenericTrieDB::atAux(RLP const& _here, Nibbl template bytes GenericTrieDB::mergeAt(RLP const& _orig, NibbleSlice _k, bytesConstRef _v, bool _inLine) { #if ETH_PARANOIA - tdebug << "mergeAt " << _orig << _k << sha3(_orig.data()).abridged(); + tdebug << "mergeAt " << _orig << _k << sha3(_orig.data()); #endif // The caller will make sure that the bytes are inserted properly. @@ -854,7 +854,7 @@ template bytes GenericTrieDB::mergeAt(RLP const& _orig, NibbleSli template void GenericTrieDB::mergeAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k, bytesConstRef _v) { #if ETH_PARANOIA - tdebug << "mergeAtAux " << _orig << _k << sha3(_orig.data()).abridged() << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash().abridged() : std::string()); + tdebug << "mergeAtAux " << _orig << _k << sha3(_orig.data()) << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash() : std::string()); #endif RLP r = _orig; @@ -902,7 +902,7 @@ template std::string GenericTrieDB::deref(RLP const& _n) const template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSlice _k) { #if ETH_PARANOIA - tdebug << "deleteAt " << _orig << _k << sha3(_orig.data()).abridged(); + tdebug << "deleteAt " << _orig << _k << sha3(_orig.data()); #endif // The caller will make sure that the bytes are inserted properly. @@ -1009,7 +1009,7 @@ template bytes GenericTrieDB::deleteAt(RLP const& _orig, NibbleSl template bool GenericTrieDB::deleteAtAux(RLPStream& _out, RLP const& _orig, NibbleSlice _k) { #if ETH_PARANOIA - tdebug << "deleteAtAux " << _orig << _k << sha3(_orig.data()).abridged() << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash().abridged() : std::string()); + tdebug << "deleteAtAux " << _orig << _k << sha3(_orig.data()) << ((_orig.isData() && _orig.size() <= 32) ? _orig.toHash() : std::string()); #endif bytes b = _orig.isEmpty() ? bytes() : deleteAt(_orig.isList() ? _orig : RLP(node(_orig.toHash())), _k); diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 53eabe349..891d3f97d 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -85,6 +85,27 @@ std::string ethash_cl_miner::platform_info(unsigned _platformId, unsigned _devic return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; } +unsigned ethash_cl_miner::get_num_devices(unsigned _platformId) +{ + std::vector platforms; + cl::Platform::get(&platforms); + if (platforms.empty()) + { + debugf("No OpenCL platforms found.\n"); + return 0; + } + + std::vector devices; + unsigned platform_num = std::min(_platformId, platforms.size() - 1); + platforms[platform_num].getDevices(CL_DEVICE_TYPE_ALL, &devices); + if (devices.empty()) + { + debugf("No OpenCL devices found.\n"); + return 0; + } + return devices.size(); +} + void ethash_cl_miner::finish() { if (m_queue()) diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index 3046f037b..21635df91 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -33,6 +33,8 @@ public: bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64, unsigned _platformId = 0, unsigned _deviceId = 0); static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); + static unsigned get_num_devices(unsigned _platformId = 0); + void finish(); void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 7ff35fd2b..c40ce2625 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -121,6 +121,8 @@ bool Ethash::verify(BlockInfo const& _header) return slow; } +unsigned Ethash::CPUMiner::s_numInstances = 1; + void Ethash::CPUMiner::workLoop() { auto tid = std::this_thread::get_id(); @@ -264,10 +266,11 @@ private: unsigned Ethash::GPUMiner::s_platformId = 0; unsigned Ethash::GPUMiner::s_deviceId = 0; +unsigned Ethash::GPUMiner::s_numInstances = 1; Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), - Worker("gpuminer"), + Worker("gpuminer" + toString(index())), m_hook(new EthashCLHook(this)) { } @@ -308,7 +311,8 @@ void Ethash::GPUMiner::workLoop() auto p = EthashAux::params(m_minerSeed); auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; - m_miner->init(p, cb, 32, s_platformId, s_deviceId); + unsigned device = instances() > 0 ? index() : s_deviceId; + m_miner->init(p, cb, 32, s_platformId, device); } uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); @@ -331,6 +335,11 @@ std::string Ethash::GPUMiner::platformInfo() return ethash_cl_miner::platform_info(s_platformId, s_deviceId); } +unsigned Ethash::GPUMiner::getNumDevices() +{ + return ethash_cl_miner::get_num_devices(s_platformId); +} + #endif } diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 2bbe7d649..9a66e9865 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -78,17 +78,18 @@ public: static bool preVerify(BlockInfo const& _header); static WorkPackage package(BlockInfo const& _header); static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + class CPUMiner: public Miner, Worker { public: CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - static unsigned instances() { return std::thread::hardware_concurrency(); } + static unsigned instances() { return s_numInstances > 0 ? s_numInstances : std::thread::hardware_concurrency(); } static std::string platformInfo(); static void setDefaultPlatform(unsigned) {} static void setDefaultDevice(unsigned) {} - + static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, std::thread::hardware_concurrency()); } protected: void kickOff() override { @@ -100,7 +101,7 @@ public: private: void workLoop() override; - static unsigned s_deviceId; + static unsigned s_numInstances; }; #if ETH_ETHASHCL || !ETH_TRUE @@ -112,11 +113,13 @@ public: GPUMiner(ConstructionInfo const& _ci); ~GPUMiner(); - static unsigned instances() { return 1; } + static unsigned instances() { return s_numInstances > 0 ? s_numInstances : 1; } static std::string platformInfo(); + static unsigned getNumDevices(); static void setDefaultPlatform(unsigned _id) { s_platformId = _id; } static void setDefaultDevice(unsigned _id) { s_deviceId = _id; } - + static void setNumInstances(unsigned _instances) { s_numInstances = std::min(_instances, getNumDevices()); } + protected: void kickOff() override; void pause() override; @@ -133,6 +136,7 @@ public: h256 m_minerSeed; ///< Last seed in m_miner static unsigned s_platformId; static unsigned s_deviceId; + static unsigned s_numInstances; }; #else using GPUMiner = CPUMiner; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 750d80082..44064cde6 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -79,7 +79,7 @@ h256 EthashAux::seedHash(unsigned _number) for (; n <= epoch; ++n, ret = sha3(ret)) { get()->m_seedHashes[n] = ret; -// cdebug << "Epoch" << n << "is" << ret.abridged(); +// cdebug << "Epoch" << n << "is" << ret; } } return get()->m_seedHashes[epoch]; @@ -95,12 +95,12 @@ ethash_params EthashAux::params(h256 const& _seedHash) } catch (...) { -// cdebug << "Searching for seedHash " << _seedHash.abridged(); +// cdebug << "Searching for seedHash " << _seedHash; for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} if (epoch == 2048) { std::ostringstream error; - error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); + error << "apparent block number for " << _seedHash << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 8bba5ff1b..fbc92a863 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -48,10 +48,17 @@ namespace js = json_spirit; #define ETH_CATCH 1 #define ETH_TIMED_IMPORTS 0 +#ifdef _WIN32 +const char* BlockChainDebug::name() { return EthBlue "8" EthWhite " <>"; } +const char* BlockChainWarn::name() { return EthBlue "8" EthOnRed EthBlackBold " X"; } +const char* BlockChainNote::name() { return EthBlue "8" EthBlue " i"; } +const char* BlockChainChat::name() { return EthBlue "8" EthWhite " o"; } +#else const char* BlockChainDebug::name() { return EthBlue "☍" EthWhite " ◇"; } const char* BlockChainWarn::name() { return EthBlue "☍" EthOnRed EthBlackBold " ✘"; } const char* BlockChainNote::name() { return EthBlue "☍" EthBlue " ℹ"; } const char* BlockChainChat::name() { return EthBlue "☍" EthWhite " ◌"; } +#endif std::ostream& dev::eth::operator<<(std::ostream& _out, BlockChain const& _bc) { @@ -247,7 +254,7 @@ void BlockChain::rebuild(std::string const& _path, std::function parent is" << bi.parentHash.abridged() << "; expected" << lastHash.abridged() << "#" << (d - 1); + cwarn << "DISJOINT CHAIN DETECTED; " << bi.hash() << "#" << d << " -> parent is" << bi.parentHash << "; expected" << lastHash << "#" << (d - 1); return; } lastHash = bi.hash(); @@ -280,16 +287,6 @@ bool contains(T const& _t, V const& _v) return false; } -inline string toString(h256s const& _bs) -{ - ostringstream out; - out << "[ "; - for (auto i: _bs) - out << i.abridged() << ", "; - out << "]"; - return out.str(); -} - LastHashes BlockChain::lastHashes(unsigned _n) const { Guard l(x_lastLastHashes); @@ -323,14 +320,14 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st } catch (dev::eth::UnknownParent) { - cwarn << "ODD: Import queue contains block with unknown parent." << boost::current_exception_diagnostic_information(); + cwarn << "ODD: Import queue contains block with unknown parent." << error << boost::current_exception_diagnostic_information(); // NOTE: don't reimport since the queue should guarantee everything in the right order. // Can't continue - chain bad. badBlocks.push_back(BlockInfo::headerHash(block)); } catch (Exception const& _e) { - cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << diagnostic_information(_e); + cnote << "Exception while importing block. Someone (Jeff? That you?) seems to be giving us dodgy blocks!" << error << diagnostic_information(_e); // NOTE: don't reimport since the queue should guarantee everything in the right order. // Can't continue - chain bad. badBlocks.push_back(BlockInfo::headerHash(block)); @@ -427,7 +424,7 @@ ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Import BOOST_THROW_EXCEPTION(FutureTime()); } - clog(BlockChainChat) << "Attempting import of " << bi.hash().abridged() << "..."; + clog(BlockChainChat) << "Attempting import of " << bi.hash() << "..."; #if ETH_TIMED_IMPORTS preliminaryChecks = t.elapsed(); @@ -704,7 +701,7 @@ void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) tuple BlockChain::treeRoute(h256 const& _from, h256 const& _to, bool _common, bool _pre, bool _post) const { -// cdebug << "treeRoute" << _from.abridged() << "..." << _to.abridged(); +// cdebug << "treeRoute" << _from << "..." << _to; if (!_from || !_to) return make_tuple(h256s(), h256(), 0); h256s ret; @@ -719,7 +716,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const ret.push_back(from); from = details(from).parent; fn--; -// cdebug << "from:" << fn << _from.abridged(); +// cdebug << "from:" << fn << _from; } h256 to = _to; while (fn < tn) @@ -728,7 +725,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const back.push_back(to); to = details(to).parent; tn--; -// cdebug << "to:" << tn << _to.abridged(); +// cdebug << "to:" << tn << _to; } for (;; from = details(from).parent, to = details(to).parent) { @@ -738,7 +735,7 @@ tuple BlockChain::treeRoute(h256 const& _from, h256 const back.push_back(to); fn--; tn--; -// cdebug << "from:" << fn << _from.abridged() << "; to:" << tn << _to.abridged(); +// cdebug << "from:" << fn << _from << "; to:" << tn << _to; if (from == to) break; if (!from) @@ -1010,7 +1007,7 @@ bytes BlockChain::block(h256 const& _hash) const if (d.empty()) { - cwarn << "Couldn't find requested block:" << _hash.abridged(); + cwarn << "Couldn't find requested block:" << _hash; return bytes(); } diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index b056dc74f..44ddda637 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -29,14 +29,18 @@ using namespace std; using namespace dev; using namespace dev::eth; +#ifdef _WIN32 +const char* BlockQueueChannel::name() { return EthOrange "[]>"; } +#else const char* BlockQueueChannel::name() { return EthOrange "▣┅▶"; } +#endif ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) { // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); - cblockq << "Queuing block" << h.abridged() << "for import..."; + cblockq << "Queuing block" << h << "for import..."; UpgradableGuard l(m_lock); @@ -95,7 +99,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, boo else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) { // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. - cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); + cblockq << "OK - queued as unknown parent:" << bi.parentHash; m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b9ddcdbaa..408f8ebca 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -124,10 +124,17 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, ActivityReport const& _r) return _out; } +#ifdef _WIN32 +const char* ClientNote::name() { return EthTeal "^" EthBlue " i"; } +const char* ClientChat::name() { return EthTeal "^" EthWhite " o"; } +const char* ClientTrace::name() { return EthTeal "^" EthGray " O"; } +const char* ClientDetail::name() { return EthTeal "^" EthCoal " 0"; } +#else const char* ClientNote::name() { return EthTeal "⧫" EthBlue " ℹ"; } const char* ClientChat::name() { return EthTeal "⧫" EthWhite " ◌"; } const char* ClientTrace::name() { return EthTeal "⧫" EthGray " ◎"; } const char* ClientDetail::name() { return EthTeal "⧫" EthCoal " ●"; } +#endif Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), @@ -291,7 +298,15 @@ static string filtersToString(T const& _fs) ret << "{"; unsigned i = 0; for (h256 const& f: _fs) - ret << (i++ ? ", " : "") << (f == PendingChangedFilter ? "pending" : f == ChainChangedFilter ? "chain" : f.abridged()); + { + ret << (i++ ? ", " : ""); + if (f == PendingChangedFilter) + ret << url << "pending"; + else if (f == ChainChangedFilter) + ret << url << "chain"; + else + ret << f; + } ret << "}"; return ret.str(); } @@ -475,7 +490,7 @@ void Client::onChainChanged(ImportRoute const& _ir) // insert transactions that we are declaring the dead part of the chain for (auto const& h: _ir.second) { - clog(ClientNote) << "Dead block:" << h.abridged(); + clog(ClientNote) << "Dead block:" << h; for (auto const& t: m_bc.transactions(h)) { clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); @@ -486,10 +501,10 @@ void Client::onChainChanged(ImportRoute const& _ir) // remove transactions from m_tq nicely rather than relying on out of date nonce later on. for (auto const& h: _ir.first) { - clog(ClientChat) << "Live block:" << h.abridged(); + clog(ClientChat) << "Live block:" << h; for (auto const& th: m_bc.transactionHashes(h)) { - clog(ClientNote) << "Safely dropping transaction " << th.abridged(); + clog(ClientNote) << "Safely dropping transaction " << th; m_tq.drop(th); } } diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index ae8567e2a..eac946230 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -311,7 +311,7 @@ Assembly& Assembly::optimise(bool _enable) unsigned total = 0; for (unsigned count = 1; count > 0; total += count) { - copt << *this; + copt << toString(*this); count = 0; copt << "Performing control flow analysis..."; diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp index cb39fc051..a2e1398b5 100644 --- a/libp2p/Common.cpp +++ b/libp2p/Common.cpp @@ -34,6 +34,19 @@ bool dev::p2p::NodeIPEndpoint::test_allowLocal = false; //⊳⊲◀▶■▣▢□▷◁▧▨▩▲◆◉◈◇◎●◍◌○◼☑☒☎☢☣☰☀♽♥♠✩✭❓✔✓✖✕✘✓✔✅⚒⚡⦸⬌∅⁕«««»»»⚙━┅┉▬ +#ifdef _WIN32 +const char* NetWarn::name() { return EthYellow "N" EthRed " X"; } +const char* NetImpolite::name() { return EthYellow "N" EthRed " !"; } +const char* NetNote::name() { return EthYellow "N" EthBlue " i"; } +const char* NetConnect::name() { return EthYellow "N" EthYellow " C"; } +const char* NetMessageSummary::name() { return EthYellow "N" EthWhite " ."; } +const char* NetMessageDetail::name() { return EthYellow "N" EthGray " o"; } +const char* NetTriviaSummary::name() { return EthYellow "N" EthGray " O"; } +const char* NetTriviaDetail::name() { return EthYellow "N" EthCoal " 0"; } +const char* NetAllDetail::name() { return EthYellow "N" EthCoal " A"; } +const char* NetRight::name() { return EthYellow "N" EthGreen "->"; } +const char* NetLeft::name() { return EthYellow "N" EthNavy "<-"; } +#else const char* NetWarn::name() { return EthYellow "⧎" EthRed " ✘"; } const char* NetImpolite::name() { return EthYellow "⧎" EthRed " !"; } const char* NetNote::name() { return EthYellow "⧎" EthBlue " ℹ"; } @@ -45,6 +58,7 @@ const char* NetTriviaDetail::name() { return EthYellow "⧎" EthCoal " ◍"; } const char* NetAllDetail::name() { return EthYellow "⧎" EthCoal " ●"; } const char* NetRight::name() { return EthYellow "⧎" EthGreen "▬▶"; } const char* NetLeft::name() { return EthYellow "⧎" EthNavy "◀▬"; } +#endif bool p2p::isPublicAddress(std::string const& _addressToCheck) { diff --git a/libp2p/Network.cpp b/libp2p/Network.cpp index 5702fbce7..1c780c5e9 100644 --- a/libp2p/Network.cpp +++ b/libp2p/Network.cpp @@ -228,7 +228,7 @@ bi::tcp::endpoint Network::resolveHost(string const& _addr) bi::tcp::resolver r(s_resolverIoService); auto it = r.resolve({split[0], toString(port)}, ec); if (ec) - clog(NetWarn) << "Error resolving host address " << _addr << ":" << ec.message(); + clog(NetWarn) << "Error resolving host address..." << url << _addr << ":" << error << ec.message(); else ep = *it; } diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp index 6d3483640..0c1beef04 100644 --- a/libp2p/NodeTable.cpp +++ b/libp2p/NodeTable.cpp @@ -87,7 +87,7 @@ shared_ptr NodeTable::addNode(Node const& _node) // we handle when tcp endpoint is 0 below if (_node.endpoint.address.to_string() == "0.0.0.0") { - clog(NodeTableWarn) << "addNode Failed. Invalid UDP address 0.0.0.0 for" << _node.id.abridged(); + clog(NodeTableWarn) << "addNode Failed. Invalid UDP address" << url << "0.0.0.0" << "for" << _node.id; return move(shared_ptr()); } diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 5f681205d..78b83d064 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -52,6 +52,7 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer const& baseSpecifier: getBaseContracts()) baseSpecifier->checkTypeRequirements(); + checkDuplicateFunctions(); checkIllegalOverrides(); checkAbstractFunctions(); checkAbstractConstructors(); @@ -87,6 +88,7 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer const& variable: m_stateVariables) variable->checkTypeRequirements(); + checkExternalTypeClashes(); // check for hash collisions in function signatures set> hashes; for (auto const& it: getInterfaceFunctionList()) @@ -131,6 +133,33 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const return nullptr; } +void ContractDefinition::checkDuplicateFunctions() const +{ + /// Checks that two functions with the same name defined in this contract have different + /// argument types and that there is at most one constructor. + map> functions; + for (ASTPointer const& function: getDefinedFunctions()) + functions[function->getName()].push_back(function.get()); + if (functions[getName()].size() > 1) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(getLocation()) << + errinfo_comment("More than one constructor defined.") + ); + for (auto const& it: functions) + { + vector const& overloads = it.second; + for (size_t i = 0; i < overloads.size(); ++i) + for (size_t j = i + 1; j < overloads.size(); ++j) + if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(overloads[j]->getLocation()) << + errinfo_comment("Function with same name and arguments already defined.") + ); + } +} + void ContractDefinition::checkAbstractFunctions() { map functions; @@ -171,7 +200,7 @@ void ContractDefinition::checkAbstractConstructors() for (auto const& modifier: constructor->getModifiers()) { auto baseContract = dynamic_cast( - modifier->getName()->getReferencedDeclaration() + &modifier->getName()->getReferencedDeclaration() ); if (baseContract) argumentsNeeded.erase(baseContract); @@ -181,7 +210,7 @@ void ContractDefinition::checkAbstractConstructors() for (ASTPointer const& base: contract->getBaseContracts()) { auto baseContract = dynamic_cast( - base->getName()->getReferencedDeclaration() + &base->getName()->getReferencedDeclaration() ); solAssert(baseContract, ""); if (!base->getArguments().empty()) @@ -196,7 +225,7 @@ void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier // into the types - map functions; + map> functions; map modifiers; // We search from derived to base, so the stored item causes the error. @@ -209,28 +238,67 @@ void ContractDefinition::checkIllegalOverrides() const string const& name = function->getName(); if (modifiers.count(name)) BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); - FunctionDefinition const*& override = functions[name]; - if (!override) - override = function.get(); - else if (override->getVisibility() != function->getVisibility() || - override->isDeclaredConst() != function->isDeclaredConst() || - FunctionType(*override) != FunctionType(*function)) - BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); + FunctionType functionType(*function); + // function should not change the return type + for (FunctionDefinition const* overriding: functions[name]) + { + FunctionType overridingType(*overriding); + if (!overridingType.hasEqualArgumentTypes(functionType)) + continue; + if ( + overriding->getVisibility() != function->getVisibility() || + overriding->isDeclaredConst() != function->isDeclaredConst() || + overridingType != functionType + ) + BOOST_THROW_EXCEPTION(overriding->createTypeError("Override changes extended function signature.")); + } + functions[name].push_back(function.get()); } for (ASTPointer const& modifier: contract->getFunctionModifiers()) { string const& name = modifier->getName(); - if (functions.count(name)) - BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function.")); ModifierDefinition const*& override = modifiers[name]; if (!override) override = modifier.get(); else if (ModifierType(*override) != ModifierType(*modifier)) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); + if (!functions[name].empty()) + BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); } } } +void ContractDefinition::checkExternalTypeClashes() const +{ + map>>> externalDeclarations; + for (ContractDefinition const* contract: getLinearizedBaseContracts()) + { + for (ASTPointer const& f: contract->getDefinedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared(*f); + externalDeclarations[functionType->externalSignature(f->getName())].push_back( + make_pair(f.get(), functionType) + ); + } + for (ASTPointer const& v: contract->getStateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared(*v); + externalDeclarations[functionType->externalSignature(v->getName())].push_back( + make_pair(v.get(), functionType) + ); + } + } + for (auto const& it: externalDeclarations) + for (size_t i = 0; i < it.second.size(); ++i) + for (size_t j = i + 1; j < it.second.size(); ++j) + if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) + BOOST_THROW_EXCEPTION(it.second[j].first->createTypeError( + "Function overload clash during conversion to external types for arguments." + )); +} + std::vector> const& ContractDefinition::getInterfaceEvents() const { if (!m_interfaceEvents) @@ -253,16 +321,21 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn if (!m_interfaceFunctionList) { set functionsSeen; + set signaturesSeen; m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) - if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) + { + string functionSignature = f->externalSignature(); + if (f->isPartOfExternalInterface() && signaturesSeen.count(functionSignature) == 0) { functionsSeen.insert(f->getName()); - FixedHash<4> hash(dev::sha3(f->externalSignature())); + signaturesSeen.insert(functionSignature); + FixedHash<4> hash(dev::sha3(functionSignature)); m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } + } for (ASTPointer const& v: contract->getStateVariables()) if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface()) @@ -314,11 +387,11 @@ TypePointer EnumValue::getType(ContractDefinition const*) const void InheritanceSpecifier::checkTypeRequirements() { - m_baseName->checkTypeRequirements(); + m_baseName->checkTypeRequirements(nullptr); for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + argument->checkTypeRequirements(nullptr); - ContractDefinition const* base = dynamic_cast(m_baseName->getReferencedDeclaration()); + ContractDefinition const* base = dynamic_cast(&m_baseName->getReferencedDeclaration()); solAssert(base, "Base contract not available."); TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes(); if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size()) @@ -428,7 +501,8 @@ void VariableDeclaration::checkTypeRequirements() if (!m_value) // This feature might be extended in the future. BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection.")); - m_value->checkTypeRequirements(); + m_value->checkTypeRequirements(nullptr); + TypePointer type = m_value->getType(); if (type->getCategory() == Type::Category::IntegerConstant) { @@ -468,18 +542,22 @@ void ModifierDefinition::checkTypeRequirements() void ModifierInvocation::checkTypeRequirements(vector const& _bases) { - m_modifierName->checkTypeRequirements(); + TypePointers argumentTypes; for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + { + argument->checkTypeRequirements(nullptr); + argumentTypes.push_back(argument->getType()); + } + m_modifierName->checkTypeRequirements(&argumentTypes); - auto declaration = m_modifierName->getReferencedDeclaration(); + auto const* declaration = &m_modifierName->getReferencedDeclaration(); vector> emptyParameterList; vector> const* parameters = nullptr; if (auto modifier = dynamic_cast(declaration)) parameters = &modifier->getParameters(); else // check parameters for Base constructors - for (auto const* base: _bases) + for (ContractDefinition const* base: _bases) if (declaration == base) { if (auto referencedConstructor = base->getConstructor()) @@ -563,9 +641,9 @@ void VariableDeclarationStatement::checkTypeRequirements() m_variable->checkTypeRequirements(); } -void Assignment::checkTypeRequirements() +void Assignment::checkTypeRequirements(TypePointers const*) { - m_leftHandSide->checkTypeRequirements(); + m_leftHandSide->checkTypeRequirements(nullptr); m_leftHandSide->requireLValue(); if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping) BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); @@ -575,7 +653,7 @@ void Assignment::checkTypeRequirements() else { // compound assignment - m_rightHandSide->checkTypeRequirements(); + m_rightHandSide->checkTypeRequirements(nullptr); TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), m_rightHandSide->getType()); if (!resultType || *resultType != *m_type) @@ -588,7 +666,7 @@ void Assignment::checkTypeRequirements() void ExpressionStatement::checkTypeRequirements() { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); 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.")); @@ -596,7 +674,7 @@ void ExpressionStatement::checkTypeRequirements() void Expression::expectType(Type const& _expectedType) { - checkTypeRequirements(); + checkTypeRequirements(nullptr); Type const& type = *getType(); if (!type.isImplicitlyConvertibleTo(_expectedType)) BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + @@ -611,10 +689,10 @@ void Expression::requireLValue() m_lvalueRequested = true; } -void UnaryOperation::checkTypeRequirements() +void UnaryOperation::checkTypeRequirements(TypePointers const*) { // Inc, Dec, Add, Sub, Not, BitNot, Delete - m_subExpression->checkTypeRequirements(); + m_subExpression->checkTypeRequirements(nullptr); 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); @@ -622,10 +700,10 @@ void UnaryOperation::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); } -void BinaryOperation::checkTypeRequirements() +void BinaryOperation::checkTypeRequirements(TypePointers const*) { - m_left->checkTypeRequirements(); - m_right->checkTypeRequirements(); + m_left->checkTypeRequirements(nullptr); + m_right->checkTypeRequirements(nullptr); m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); if (!m_commonType) BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + @@ -635,11 +713,22 @@ void BinaryOperation::checkTypeRequirements() m_type = Token::isCompareOp(m_operator) ? make_shared() : m_commonType; } -void FunctionCall::checkTypeRequirements() +void FunctionCall::checkTypeRequirements(TypePointers const*) { - m_expression->checkTypeRequirements(); + bool isPositionalCall = m_names.empty(); + + // we need to check arguments' type first as they will be forwarded to + // m_expression->checkTypeRequirements + TypePointers argumentTypes; for (ASTPointer const& argument: m_arguments) - argument->checkTypeRequirements(); + { + argument->checkTypeRequirements(nullptr); + // only store them for positional calls + if (isPositionalCall) + argumentTypes.push_back(argument->getType()); + } + + m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr); Type const* expressionType = m_expression->getType().get(); if (isTypeConversion()) @@ -649,7 +738,7 @@ void FunctionCall::checkTypeRequirements() // number of non-mapping members if (m_arguments.size() != 1) BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); - if (!m_names.empty()) + if (!isPositionalCall) 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.")); @@ -664,8 +753,9 @@ void FunctionCall::checkTypeRequirements() if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); - if (m_names.empty()) + if (isPositionalCall) { + // call by positional arguments for (size_t i = 0; i < m_arguments.size(); ++i) if (!functionType->takesArbitraryParameters() && !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) @@ -673,6 +763,7 @@ void FunctionCall::checkTypeRequirements() } else { + // call by named arguments if (functionType->takesArbitraryParameters()) BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " "that take arbitrary parameters.")); @@ -719,10 +810,10 @@ bool FunctionCall::isTypeConversion() const return m_expression->getType()->getCategory() == Type::Category::TypeType; } -void NewExpression::checkTypeRequirements() +void NewExpression::checkTypeRequirements(TypePointers const*) { - m_contractName->checkTypeRequirements(); - m_contract = dynamic_cast(m_contractName->getReferencedDeclaration()); + m_contractName->checkTypeRequirements(nullptr); + m_contract = dynamic_cast(&m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); if (!m_contract->isFullyImplemented()) @@ -733,15 +824,37 @@ void NewExpression::checkTypeRequirements() FunctionType::Location::Creation); } -void MemberAccess::checkTypeRequirements() +void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); Type const& type = *m_expression->getType(); - m_type = type.getMemberType(*m_memberName); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " - "visible in " + type.toString())); - // This should probably move somewhere else. + + MemberList::MemberMap possibleMembers = type.getMembers().membersByName(*m_memberName); + if (possibleMembers.size() > 1 && _argumentTypes) + { + // do override resolution + for (auto it = possibleMembers.begin(); it != possibleMembers.end();) + if ( + it->type->getCategory() == Type::Category::Function && + !dynamic_cast(*it->type).canTakeArguments(*_argumentTypes) + ) + it = possibleMembers.erase(it); + else + ++it; + } + if (possibleMembers.size() == 0) + BOOST_THROW_EXCEPTION(createTypeError( + "Member \"" + *m_memberName + "\" not found or not visible " + "after argument-dependent lookup in " + type.toString() + )); + else if (possibleMembers.size() > 1) + BOOST_THROW_EXCEPTION(createTypeError( + "Member \"" + *m_memberName + "\" not unique " + "after argument-dependent lookup in " + type.toString() + )); + + m_referencedDeclaration = possibleMembers.front().declaration; + m_type = possibleMembers.front().type; if (type.getCategory() == Type::Category::Struct) m_isLValue = true; else if (type.getCategory() == Type::Category::Array) @@ -754,9 +867,9 @@ void MemberAccess::checkTypeRequirements() m_isLValue = false; } -void IndexAccess::checkTypeRequirements() +void IndexAccess::checkTypeRequirements(TypePointers const*) { - m_base->checkTypeRequirements(); + m_base->checkTypeRequirements(nullptr); switch (m_base->getType()->getCategory()) { case Type::Category::Array: @@ -789,7 +902,7 @@ void IndexAccess::checkTypeRequirements() m_type = make_shared(make_shared(ArrayType::Location::Memory, type.getActualType())); else { - m_index->checkTypeRequirements(); + m_index->checkTypeRequirements(nullptr); auto length = dynamic_cast(m_index->getType().get()); if (!length) BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); @@ -804,22 +917,57 @@ void IndexAccess::checkTypeRequirements() } } -void Identifier::checkTypeRequirements() +void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) { - solAssert(m_referencedDeclaration, "Identifier not resolved."); - + if (!m_referencedDeclaration) + { + if (!_argumentTypes) + BOOST_THROW_EXCEPTION(createTypeError("Unable to determine overloaded type.")); + overloadResolution(*_argumentTypes); + } + solAssert(!!m_referencedDeclaration, "Referenced declaration is null after overload resolution."); m_isLValue = m_referencedDeclaration->isLValue(); m_type = m_referencedDeclaration->getType(m_currentContract); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); } -void ElementaryTypeNameExpression::checkTypeRequirements() +Declaration const& Identifier::getReferencedDeclaration() const +{ + solAssert(!!m_referencedDeclaration, "Identifier not resolved."); + return *m_referencedDeclaration; +} + +void Identifier::overloadResolution(TypePointers const& _argumentTypes) +{ + solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); + solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found."); + + std::vector possibles; + if (m_overloadedDeclarations.size() == 1) + m_referencedDeclaration = *m_overloadedDeclarations.begin(); + + for (Declaration const* declaration: m_overloadedDeclarations) + { + TypePointer const& function = declaration->getType(); + auto const* functionType = dynamic_cast(function.get()); + if (functionType && functionType->canTakeArguments(_argumentTypes)) + possibles.push_back(declaration); + } + if (possibles.size() == 1) + m_referencedDeclaration = possibles.front(); + else if (possibles.empty()) + BOOST_THROW_EXCEPTION(createTypeError("No matching declaration found after argument-dependent lookup.")); + else + BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup.")); +} + +void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) { m_type = make_shared(Type::fromElementaryTypeName(m_typeToken)); } -void Literal::checkTypeRequirements() +void Literal::checkTypeRequirements(TypePointers const*) { m_type = Type::forLiteral(*this); if (!m_type) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 0c133ff1a..c9ad6447e 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -143,8 +143,8 @@ public: 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; } - bool isVisibleInContract() const { return getVisibility() != Visibility::External; } - virtual bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } + virtual bool isVisibleInContract() const { return getVisibility() != Visibility::External; } + bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; } /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. @@ -156,7 +156,7 @@ public: /// contract types. virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual bool isLValue() const { return false; } - virtual bool isPartOfExternalInterface() const { return false; }; + virtual bool isPartOfExternalInterface() const { return false; } protected: virtual Visibility getDefaultVisibility() const { return Visibility::Public; } @@ -282,9 +282,15 @@ public: FunctionDefinition const* getFallbackFunction() const; private: + /// Checks that two functions defined in this contract with the same name have different + /// arguments and that there is at most one constructor. + void checkDuplicateFunctions() const; void checkIllegalOverrides() const; void checkAbstractFunctions(); void checkAbstractConstructors(); + /// Checks that different functions with external visibility end up having different + /// external argument types (i.e. different signature). + void checkExternalTypeClashes() const; std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; @@ -437,10 +443,9 @@ public: ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block const& getBody() const { return *m_body; } - virtual bool isVisibleInDerivedContracts() const override + virtual bool isVisibleInContract() const override { - return !isConstructor() && !getName().empty() && isVisibleInContract() && - getVisibility() >= Visibility::Internal; + return Declaration::isVisibleInContract() && !isConstructor() && !getName().empty(); } virtual TypePointer getType(ContractDefinition const*) const override; virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); } @@ -968,7 +973,10 @@ class Expression: public ASTNode { public: Expression(SourceLocation const& _location): ASTNode(_location) {} - virtual void checkTypeRequirements() = 0; + /// Performs type checking after which m_type should be set. + /// @arg _argumentTypes if set, provides the argument types for the case that this expression + /// is used in the context of a call, used for function overload resolution. + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) = 0; std::shared_ptr const& getType() const { return m_type; } bool isLValue() const { return m_isLValue; } @@ -1007,7 +1015,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getLeftHandSide() const { return *m_leftHandSide; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; } @@ -1035,7 +1043,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -1062,7 +1070,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getLeftExpression() const { return *m_left; } Expression const& getRightExpression() const { return *m_right; } @@ -1090,7 +1098,7 @@ public: Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getExpression() const { return *m_expression; } std::vector> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; } @@ -1116,7 +1124,7 @@ public: Expression(_location), m_contractName(_contractName) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; /// Returns the referenced contract. Can only be called after type checking. ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; } @@ -1140,11 +1148,18 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& getExpression() const { return *m_expression; } ASTString const& getMemberName() const { return *m_memberName; } - virtual void checkTypeRequirements() override; + /// @returns the declaration referenced by this expression. Might return nullptr even if the + /// expression is valid, e.g. if the member does not correspond to an AST node. + Declaration const* referencedDeclaration() const { return m_referencedDeclaration; } + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; private: ASTPointer m_expression; ASTPointer m_memberName; + + /// Pointer to the referenced declaration, this is sometimes needed to resolve function over + /// loads in the type-checking phase. + Declaration const* m_referencedDeclaration = nullptr; }; /** @@ -1158,7 +1173,7 @@ public: Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& getBaseExpression() const { return *m_base; } Expression const* getIndexExpression() const { return m_index.get(); } @@ -1188,18 +1203,31 @@ public: PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration const& _referencedDeclaration, - ContractDefinition const* _currentContract = nullptr) + void setReferencedDeclaration( + Declaration const& _referencedDeclaration, + ContractDefinition const* _currentContract = nullptr + ) { m_referencedDeclaration = &_referencedDeclaration; m_currentContract = _currentContract; } - Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } - ContractDefinition const* getCurrentContract() const { return m_currentContract; } + Declaration const& getReferencedDeclaration() const; + + /// Stores a set of possible declarations referenced by this identifier. Has to be resolved + /// providing argument types using overloadResolution before the referenced declaration + /// is accessed. + void setOverloadedDeclarations(std::set const& _declarations) + { + m_overloadedDeclarations = _declarations; + } + + /// Tries to find exactly one of the possible referenced declarations provided the given + /// argument types in a call context. + void overloadResolution(TypePointers const& _argumentTypes); private: ASTPointer m_name; @@ -1209,6 +1237,8 @@ private: /// Stores a reference to the current contract. This is needed because types of base contracts /// change depending on the context. ContractDefinition const* m_currentContract = nullptr; + /// A set of overloaded declarations, right now only FunctionDefinition has overloaded declarations. + std::set m_overloadedDeclarations; }; /** @@ -1226,7 +1256,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getTypeToken() const { return m_typeToken; } @@ -1260,7 +1290,7 @@ public: 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; + virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getToken() const { return m_token; } /// @returns the non-parsed value of the literal diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index f0197e23b..37b577ccd 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -90,7 +90,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp for (auto const& modifier: constructor->getModifiers()) { auto baseContract = dynamic_cast( - modifier->getName()->getReferencedDeclaration()); + &modifier->getName()->getReferencedDeclaration()); if (baseContract) if (m_baseArguments.count(baseContract->getConstructor()) == 0) m_baseArguments[baseContract->getConstructor()] = &modifier->getArguments(); @@ -99,7 +99,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp for (ASTPointer const& base: contract->getBaseContracts()) { ContractDefinition const* baseContract = dynamic_cast( - base->getName()->getReferencedDeclaration()); + &base->getName()->getReferencedDeclaration()); solAssert(baseContract, ""); if (m_baseArguments.count(baseContract->getConstructor()) == 0) @@ -542,7 +542,7 @@ void Compiler::appendModifierOrFunctionCode() ASTPointer const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth]; // constructor call should be excluded - if (dynamic_cast(modifierInvocation->getName()->getReferencedDeclaration())) + if (dynamic_cast(&modifierInvocation->getName()->getReferencedDeclaration())) { ++m_modifierDepth; appendModifierOrFunctionCode(); diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 7cade367c..f373fdfb0 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -102,23 +102,13 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _dec eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _function.getName()) - return getFunctionEntryLabel(*function); - solAssert(false, "Virtual function " + _function.getName() + " not found."); - return m_asm.newTag(); // not reached + return getVirtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin()); } -eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base) +eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base) { - auto it = getSuperContract(_base); - for (; it != m_inheritanceHierarchy.end(); ++it) - for (ASTPointer const& function: (*it)->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _name) - return getFunctionEntryLabel(*function); - solAssert(false, "Super function " + _name + " not found."); - return m_asm.newTag(); // not reached + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + return getVirtualFunctionEntryLabel(_function, getSuperContract(_base)); } FunctionDefinition const* CompilerContext::getNextConstructor(ContractDefinition const& _contract) const @@ -190,6 +180,26 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) updateSourceLocation(); } +eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel( + FunctionDefinition const& _function, + vector::const_iterator _searchStart +) +{ + string name = _function.getName(); + FunctionType functionType(_function); + auto it = _searchStart; + for (; it != m_inheritanceHierarchy.end(); ++it) + for (ASTPointer const& function: (*it)->getDefinedFunctions()) + if ( + function->getName() == name && + !function->isConstructor() && + FunctionType(*function).hasEqualArgumentTypes(functionType) + ) + return getFunctionEntryLabel(*function); + solAssert(false, "Super function " + name + " not found."); + return m_asm.newTag(); // not reached +} + vector::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const { solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 9c2156bfa..34a3f97cd 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -63,9 +63,9 @@ public: void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @returns the entry label of the given function and takes overrides into account. eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function); - /// @returns the entry label of function with the given name from the most derived class just + /// @returns the entry label of a function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. - eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base); + eth::AssemblyItem getSuperFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base); FunctionDefinition const* getNextConstructor(ContractDefinition const& _contract) const; /// @returns the set of functions for which we still need to generate code @@ -141,6 +141,13 @@ public: }; private: + /// @returns the entry label of the given function - searches the inheritance hierarchy + /// startig from the given point towards the base. + eth::AssemblyItem getVirtualFunctionEntryLabel( + FunctionDefinition const& _function, + std::vector::const_iterator _searchStart + ); + /// @returns an iterator to the contract directly above the given contract. std::vector::const_iterator getSuperContract(const ContractDefinition &_contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); diff --git a/libsolidity/DeclarationContainer.cpp b/libsolidity/DeclarationContainer.cpp index 2594d4281..5f8d24e34 100644 --- a/libsolidity/DeclarationContainer.cpp +++ b/libsolidity/DeclarationContainer.cpp @@ -22,11 +22,11 @@ #include #include +#include -namespace dev -{ -namespace solidity -{ +using namespace std; +using namespace dev; +using namespace dev::solidity; bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update) { @@ -34,17 +34,34 @@ bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, if (name.empty()) return true; - if (!_update && (m_declarations.count(name) || m_invisibleDeclarations.count(name))) - return false; + if (_update) + { + solAssert(!dynamic_cast(&_declaration), "Attempt to update function definition."); + m_declarations[name].clear(); + m_invisibleDeclarations[name].clear(); + } + else + { + if (dynamic_cast(&_declaration)) + { + // check that all other declarations with the same name are functions + for (auto&& declaration: m_invisibleDeclarations[name] + m_declarations[name]) + if (!dynamic_cast(declaration)) + return false; + } + else if (m_declarations.count(name) > 0 || m_invisibleDeclarations.count(name) > 0) + return false; + } if (_invisible) - m_invisibleDeclarations.insert(name); + m_invisibleDeclarations[name].insert(&_declaration); else - m_declarations[name] = &_declaration; + m_declarations[name].insert(&_declaration); + return true; } -Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +set DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); auto result = m_declarations.find(_name); @@ -52,8 +69,5 @@ Declaration const* DeclarationContainer::resolveName(ASTString const& _name, boo return result->second; if (_recursive && m_enclosingContainer) return m_enclosingContainer->resolveName(_name, true); - return nullptr; -} - -} + return set({}); } diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h index f70881f5b..35a6ea077 100644 --- a/libsolidity/DeclarationContainer.h +++ b/libsolidity/DeclarationContainer.h @@ -48,15 +48,15 @@ public: /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false); - Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const; + std::set resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; } - std::map const& getDeclarations() const { return m_declarations; } + std::map> const& getDeclarations() const { return m_declarations; } private: Declaration const* m_enclosingDeclaration; DeclarationContainer const* m_enclosingContainer; - std::map m_declarations; - std::set m_invisibleDeclarations; + std::map> m_declarations; + std::map> m_invisibleDeclarations; }; } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 7ea71a7a4..cf6a01ec1 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -626,13 +626,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) bool alsoSearchInteger = false; ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); if (type.isSuper()) - m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag(); + { + solAssert(!!_memberAccess.referencedDeclaration(), "Referenced declaration not resolved."); + m_context << m_context.getSuperFunctionEntryLabel( + dynamic_cast(*_memberAccess.referencedDeclaration()), + type.getContractDefinition() + ).pushTag(); + } else { // ordinary contract type - u256 identifier = type.getFunctionIdentifier(member); - if (identifier != Invalid256) + if (Declaration const* declaration = _memberAccess.referencedDeclaration()) { + u256 identifier; + if (auto const* variable = dynamic_cast(declaration)) + identifier = FunctionType(*variable).externalIdentifier(); + else if (auto const* function = dynamic_cast(declaration)) + identifier = FunctionType(*function).externalIdentifier(); + else + solAssert(false, "Contract member is neither variable nor function."); appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::Address), true); m_context << identifier; } @@ -708,19 +720,16 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::TypeType: { TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - if (!type.getMembers().getMemberType(member)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); + solAssert( + !type.getMembers().membersByName(_memberAccess.getMemberName()).empty(), + "Invalid member access to " + type.toString() + ); - if (auto contractType = dynamic_cast(type.getActualType().get())) + if (dynamic_cast(type.getActualType().get())) { - ContractDefinition const& contract = contractType->getContractDefinition(); - for (ASTPointer const& function: contract.getDefinedFunctions()) - if (function->getName() == member) - { - m_context << m_context.getFunctionEntryLabel(*function).pushTag(); - return; - } - solAssert(false, "Function not found in member access."); + auto const* function = dynamic_cast(_memberAccess.referencedDeclaration()); + solAssert(!!function, "Function not found in member access"); + m_context << m_context.getFunctionEntryLabel(*function).pushTag(); } else if (auto enumType = dynamic_cast(type.getActualType().get())) m_context << enumType->getMemberValue(_memberAccess.getMemberName()); @@ -805,7 +814,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { CompilerContext::LocationSetter locationSetter(m_context, _identifier); - Declaration const* declaration = _identifier.getReferencedDeclaration(); + Declaration const* declaration = &_identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { switch (magicVar->getType()->getCategory()) @@ -1066,7 +1075,13 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio m_context << eth::Instruction::POP; m_context << eth::Instruction::POP; // pop contract address - if (firstType) + if (_functionType.getLocation() == FunctionType::Location::RIPEMD160) + { + // fix: built-in contract returns right-aligned data + CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true); + appendTypeConversion(IntegerType(160), FixedBytesType(20)); + } + else if (firstType) CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true); } diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index 68428a0bf..bb68a663f 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -193,10 +193,10 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc for (auto const& member: structType.getMembers()) { // assign each member that is not a mapping - TypePointer const& memberType = member.second; + TypePointer const& memberType = member.type; if (memberType->getCategory() == Type::Category::Mapping) continue; - pair const& offsets = structType.getStorageOffsetsOfMember(member.first); + pair const& offsets = structType.getStorageOffsetsOfMember(member.name); m_context << offsets.first << u256(offsets.second) << eth::Instruction::DUP6 << eth::Instruction::DUP3 @@ -247,10 +247,10 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const for (auto const& member: structType.getMembers()) { // zero each member that is not a mapping - TypePointer const& memberType = member.second; + TypePointer const& memberType = member.type; if (memberType->getCategory() == Type::Category::Mapping) continue; - pair const& offsets = structType.getStorageOffsetsOfMember(member.first); + pair const& offsets = structType.getStorageOffsetsOfMember(member.name); m_context << offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD << u256(offsets.second); diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index f6ee2f1d0..a286934a9 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -31,7 +31,7 @@ namespace dev namespace solidity { -NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) +NameAndTypeResolver::NameAndTypeResolver(vector const& _globals) { for (Declaration const* declaration: _globals) m_scopes[nullptr].registerDeclaration(*declaration); @@ -53,8 +53,9 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[&_contract]; linearizeBaseContracts(_contract); + // we first import non-functions only as we do not yet know the argument types for (ContractDefinition const* base: _contract.getLinearizedBaseContracts()) - importInheritedScope(*base); + importInheritedScope(*base, false); // import non-functions for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); @@ -64,6 +65,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver resolver(*variable, *this, &_contract, nullptr); for (ASTPointer const& event: _contract.getEvents()) ReferencesResolver resolver(*event, *this, &_contract, nullptr); + + // these can contain code, only resolve parameters for now for (ASTPointer const& modifier: _contract.getFunctionModifiers()) { m_currentScope = &m_scopes[modifier.get()]; @@ -75,6 +78,28 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) ReferencesResolver referencesResolver(*function, *this, &_contract, function->getReturnParameterList().get()); } + + m_currentScope = &m_scopes[&_contract]; + for (ContractDefinition const* base: _contract.getLinearizedBaseContracts()) + importInheritedScope(*base, true); // import functions + + // now resolve references inside the code + for (ASTPointer const& modifier: _contract.getFunctionModifiers()) + { + m_currentScope = &m_scopes[modifier.get()]; + ReferencesResolver resolver(*modifier, *this, &_contract, nullptr, true); + } + for (ASTPointer const& function: _contract.getDefinedFunctions()) + { + m_currentScope = &m_scopes[function.get()]; + ReferencesResolver referencesResolver( + *function, + *this, + &_contract, + function->getReturnParameterList().get(), + true + ); + } } void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) @@ -90,31 +115,51 @@ void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope."); } -Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +set NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) - return nullptr; + return set({}); return iterator->second.resolveName(_name, false); } -Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +set NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) +void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base, bool _importFunctions) { auto iterator = m_scopes.find(&_base); solAssert(iterator != end(m_scopes), ""); for (auto const& nameAndDeclaration: iterator->second.getDeclarations()) - { - Declaration const* declaration = nameAndDeclaration.second; - // Import if it was declared in the base, is not the constructor and is visible in derived classes - if (declaration->getScope() == &_base && declaration->getName() != _base.getName() && - declaration->isVisibleInDerivedContracts()) - m_currentScope->registerDeclaration(*declaration); - } + for (auto const& declaration: nameAndDeclaration.second) + // Import if it was declared in the base, is not the constructor and is visible in derived classes + if (declaration->getScope() == &_base && declaration->isVisibleInDerivedContracts()) + { + auto function = dynamic_cast(declaration); + if ((function == nullptr) == _importFunctions) + continue; + if (!!function) + { + FunctionType functionType(*function); + // only import if a function with the same arguments does not exist yet + bool functionWithEqualArgumentsFound = false; + for (auto knownDeclaration: m_currentScope->resolveName(nameAndDeclaration.first)) + { + auto knownFunction = dynamic_cast(knownDeclaration); + if (!knownFunction) + continue; // this is not legal, but will be caught later + if (!FunctionType(*knownFunction).hasEqualArgumentTypes(functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (functionWithEqualArgumentsFound) + continue; + } + m_currentScope->registerDeclaration(*declaration); + } } void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const @@ -125,8 +170,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) for (ASTPointer const& baseSpecifier: _contract.getBaseContracts()) { ASTPointer baseName = baseSpecifier->getName(); - ContractDefinition const* base = dynamic_cast( - baseName->getReferencedDeclaration()); + auto base = dynamic_cast(&baseName->getReferencedDeclaration()); if (!base) BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected.")); // "push_front" has the effect that bases mentioned later can overwrite members of bases @@ -318,11 +362,19 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio enterNewSubScope(_declaration); } -ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, bool _allowLazyTypes): - m_resolver(_resolver), m_currentContract(_currentContract), - m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) +ReferencesResolver::ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode, + bool _allowLazyTypes +): + m_resolver(_resolver), + m_currentContract(_currentContract), + m_returnParameters(_returnParameters), + m_resolveInsideCode(_resolveInsideCode), + m_allowLazyTypes(_allowLazyTypes) { _root.accept(*this); } @@ -361,24 +413,37 @@ bool ReferencesResolver::visit(Mapping&) bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) { - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); - if (!declaration) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) - << errinfo_comment("Undeclared identifier.")); - _typeName.setReferencedDeclaration(*declaration); + auto declarations = m_resolver.getNameFromCurrentScope(_typeName.getName()); + if (declarations.empty()) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.getLocation()) << + errinfo_comment("Undeclared identifier.") + ); + else if (declarations.size() > 1) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.getLocation()) << + errinfo_comment("Duplicate identifier.") + ); + else + _typeName.setReferencedDeclaration(**declarations.begin()); return false; } bool ReferencesResolver::visit(Identifier& _identifier) { - Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (!declaration) + auto declarations = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (declarations.empty()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) << errinfo_comment("Undeclared identifier.")); - _identifier.setReferencedDeclaration(*declaration, m_currentContract); + else if (declarations.size() == 1) + _identifier.setReferencedDeclaration(**declarations.begin(), m_currentContract); + else + // Duplicate declaration will be checked in checkTypeRequirements() + _identifier.setOverloadedDeclarations(declarations); return false; } - } } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 63b8ab637..6528bbef2 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -56,18 +56,19 @@ public: /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the contract). /// @returns a pointer to the declaration on success or nullptr on failure. - Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; + std::set resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; /// Resolves a name in the "current" scope. Should only be called during the initial /// resolving phase. - Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + std::set getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); private: void reset(); - /// Imports all members declared directly in the given contract (i.e. does not import inherited - /// members) into the current scope if they are not present already. - void importInheritedScope(ContractDefinition const& _base); + /// Either imports all non-function members or all function members declared directly in the + /// given contract (i.e. does not import inherited members) into the current scope if they are + ///not present already. + void importInheritedScope(ContractDefinition const& _base, bool _importFunctions); /// Computes "C3-Linearization" of base contracts and stores it inside the contract. void linearizeBaseContracts(ContractDefinition& _contract) const; @@ -126,13 +127,18 @@ private: class ReferencesResolver: private ASTVisitor { public: - ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, - bool _allowLazyTypes = true); + ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode = false, + bool _allowLazyTypes = true + ); private: virtual void endVisit(VariableDeclaration& _variable) override; + virtual bool visit(Block&) override { return m_resolveInsideCode; } virtual bool visit(Identifier& _identifier) override; virtual bool visit(UserDefinedTypeName& _typeName) override; virtual bool visit(Mapping&) override; @@ -141,6 +147,7 @@ private: NameAndTypeResolver& m_resolver; ContractDefinition const* m_currentContract; ParameterList const* m_returnParameters; + bool m_resolveInsideCode; bool m_allowLazyTypes; }; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index bfde7187a..a445d56e1 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -92,13 +93,13 @@ std::pair const* MemberList::getMemberStorageOffset(string const { TypePointers memberTypes; memberTypes.reserve(m_memberTypes.size()); - for (auto const& nameAndType: m_memberTypes) - memberTypes.push_back(nameAndType.second); + for (auto const& member: m_memberTypes) + memberTypes.push_back(member.type); m_storageOffsets.reset(new StorageOffsets()); m_storageOffsets->computeOffsets(memberTypes); } for (size_t index = 0; index < m_memberTypes.size(); ++index) - if (m_memberTypes[index].first == _name) + if (m_memberTypes[index].name == _name) return m_storageOffsets->getOffset(index); return nullptr; } @@ -189,7 +190,7 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length if (_length) { if (!_length->getType()) - _length->checkTypeRequirements(); + _length->checkTypeRequirements(nullptr); auto const* length = dynamic_cast(_length->getType().get()); if (!length) BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); @@ -793,18 +794,46 @@ MemberList const& ContractType::getMembers() const if (!m_members) { // All address members and all interface functions - vector> members(IntegerType::AddressMemberList.begin(), - IntegerType::AddressMemberList.end()); + MemberList::MemberMap members( + IntegerType::AddressMemberList.begin(), + IntegerType::AddressMemberList.end() + ); if (m_super) { + // add the most derived of all functions which are visible in derived contracts for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) for (ASTPointer const& function: base->getDefinedFunctions()) - if (function->isVisibleInDerivedContracts()) - members.push_back(make_pair(function->getName(), make_shared(*function, true))); + { + if (!function->isVisibleInDerivedContracts()) + continue; + auto functionType = make_shared(*function, true); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) + { + if (member.name != function->getName()) + continue; + auto memberType = dynamic_cast(member.type.get()); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualArgumentTypes(*functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (!functionWithEqualArgumentsFound) + members.push_back(MemberList::Member( + function->getName(), + functionType, + function.get() + )); + } } else for (auto const& it: m_contract.getInterfaceFunctions()) - members.push_back(make_pair(it.second->getDeclaration().getName(), it.second)); + members.push_back(MemberList::Member( + it.second->getDeclaration().getName(), + it.second, + &it.second->getDeclaration() + )); m_members.reset(new MemberList(members)); } return *m_members; @@ -823,16 +852,6 @@ shared_ptr const& ContractType::getConstructorType() const return m_constructorType; } -u256 ContractType::getFunctionIdentifier(string const& _functionName) const -{ - auto interfaceFunctions = m_contract.getInterfaceFunctions(); - for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second->getDeclaration().getName() == _functionName) - return FixedHash<4>::Arith(it.first); - - return Invalid256; -} - vector> ContractType::getStateVariables() const { vector variables; @@ -873,8 +892,8 @@ u256 StructType::getStorageSize() const bool StructType::canLiveOutsideStorage() const { - for (pair const& member: getMembers()) - if (!member.second->canLiveOutsideStorage()) + for (auto const& member: getMembers()) + if (!member.type->canLiveOutsideStorage()) return false; return true; } @@ -891,7 +910,7 @@ MemberList const& StructType::getMembers() const { MemberList::MemberMap members; for (ASTPointer const& variable: m_struct.getMembers()) - members.push_back(make_pair(variable->getName(), variable->getType())); + members.push_back(MemberList::Member(variable->getName(), variable->getType(), variable.get())); m_members.reset(new MemberList(members)); } return *m_members; @@ -1007,11 +1026,11 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): vector retParamNames; if (auto structType = dynamic_cast(returnType.get())) { - for (pair const& member: structType->getMembers()) - if (member.second->getCategory() != Category::Mapping && member.second->getCategory() != Category::Array) + for (auto const& member: structType->getMembers()) + if (member.type->getCategory() != Category::Mapping && member.type->getCategory() != Category::Array) { - retParamNames.push_back(member.first); - retParams.push_back(member.second); + retParamNames.push_back(member.name); + retParams.push_back(member.type); } } else @@ -1109,7 +1128,7 @@ unsigned FunctionType::getSizeOnStack() const return size; } -TypePointer FunctionType::externalType() const +FunctionTypePointer FunctionType::externalFunctionType() const { TypePointers paramTypes; TypePointers retParamTypes; @@ -1117,13 +1136,13 @@ TypePointer FunctionType::externalType() const for (auto type: m_parameterTypes) { if (!type->externalType()) - return TypePointer(); + return FunctionTypePointer(); paramTypes.push_back(type->externalType()); } for (auto type: m_returnParameterTypes) { if (!type->externalType()) - return TypePointer(); + return FunctionTypePointer(); retParamTypes.push_back(type->externalType()); } return make_shared(paramTypes, retParamTypes, m_location, m_arbitraryParameters); @@ -1141,12 +1160,12 @@ MemberList const& FunctionType::getMembers() const case Location::Bare: if (!m_members) { - vector> members{ + MemberList::MemberMap members{ {"value", make_shared(parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(false, true)}, Location::SetValue, false, m_gasSet, m_valueSet)}}; if (m_location != Location::Creation) - members.push_back(make_pair("gas", make_shared( + members.push_back(MemberList::Member("gas", make_shared( parseElementaryTypeVector({"uint"}), TypePointers{copyAndSetGasOrValue(true, false)}, Location::SetGas, false, m_gasSet, m_valueSet))); @@ -1158,6 +1177,37 @@ MemberList const& FunctionType::getMembers() const } } +bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes) const +{ + TypePointers const& parameterTypes = getParameterTypes(); + if (takesArbitraryParameters()) + return true; + else if (_argumentTypes.size() != parameterTypes.size()) + return false; + else + return std::equal( + _argumentTypes.cbegin(), + _argumentTypes.cend(), + parameterTypes.cbegin(), + [](TypePointer const& argumentType, TypePointer const& parameterType) + { + return argumentType->isImplicitlyConvertibleTo(*parameterType); + } + ); +} + +bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const +{ + if (m_parameterTypes.size() != _other.m_parameterTypes.size()) + return false; + return equal( + m_parameterTypes.cbegin(), + m_parameterTypes.cend(), + _other.m_parameterTypes.cbegin(), + [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; } + ); +} + string FunctionType::externalSignature(std::string const& _name) const { std::string funcName = _name; @@ -1168,7 +1218,7 @@ string FunctionType::externalSignature(std::string const& _name) const } string ret = funcName + "("; - TypePointers externalParameterTypes = dynamic_cast(*externalType()).getParameterTypes(); + TypePointers externalParameterTypes = externalFunctionType()->getParameterTypes(); for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) { solAssert(!!(*it), "Parameter should have external type"); @@ -1178,6 +1228,11 @@ string FunctionType::externalSignature(std::string const& _name) const return ret + ")"; } +u256 FunctionType::externalIdentifier() const +{ + return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature()))); +} + TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) { TypePointers pointers; @@ -1261,7 +1316,7 @@ MemberList const& TypeType::getMembers() const // We need to lazy-initialize it because of recursive references. if (!m_members) { - vector> members; + MemberList::MemberMap members; if (m_actualType->getCategory() == Category::Contract && m_currentContract != nullptr) { ContractDefinition const& contract = dynamic_cast(*m_actualType).getContractDefinition(); @@ -1270,14 +1325,14 @@ MemberList const& TypeType::getMembers() const // We are accessing the type of a base contract, so add all public and protected // members. Note that this does not add inherited functions on purpose. for (Declaration const* decl: contract.getInheritableMembers()) - members.push_back(make_pair(decl->getName(), decl->getType())); + members.push_back(MemberList::Member(decl->getName(), decl->getType(), decl)); } else if (m_actualType->getCategory() == Category::Enum) { EnumDefinition const& enumDef = dynamic_cast(*m_actualType).getEnumDefinition(); auto enumType = make_shared(enumDef); for (ASTPointer const& enumValue: enumDef.getMembers()) - members.push_back(make_pair(enumValue->getName(), enumType)); + members.push_back(MemberList::Member(enumValue->getName(), enumType)); } m_members.reset(new MemberList(members)); } diff --git a/libsolidity/Types.h b/libsolidity/Types.h index e6d32d175..ab41d4d4d 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -69,17 +69,43 @@ private: class MemberList { public: - using MemberMap = std::vector>; + struct Member + { + Member(std::string const& _name, TypePointer const& _type, Declaration const* _declaration = nullptr): + name(_name), + type(_type), + declaration(_declaration) + { + } + + std::string name; + TypePointer type; + Declaration const* declaration = nullptr; + }; + + using MemberMap = std::vector; MemberList() {} explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} MemberList& operator=(MemberList&& _other); TypePointer getMemberType(std::string const& _name) const { + TypePointer type; for (auto const& it: m_memberTypes) - if (it.first == _name) - return it.second; - return TypePointer(); + if (it.name == _name) + { + solAssert(!type, "Requested member type by non-unique name."); + type = it.type; + } + return type; + } + MemberMap membersByName(std::string const& _name) const + { + MemberMap members; + for (auto const& it: m_memberTypes) + if (it.name == _name) + members.push_back(it); + return members; } /// @returns the offset of the given member in storage slots and bytes inside a slot or /// a nullptr if the member is not part of storage. @@ -404,12 +430,20 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; + virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + { + return externalType()->getCalldataEncodedSize(_padded); + } virtual unsigned getStorageBytes() const override { return 20; } + virtual bool canLiveOutsideStorage() const override { return true; } virtual bool isValueType() const override { return true; } virtual std::string toString() const override; virtual MemberList const& getMembers() const override; - virtual TypePointer externalType() const override { return std::make_shared(160, IntegerType::Modifier::Address); } + virtual TypePointer externalType() const override + { + return std::make_shared(160, IntegerType::Modifier::Address); + } bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } @@ -472,13 +506,21 @@ public: explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; + virtual unsigned getCalldataEncodedSize(bool _padded = true) const override + { + return externalType()->getCalldataEncodedSize(_padded); + } virtual unsigned getSizeOnStack() const override { return 1; } virtual unsigned getStorageBytes() const override; + virtual bool canLiveOutsideStorage() const override { return true; } virtual std::string toString() const override; virtual bool isValueType() const override { return true; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer externalType() const override { return std::make_shared(8 * int(getStorageBytes())); } + virtual TypePointer externalType() const override + { + return std::make_shared(8 * int(getStorageBytes())); + } EnumDefinition const& getEnumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum @@ -512,9 +554,12 @@ public: virtual Category getCategory() const override { return Category::Function; } - /// @returns TypePointer of a new FunctionType object. All input/return parameters are an appropriate external types of input/return parameters of current function. - /// Returns an empty shared pointer if one of the input/return parameters does not have an externaltype. - virtual TypePointer externalType() const override; + /// @returns TypePointer of a new FunctionType object. All input/return parameters are an + /// appropriate external types of input/return parameters of current function. + /// Returns an empty shared pointer if one of the input/return parameters does not have an + /// external type. + virtual FunctionTypePointer externalFunctionType() const; + virtual TypePointer externalType() const override { return externalFunctionType(); } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(VariableDeclaration const& _varDecl); @@ -554,11 +599,18 @@ public: virtual unsigned getSizeOnStack() const override; virtual MemberList const& getMembers() const override; + /// @returns true if this function can take the given argument types (possibly + /// after implicit conversion). + bool canTakeArguments(TypePointers const& _arguments) const; + bool hasEqualArgumentTypes(FunctionType const& _other) const; + Location const& getLocation() const { return m_location; } /// @returns the external signature of this function type given the function name /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used std::string externalSignature(std::string const& _name = "") const; + /// @returns the external identifier of this function (the hash of the signature). + u256 externalIdentifier() const; Declaration const& getDeclaration() const { solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index d84bbd8f4..c14d26935 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -398,8 +398,8 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) StructType const* s = dynamic_cast(_type); for(auto const& structMember: s->getMembers()) { - auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.first); - r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.first), nodeType(structMember.second.get()), slotAndOffset.first, slotAndOffset.second }); + auto slotAndOffset = s->getStorageOffsetsOfMember(structMember.name); + r.members.push_back(SolidityDeclaration { QString::fromStdString(structMember.name), nodeType(structMember.type.get()), slotAndOffset.first, slotAndOffset.second }); } } break; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b46ab06e5..a97bb86fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,19 +23,18 @@ add_subdirectory(libdevcrypto) add_subdirectory(libethcore) add_subdirectory(libethereum) add_subdirectory(libevm) -add_subdirectory(libevmcore) add_subdirectory(libnatspec) add_subdirectory(libp2p) -add_subdirectory(libsolidity) +if (SOLIDITY) + add_subdirectory(libsolidity) +endif () +if (JSONRPC) add_subdirectory(libweb3jsonrpc) +endif () add_subdirectory(libwhisper) set(SRC_LIST ${SRC_LIST} ${SRC}) -if (NOT JSONRPC) - list(REMOVE_ITEM SRC_LIST "libweb3jsonrpc/./AccountHolder.cpp") -endif() - include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ..) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 1419afde5..724bcaf61 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -539,6 +539,8 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e void userDefinedTest(string testTypeFlag, std::function doTests) { + Options::get(); // parse command line options, e.g. to enable JIT + for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) { string arg = boost::unit_test::framework::master_test_suite().argv[i]; diff --git a/test/libethereum/state.cpp b/test/libethereum/state.cpp index e6811d972..670ed0330 100644 --- a/test/libethereum/state.cpp +++ b/test/libethereum/state.cpp @@ -171,10 +171,12 @@ BOOST_AUTO_TEST_CASE(stMemoryStressTest) dev::test::executeTests("stMemoryStressTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } +#if ETH_SOLIDITY BOOST_AUTO_TEST_CASE(stSolidityTest) { dev::test::executeTests("stSolidityTest", "/StateTests",dev::test::getFolder(__FILE__) + "/StateTestsFiller", dev::test::doStateTests); } +#endif BOOST_AUTO_TEST_CASE(stMemoryTest) { diff --git a/test/libevmcore/CMakeLists.txt b/test/libevmcore/CMakeLists.txt deleted file mode 100644 index 3ceda13b0..000000000 --- a/test/libevmcore/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -cmake_policy(SET CMP0015 NEW) - -aux_source_directory(. SRCS) - -add_sources(${SRCS}) diff --git a/test/libevmcore/Assembly.cpp b/test/libsolidity/Assembly.cpp similarity index 99% rename from test/libevmcore/Assembly.cpp rename to test/libsolidity/Assembly.cpp index fab260a9d..8dcee7fb5 100644 --- a/test/libevmcore/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,8 +20,6 @@ * Unit tests for Assembly Items from evmcore/Assembly.h */ -#if ETH_SOLIDITY - #include #include #include @@ -120,5 +118,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index b0633cca1..f0e54a940 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -19,7 +19,6 @@ * @date 2014 * Unit tests for the solidity compiler JSON Interface output. */ -#if ETH_SOLIDITY #include "../TestHelper.h" #include @@ -501,5 +500,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index bb16c88cd..7b0ceedb6 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity compiler. */ -#if ETH_SOLIDITY - #include #include #include @@ -193,5 +191,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c345f5204..8b926d6cf 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -21,8 +21,6 @@ * Unit tests for the solidity expression compiler, testing the behaviour of the code. */ -#if ETH_SOLIDITY - #include #include #include @@ -304,7 +302,6 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) BOOST_AUTO_TEST_CASE(calling_other_functions) { - // note that the index of a function is its index in the sorted sequence of functions char const* sourceCode = "contract collatz {\n" " function run(uint x) returns(uint y) {\n" " while ((y = x) > 1) {\n" @@ -1149,26 +1146,6 @@ BOOST_AUTO_TEST_CASE(now) BOOST_CHECK(callContractFunction("someInfo()") == encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(function_types) -{ - char const* sourceCode = "contract test {\n" - " function a(bool selector) returns (uint b) {\n" - " var f = fun1;\n" - " if (selector) f = fun2;\n" - " return f(9);\n" - " }\n" - " function fun1(uint x) returns (uint b) {\n" - " return 11;\n" - " }\n" - " function fun2(uint x) returns (uint b) {\n" - " return 12;\n" - " }\n" - "}\n"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("a(bool)", false) == encodeArgs(11)); - BOOST_CHECK(callContractFunction("a(bool)", true) == encodeArgs(12)); -} - BOOST_AUTO_TEST_CASE(type_conversions_cleanup) { // 22-byte integer converted to a contract (i.e. address, 20 bytes), converted to a 32 byte @@ -1499,7 +1476,7 @@ BOOST_AUTO_TEST_CASE(ripemd) { h256 ret; dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return u256(ret) >> (256 - 160); + return u256(ret); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1814,7 +1791,7 @@ BOOST_AUTO_TEST_CASE(gas_for_builtin) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("test(uint256)", 500) == bytes()); - BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"), true)); + BOOST_CHECK(callContractFunction("test(uint256)", 800) == encodeArgs(u256("0x8eb208f7e05d987a9b044a8e98c6b087f15a0bfc000000000000000000000000"), true)); } BOOST_AUTO_TEST_CASE(value_complex) @@ -3676,6 +3653,94 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0) BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) +{ + char const* sourceCode = R"( + contract test { + function f(uint k) returns(uint d) { return k; } + function f(uint a, uint b) returns(uint d) { return a + b; } + function g() returns(uint d) { return f(3); } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(3)); +} + +BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second) +{ + char const* sourceCode = R"( + contract test { + function f(uint a, uint b) returns(uint d) { return a + b; } + function f(uint k) returns(uint d) { return k; } + function g() returns(uint d) { return f(3, 7); } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); +} + +BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) +{ + char const* sourceCode = R"( + contract test { + function f(uint a, uint b) returns(uint d) { return a + b; } + function f(uint k) returns(uint d) { return k; } + function g(bool flag) returns(uint d) { + if (flag) + return f(3); + else + return f(3, 7); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("g(bool)", true) == encodeArgs(3)); + BOOST_CHECK(callContractFunction("g(bool)", false) == encodeArgs(10)); +} + +BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) +{ + char const* sourceCode = R"( + contract B { function f() returns(uint) { return 10; } } + contract C is B { + function f(uint i) returns(uint) { return 2 * i; } + function g() returns(uint) { return f(1); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) +{ + char const* sourceCode = R"( + contract A { function f(uint a) returns(uint) { return 2 * a; } } + contract B { function f() returns(uint) { return 10; } } + contract C is A, B { + function g() returns(uint) { return f(); } + function h() returns(uint) { return f(1); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(super_overload) +{ + char const* sourceCode = R"( + contract A { function f(uint a) returns(uint) { return 2 * a; } } + contract B { function f(bool b) returns(uint) { return 10; } } + contract C is A, B { + function g() returns(uint) { return super.f(true); } + function h() returns(uint) { return super.f(1); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(10)); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(packed_storage_signed) { char const* sourceCode = R"( @@ -3699,10 +3764,27 @@ BOOST_AUTO_TEST_CASE(packed_storage_signed) BOOST_CHECK( callContractFunction("test()") == encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); } +BOOST_AUTO_TEST_CASE(external_types_in_calls) +{ + char const* sourceCode = R"( + contract C1 { C1 public bla; function C1(C1 x) { bla = x; } } + contract C { + function test() returns (C1 x, C1 y) { + C1 c = new C1(C1(9)); + x = c.bla(); + y = this.t1(C1(7)); + } + function t1(C1 a) returns (C1) { return a; } + function() returns (C1) { return C1(9); } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(9), u256(7))); + BOOST_CHECK(callContractFunction("nonexisting") == encodeArgs(u256(9))); +} + BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 505cac991..b2436cfa7 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity expression compiler. */ -#if ETH_SOLIDITY - #include #include @@ -80,7 +78,9 @@ Declaration const& resolveDeclaration( // bracers are required, cause msvc couldnt handle this macro in for statement for (string const& namePart: _namespacedName) { - BOOST_REQUIRE(declaration = _resolver.resolveName(namePart, declaration)); + auto declarations = _resolver.resolveName(namePart, declaration); + BOOST_REQUIRE(!declarations.empty()); + BOOST_REQUIRE(declaration = *declarations.begin()); } BOOST_REQUIRE(declaration); return *declaration; @@ -491,5 +491,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp index ab6cb9029..c8f74e3aa 100644 --- a/test/libsolidity/SolidityInterface.cpp +++ b/test/libsolidity/SolidityInterface.cpp @@ -20,8 +20,6 @@ * Unit tests for generating source interfaces for Solidity contracts. */ -#if ETH_SOLIDITY - #include "../TestHelper.h" #include #include @@ -149,5 +147,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 917ea0007..c317dad97 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -20,8 +20,6 @@ * Unit tests for the name and type resolution of the solidity parser. */ -#if ETH_SOLIDITY - #include #include @@ -625,23 +623,23 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } -BOOST_AUTO_TEST_CASE(illegal_override_direct) +BOOST_AUTO_TEST_CASE(legal_override_direct) { char const* text = R"( contract B { function f() {} } contract C is B { function f(uint i) {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } -BOOST_AUTO_TEST_CASE(illegal_override_indirect) +BOOST_AUTO_TEST_CASE(legal_override_indirect) { char const* text = R"( contract A { function f(uint a) {} } contract B { function f() {} } contract C is A, B { } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } BOOST_AUTO_TEST_CASE(illegal_override_visibility) @@ -1656,6 +1654,103 @@ BOOST_AUTO_TEST_CASE(bytes0_array) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint) { return 1; } + function f(uint a) returns(uint) { return a; } + function g() returns(uint) { return f(3, 5); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) +{ + // literal 1 can be both converted to uint and uint8, so the call is ambiguous. + char const* sourceCode = R"( + contract test { + function f(uint8 a) returns(uint) { return a; } + function f(uint a) returns(uint) { return 2*a; } + function g() returns(uint) { return f(1); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) +{ + char const* sourceCode = R"( + contract test { + function f(uint a) returns(uint) { return 2 * a; } + function g() returns(uint) { var x = f; return x(7); } + } + )"; + ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(sourceCode), "Type resolving failed"); +} + +BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint) { return 1; } + function f(uint a) returns(uint) { return 2 * a; } + function g() returns(uint) { var x = f; return x(7); } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(external_types_clash) +{ + char const* sourceCode = R"( + contract base { + enum a { X } + function f(a) { } + } + contract test is base { + function f(uint8 a) { } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(override_changes_return_types) +{ + char const* sourceCode = R"( + contract base { + function f(uint a) returns (uint) { } + } + contract test is base { + function f(uint a) returns (uint8) { } + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); +} + +BOOST_AUTO_TEST_CASE(multiple_constructors) +{ + char const* sourceCode = R"( + contract test { + function test(uint a) { } + function test() {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); +} + +BOOST_AUTO_TEST_CASE(equal_overload) +{ + char const* sourceCode = R"( + contract test { + function test(uint a) returns (uint b) { } + function test(uint a) external {} + } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); +} + BOOST_AUTO_TEST_CASE(uninitialized_var) { char const* sourceCode = R"( @@ -1671,5 +1766,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 99adcf199..d2c1ec186 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#if ETH_SOLIDITY - #include "../TestHelper.h" #include #include @@ -539,5 +537,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 8ab1de8f1..af9b51467 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -20,8 +20,6 @@ * Tests for the Solidity optimizer. */ -#if ETH_SOLIDITY - #include #include #include @@ -816,5 +814,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 7baa12921..cad0e1f2c 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity parser. */ -#if ETH_SOLIDITY - #include #include #include @@ -136,6 +134,31 @@ BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) BOOST_CHECK_THROW(parseText(text), ParserError); } +BOOST_AUTO_TEST_CASE(two_exact_functions) +{ + char const* text = R"( + contract test { + function fun(uint a) returns(uint r) { return a; } + function fun(uint a) returns(uint r) { return a; } + } + )"; + // with support of overloaded functions, during parsing, + // we can't determine whether they match exactly, however + // it will throw DeclarationError in following stage. + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(overloaded_functions) +{ + char const* text = R"( + contract test { + function fun(uint a) returns(uint r) { return a; } + function fun(uint a, uint b) returns(uint r) { return a + b; } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_CASE(function_natspec_documentation) { ASTPointer contract; @@ -855,5 +878,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 20b946ee0..8d3e53929 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -20,8 +20,6 @@ * Unit tests for the solidity scanner. */ -#if ETH_SOLIDITY - #include #include @@ -288,5 +286,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces - -#endif diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index da8b48303..6b6306479 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -20,8 +20,6 @@ * Unit tests for the type system of Solidity. */ -#if ETH_SOLIDITY - #include #include @@ -93,5 +91,3 @@ BOOST_AUTO_TEST_SUITE_END() } } } - -#endif