diff --git a/test/Common.cpp b/test/Common.cpp index b56cb3257..5e7059013 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include #include #include @@ -57,9 +58,9 @@ boost::filesystem::path testPath() return {}; } -std::string EVMOneEnvOrDefaultPath() +std::string envOrDefaultPath(std::string const& env_name, std::string const& lib_name) { - if (auto path = getenv("ETH_EVMONE")) + if (auto path = getenv(env_name.c_str())) return path; auto const searchPath = @@ -76,7 +77,7 @@ std::string EVMOneEnvOrDefaultPath() }; for (auto const& basePath: searchPath) { - fs::path p = basePath / evmoneFilename; + fs::path p = basePath / lib_name; if (fs::exists(p)) return p.string(); } @@ -92,7 +93,8 @@ CommonOptions::CommonOptions(std::string _caption): options.add_options() ("evm-version", po::value(&evmVersionString), "which evm version to use") ("testpath", po::value(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") - ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") + ("vm", po::value>(&vmPaths), "path to evmc library, can be supplied multiple times.") + ("ewasm", po::bool_switch(&ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("optimize", po::bool_switch(&optimize), "enables optimization") ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") @@ -141,6 +143,33 @@ bool CommonOptions::parse(int argc, char const* const* argv) throw std::runtime_error(errorMessage.str()); } + if (vmPaths.empty()) + { + std::string evmone = envOrDefaultPath("ETH_EVMONE", evmoneFilename); + if (!evmone.empty()) + vmPaths.emplace_back(evmone); + else + { + std::cout << "Unable to find " << solidity::test::evmoneFilename + << ". Please provide the path using --vm ." << std::endl; + std::cout << "You can download it at" << std::endl; + std::cout << solidity::test::evmoneDownloadLink << std::endl; + } + } + + if (ewasm) { + std::string hera = envOrDefaultPath("ETH_HERA", heraFilename); + if (!hera.empty()) + vmPaths.emplace_back(hera); + else { + std::cout << "Unable to find " << solidity::test::heraFilename + << ". Please provide the path using --vm ." << std::endl; + std::cout << "You can download it at" << std::endl; + std::cout << solidity::test::heraDownloadLink << std::endl; + std::cout << "Ewasm tests disabled." << std::endl; + } + } + return true; } diff --git a/test/Common.h b/test/Common.h index eaad7f0ba..a665d563f 100644 --- a/test/Common.h +++ b/test/Common.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -31,21 +33,27 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip"; +static constexpr auto heraFilename = "hera.dll"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.0.tar.gz"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz"; +static constexpr auto heraFilename = "libhera.dylib"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz"; +static constexpr auto heraFilename = "libhera.so"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-linux-x86_64.tar.gz"; #endif - struct ConfigException : public util::Exception {}; struct CommonOptions: boost::noncopyable { - boost::filesystem::path evmonePath; + std::vector vmPaths; boost::filesystem::path testPath; + bool ewasm = false; bool optimize = false; bool enforceViaYul = false; bool disableSMT = false; @@ -64,8 +72,8 @@ struct CommonOptions: boost::noncopyable CommonOptions(std::string caption = ""); virtual ~CommonOptions() {}; -protected: +protected: boost::program_options::options_description options; private: diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 51dc83352..10b29d99f 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -39,17 +39,18 @@ using namespace evmc::literals; evmc::VM& EVMHost::getVM(string const& _path) { - static evmc::VM theVM; - if (!theVM && !_path.empty()) + static evmc::VM NullVM{nullptr}; + static map> vms; + if (vms.count(_path) == 0) { evmc_loader_error_code errorCode = {}; auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)}; if (vm && errorCode == EVMC_LOADER_SUCCESS) { - if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1) - theVM = std::move(vm); + if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM)) + vms[_path] = make_unique(evmc::VM(move(vm))); else - cerr << "VM loaded does not support EVM1" << endl; + cerr << "VM loaded neither supports EVM1 nor EWASM" << endl; } else { @@ -59,7 +60,38 @@ evmc::VM& EVMHost::getVM(string const& _path) cerr << endl; } } - return theVM; + + if (vms.count(_path) > 0) + return *vms[_path]; + + return NullVM; +} + +bool EVMHost::checkVmPaths(vector const& _vmPaths) +{ + bool evmVmFound = false; + bool ewasmVmFound = false; + for (auto const& path: _vmPaths) + { + evmc::VM& vm = EVMHost::getVM(path.string()); + if (!vm) + return false; + + if (vm.has_capability(EVMC_CAPABILITY_EVM1)) + { + if (evmVmFound) + throw runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm."); + evmVmFound = true; + } + + if (vm.has_capability(EVMC_CAPABILITY_EWASM)) + { + if (ewasmVmFound) + throw runtime_error("Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm."); + ewasmVmFound = true; + } + } + return evmVmFound; } EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): diff --git a/test/EVMHost.h b/test/EVMHost.h index af38426be..91cb9b809 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -30,6 +30,8 @@ #include +#include + namespace solidity::test { using Address = util::h160; @@ -40,12 +42,17 @@ public: using MockedHost::get_code_size; using MockedHost::get_balance; - /// Tries to dynamically load libevmone. @returns nullptr on failure. - /// The path has to be provided for the first successful run and will be ignored - /// afterwards. + /// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM. + /// @returns vmc::VM(nullptr) on failure. static evmc::VM& getVM(std::string const& _path = {}); - explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); + /// Tries to load all defined evmc vm shared libraries. + /// @param _vmPaths paths to multiple evmc shared libraries. + /// @throw Exception if multiple evm1 or multiple ewasm evmc vms where loaded. + /// @returns true, if an evmc vm was supporting evm1 loaded properly. + static bool checkVmPaths(std::vector const& _vmPaths); + + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm); void reset() { accounts.clear(); m_currentAddress = {}; } void newBlock() @@ -71,6 +78,12 @@ public: static util::h256 convertFromEVMC(evmc::bytes32 const& _data); static evmc::bytes32 convertToEVMC(util::h256 const& _data); + /// @returns true, if the evmc VM has the given capability. + bool hasCapability(evmc_capabilities capability) const noexcept + { + return m_vm.has_capability(capability); + } + private: evmc::address m_currentAddress = {}; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 7f5dfcf74..a17f65f3c 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -29,6 +29,8 @@ #include +#include + #include #include @@ -40,27 +42,47 @@ using namespace solidity::util; using namespace solidity::test; ExecutionFramework::ExecutionFramework(): - ExecutionFramework(solidity::test::CommonOptions::get().evmVersion()) + ExecutionFramework(solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths) { } -ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): +ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector const& _vmPaths): m_evmVersion(_evmVersion), m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()), m_showMessages(solidity::test::CommonOptions::get().showMessages), - m_evmHost(make_shared(m_evmVersion)) + m_vmPaths(_vmPaths) { if (solidity::test::CommonOptions::get().optimize) m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); + for (auto const& path: m_vmPaths) + if (EVMHost::getVM(path.string()).has_capability(EVMC_CAPABILITY_EWASM)) + m_supportsEwasm = true; + + selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); +} + +void ExecutionFramework::selectVM(evmc_capabilities _cap) +{ + m_evmcHost.reset(); + for (auto const& path: m_vmPaths) + { + evmc::VM& vm = EVMHost::getVM(path.string()); + if (vm.has_capability(_cap)) + { + m_evmcHost = make_unique(m_evmVersion, vm); + break; + } + } + solAssert(m_evmcHost != nullptr, ""); reset(); } void ExecutionFramework::reset() { - m_evmHost->reset(); + m_evmcHost->reset(); for (size_t i = 0; i < 10; i++) - m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance = + m_evmcHost->accounts[EVMHost::convertToEVMC(account(i))].balance = EVMHost::convertToEVMC(u256(1) << 100); } @@ -92,7 +114,7 @@ std::pair ExecutionFramework::compareAndCreateMessage( u256 ExecutionFramework::gasLimit() const { - return {m_evmHost->tx_context.block_gas_limit}; + return {m_evmcHost->tx_context.block_gas_limit}; } u256 ExecutionFramework::gasPrice() const @@ -100,24 +122,24 @@ u256 ExecutionFramework::gasPrice() const // here and below we use "return u256{....}" instead of just "return {....}" // to please MSVC and avoid unexpected // warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied - return u256{EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; + return u256{EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const { return u256{EVMHost::convertFromEVMC( - m_evmHost->get_block_hash(static_cast(_number & numeric_limits::max())) + m_evmcHost->get_block_hash(static_cast(_number & numeric_limits::max())) )}; } u256 ExecutionFramework::blockNumber() const { - return m_evmHost->tx_context.block_number; + return m_evmcHost->tx_context.block_number; } void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) { - m_evmHost->newBlock(); + m_evmcHost->newBlock(); if (m_showMessages) { @@ -147,7 +169,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 } message.gas = m_gas.convert_to(); - evmc::result result = m_evmHost->call(message); + evmc::result result = m_evmcHost->call(message); m_output = bytes(result.output_data, result.output_data + result.output_size); if (_isCreation) @@ -166,7 +188,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) { - m_evmHost->newBlock(); + m_evmcHost->newBlock(); if (m_showMessages) { @@ -181,12 +203,12 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) message.destination = EVMHost::convertToEVMC(_addr); message.gas = m_gas.convert_to(); - m_evmHost->call(message); + m_evmcHost->call(message); } size_t ExecutionFramework::currentTimestamp() { - return static_cast(m_evmHost->tx_context.block_timestamp); + return static_cast(m_evmcHost->tx_context.block_timestamp); } size_t ExecutionFramework::blockTimestamp(u256 _block) @@ -204,32 +226,32 @@ Address ExecutionFramework::account(size_t _idx) bool ExecutionFramework::addressHasCode(Address const& _addr) { - return m_evmHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; + return m_evmcHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; } size_t ExecutionFramework::numLogs() const { - return m_evmHost->recorded_logs.size(); + return m_evmcHost->recorded_logs.size(); } size_t ExecutionFramework::numLogTopics(size_t _logIdx) const { - return m_evmHost->recorded_logs.at(_logIdx).topics.size(); + return m_evmcHost->recorded_logs.at(_logIdx).topics.size(); } h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const { - return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); + return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); } Address ExecutionFramework::logAddress(size_t _logIdx) const { - return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).creator); + return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).creator); } bytes ExecutionFramework::logData(size_t _logIdx) const { - const auto& data = m_evmHost->recorded_logs.at(_logIdx).data; + const auto& data = m_evmcHost->recorded_logs.at(_logIdx).data; // TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(), // but reference type like string_view would be preferable. return {data.begin(), data.end()}; @@ -237,13 +259,13 @@ bytes ExecutionFramework::logData(size_t _logIdx) const u256 ExecutionFramework::balanceAt(Address const& _addr) { - return u256(EVMHost::convertFromEVMC(m_evmHost->get_balance(EVMHost::convertToEVMC(_addr)))); + return u256(EVMHost::convertFromEVMC(m_evmcHost->get_balance(EVMHost::convertToEVMC(_addr)))); } bool ExecutionFramework::storageEmpty(Address const& _addr) { - const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr)); - if (it != m_evmHost->accounts.end()) + const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr)); + if (it != m_evmcHost->accounts.end()) { for (auto const& entry: it->second.storage) if (!(entry.second.value == evmc::bytes32{})) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 69fd93a1c..9b01d9a91 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -39,8 +40,6 @@ namespace solidity::test { -class EVMHost; - using rational = boost::rational; /// An Ethereum address: 20 bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes. @@ -55,7 +54,7 @@ class ExecutionFramework public: ExecutionFramework(); - explicit ExecutionFramework(langutil::EVMVersion _evmVersion); + ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths); virtual ~ExecutionFramework() = default; virtual bytes const& compileAndRunWithoutCheck( @@ -255,6 +254,7 @@ private: } protected: + void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1); void reset(); void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); @@ -279,7 +279,10 @@ protected: solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default; solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal(); bool m_showMessages = false; - std::shared_ptr m_evmHost; + bool m_supportsEwasm = false; + std::unique_ptr m_evmcHost; + + std::vector m_vmPaths; bool m_transactionSuccessful = true; Address m_sender = account(0); diff --git a/test/TestCase.h b/test/TestCase.h index 1815409fe..65cc4eeb6 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -39,6 +39,7 @@ public: { std::string filename; langutil::EVMVersion evmVersion; + std::vector vmPaths; bool enforceCompileViaYul; }; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 8f065a7bb..18a5a2ea8 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -72,7 +71,7 @@ int registerTests( { int numTestsAdded = 0; fs::path fullpath = _basepath / _path; - TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul}; + TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths, _enforceViaYul}; if (fs::is_directory(fullpath)) { test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); @@ -156,14 +155,19 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) initializeOptions(); - bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string()); - if (disableSemantics) + bool disableSemantics = true; + try { - cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath ." << endl; - cout << "You can download it at" << endl; - cout << solidity::test::evmoneDownloadLink << endl; - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths); } + catch (std::runtime_error const& _exception) + { + cerr << "Error: " << _exception.what() << endl; + exit(1); + } + if (disableSemantics) + cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + // Include the interactive tests in the automatic tests as well for (auto const& ts: g_interactiveTestsuites) { diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index ce99fd585..8f25f9437 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -417,7 +417,7 @@ BOOST_AUTO_TEST_CASE(auction_simple) BOOST_CHECK_EQUAL(registrar.owner(name), 0); // "wait" until auction end - m_evmHost->tx_context.block_timestamp += m_biddingTime + 10; + m_evmcHost->tx_context.block_timestamp += m_biddingTime + 10; // trigger auction again registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); @@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding) string name = "x"; unsigned startTime = 0x776347e2; - m_evmHost->tx_context.block_timestamp = startTime; + m_evmcHost->tx_context.block_timestamp = startTime; RegistrarInterface registrar(*this); // initiate auction @@ -437,19 +437,19 @@ BOOST_AUTO_TEST_CASE(auction_bidding) registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), 0); // overbid self - m_evmHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; + m_evmcHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; registrar.setNextValue(12); registrar.reserve(name); // another bid by someone else sendEther(account(1), 10 * ether); m_sender = account(1); - m_evmHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; + m_evmcHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; registrar.setNextValue(13); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), 0); // end auction by first bidder (which is not highest) trying to overbid again (too late) m_sender = account(0); - m_evmHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; + m_evmcHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; registrar.setNextValue(20); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(1)); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 180e49bd7..c46f59939 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -39,8 +39,8 @@ using namespace boost::unit_test; namespace fs = boost::filesystem; -SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul): - SolidityExecutionFramework(_evmVersion), +SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, vector const& _vmPaths, bool enforceViaYul): + SolidityExecutionFramework(_evmVersion, _vmPaths), EVMVersionRestrictedTestCase(_filename), m_sources(m_reader.sources()), m_lineOffset(m_reader.lineNumber()), @@ -72,6 +72,21 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer else BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); + string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false"); + if (compileToEwasm == "also") + m_runWithEwasm = true; + else if (compileToEwasm == "false") + m_runWithEwasm = false; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ".")); + + if (m_runWithEwasm && !m_runWithYul) + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled.")); + + // run ewasm tests only, if an ewasm evmc vm was defined + if (m_runWithEwasm && !m_supportsEwasm) + m_runWithEwasm = false; + m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) m_shouldRun = false; @@ -88,156 +103,180 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { + TestResult result = TestResult::Success; + bool compileViaYul = m_runWithYul || m_enforceViaYul; - for (bool compileViaYul: set{!m_runWithoutYul, m_runWithYul || m_enforceViaYul}) + if (m_runWithoutYul) + result = runTest(_stream, _linePrefix, _formatted, false, false); + + if (compileViaYul && result == TestResult::Success) + result = runTest(_stream, _linePrefix, _formatted, true, false); + + if (m_runWithEwasm && result == TestResult::Success) + result = runTest(_stream, _linePrefix, _formatted, true, true); + + return result; +} + +TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm) +{ + try { - try + bool success = true; + + if (_compileViaYul && _compileToEwasm) + selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM); + else + selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); + + reset(); + + m_compileViaYul = _compileViaYul; + if (_compileToEwasm) { - reset(); - bool success = true; + soltestAssert(m_compileViaYul, ""); + m_compileToEwasm = _compileToEwasm; + } - m_compileViaYul = compileViaYul; - m_compileViaYulCanBeSet = false; + m_compileViaYulCanBeSet = false; - if (compileViaYul) - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; + if (_compileViaYul) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; - for (auto& test: m_tests) - test.reset(); + for (auto& test: m_tests) + test.reset(); - map libraries; + map libraries; - bool constructed = false; + bool constructed = false; - for (auto& test: m_tests) + for (auto& test: m_tests) + { + if (constructed) { - if (constructed) - { - soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); - } - else if (test.call().isLibrary) + soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); + soltestAssert( + !test.call().isConstructor, + "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().isLibrary) + { + soltestAssert( + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature); + libraries[test.call().signature] = m_contractAddress; + continue; + } + else + { + if (test.call().isConstructor) + deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); + else + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; + } + + if (test.call().isConstructor) + { + if (m_transactionSuccessful == test.call().expectations.failure) + success = false; + + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(bytes()); + } + else + { + bytes output; + if (test.call().useCallWithoutSignature) + output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); + else { soltestAssert( - deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, - "Failed to deploy library " + test.call().signature + m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()) + .isMember(test.call().signature), + "The function " + test.call().signature + " is not known to the compiler"); + + output = callContractFunctionWithValueNoEncoding( + test.call().signature, test.call().value.value, test.call().arguments.rawBytes() ); - libraries[test.call().signature] = m_contractAddress; - continue; - } - else - { - if (test.call().isConstructor) - deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); - else - soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); - constructed = true; } - if (test.call().isConstructor) - { - if (m_transactionSuccessful == test.call().expectations.failure) - success = false; + if ((m_transactionSuccessful == test.call().expectations.failure) + || (output != test.call().expectations.rawBytes())) + success = false; - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(bytes()); - } - else - { - bytes output; - if (test.call().useCallWithoutSignature) - output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); - else - { - soltestAssert( - m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), - "The function " + test.call().signature + " is not known to the compiler" - ); - - output = callContractFunctionWithValueNoEncoding( - test.call().signature, - test.call().value.value, - test.call().arguments.rawBytes() - ); - } - - if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) - success = false; - - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(std::move(output)); - test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); - } + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(std::move(output)); + test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); } + } - if (success && !m_runWithYul && compileViaYul) + if (success && !m_runWithYul && _compileViaYul) + { + m_compileViaYulCanBeSet = true; + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << + _linePrefix << endl << + _linePrefix << "Test can pass via Yul and marked with compileViaYul: false." << endl; + return TestResult::Failure; + } + + if (!success && (m_runWithYul || !_compileViaYul)) + { + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + for (auto const& test: m_tests) { - m_compileViaYulCanBeSet = true; - AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix - << "Test can pass via Yul and marked with compileViaYul: false." << endl; - return TestResult::Failure; + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); } - - if (!success && (m_runWithYul || !compileViaYul)) + _stream << endl; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + for (auto const& test: m_tests) { - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); + } + AnsiColorized(_stream, _formatted, {BOLD, RED}) + << _linePrefix << endl + << _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl; + if (_compileViaYul && m_runWithoutYul) + { + _stream << _linePrefix << endl << _linePrefix; + AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul."; _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } - AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix - << "Attention: Updates on the test will apply the detected format displayed." << endl; - if (compileViaYul && m_runWithoutYul) - { - _stream << _linePrefix << endl << _linePrefix; - AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) - << "Note that the test passed without Yul."; - _stream << endl; - } - else if (!compileViaYul && m_runWithYul) - AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix - << "Note that the test also has to pass via Yul." << endl; - return TestResult::Failure; } + else if (!_compileViaYul && m_runWithYul) + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) + << _linePrefix << endl + << _linePrefix << "Note that the test also has to pass via Yul." << endl; + return TestResult::Failure; } - catch (WhiskersError const&) - { - // this is an error in Whiskers template, so should be thrown anyway + } + catch (WhiskersError const&) + { + // this is an error in Whiskers template, so should be thrown anyway + throw; + } + catch (YulException const&) + { + // this should be an error in yul compilation or translation + throw; + } + catch (boost::exception const&) + { + if (!_compileViaYul || m_runWithYul) throw; - } - catch (YulException const&) - { - // this should be an error in yul compilation or translation + } + catch (std::exception const&) + { + if (!_compileViaYul || m_runWithYul) throw; - } - catch (boost::exception const&) - { - if (compileViaYul && !m_runWithYul) - continue; + } + catch (...) + { + if (!_compileViaYul || m_runWithYul) throw; - } - catch (std::exception const&) - { - if (compileViaYul && !m_runWithYul) - continue; - throw; - } - catch (...) - { - if (compileViaYul && !m_runWithYul) - continue; - throw; - } } return TestResult::Success; diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 646e7e00b..acb656763 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -40,9 +40,9 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict { public: static std::unique_ptr create(Config const& _options) - { return std::make_unique(_options.filename, _options.evmVersion, _options.enforceCompileViaYul); } + { return std::make_unique(_options.filename, _options.evmVersion, _options.vmPaths, _options.enforceCompileViaYul); } - explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _enforceViaYul = false); + explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, std::vector const& _vmPaths, bool _enforceViaYul = false); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; @@ -59,10 +59,12 @@ public: /// Returns true if deployment was successful, false otherwise. bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: + TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm); SourceMap m_sources; std::size_t m_lineOffset; std::vector m_tests; bool m_runWithYul = false; + bool m_runWithEwasm = false; bool m_runWithoutYul = true; bool m_enforceViaYul = false; bool m_runWithABIEncoderV1Only = false; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d3398f510..5dc022137 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -48,18 +48,38 @@ using namespace solidity::util; using namespace solidity::test; using namespace solidity::langutil; -#define ALSO_VIA_YUL(CODE) \ -{ \ - { CODE } \ - reset(); \ - m_compileViaYul = true; \ - { CODE } \ +#define ALSO_VIA_YUL(CODE) \ +{ \ + m_doEwasmTestrun = true; \ + \ + m_compileViaYul = false; \ + m_compileToEwasm = false; \ + { CODE } \ + \ + m_compileViaYul = true; \ + reset(); \ + { CODE } \ + \ + if (m_doEwasmTestrun) \ + { \ + m_compileToEwasm = true; \ + reset(); \ + { CODE } \ + } \ } +#define DISABLE_EWASM_TESTRUN() \ + { m_doEwasmTestrun = false; } + namespace solidity::frontend::test { -BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) +struct SolidityEndToEndTestExecutionFramework: public SolidityExecutionFramework +{ + bool m_doEwasmTestrun = false; +}; + +BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework) int constexpr roundTo32(int _num) { @@ -115,6 +135,8 @@ BOOST_AUTO_TEST_CASE(recursive_calls) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); function recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256 { @@ -140,6 +162,8 @@ BOOST_AUTO_TEST_CASE(while_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto while_loop_cpp = [](u256 const& n) -> u256 @@ -168,6 +192,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto do_while_loop_cpp = [](u256 const& n) -> u256 @@ -213,6 +239,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto do_while = [](u256 n) -> u256 @@ -263,6 +291,8 @@ BOOST_AUTO_TEST_CASE(nested_loops) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto nested_loops_cpp = [](u256 n) -> u256 @@ -329,6 +359,8 @@ BOOST_AUTO_TEST_CASE(nested_loops_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto nested_loops_cpp = [](u256 n) -> u256 @@ -383,6 +415,8 @@ BOOST_AUTO_TEST_CASE(for_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop = [](u256 n) -> u256 @@ -444,6 +478,8 @@ BOOST_AUTO_TEST_CASE(nested_for_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop = [](u256 n) -> u256 @@ -484,6 +520,8 @@ BOOST_AUTO_TEST_CASE(for_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop_cpp = [](u256 const& n) -> u256 @@ -512,6 +550,8 @@ BOOST_AUTO_TEST_CASE(for_loop_empty) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop_empty_cpp = []() -> u256 @@ -542,6 +582,8 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256 @@ -665,6 +707,8 @@ BOOST_AUTO_TEST_CASE(many_local_variables) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256 { @@ -689,6 +733,8 @@ BOOST_AUTO_TEST_CASE(short_circuiting) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto short_circuiting_cpp = [](u256 n) -> u256 @@ -801,6 +847,8 @@ BOOST_AUTO_TEST_CASE(compound_assign) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value1; @@ -863,6 +911,8 @@ BOOST_AUTO_TEST_CASE(mapping_state) map m_voted; }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); Ballot ballot; @@ -936,6 +986,8 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) return --table[value++]; }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); value = 0; table.clear(); @@ -962,6 +1014,8 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping) else return table[_x][_y] = _z; }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); table.clear(); @@ -998,6 +1052,8 @@ BOOST_AUTO_TEST_CASE(constructor) }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); testContractAgainstCpp("get(uint256)", get, u256(6)); testContractAgainstCpp("get(uint256)", get, u256(7)); @@ -1016,12 +1072,12 @@ BOOST_AUTO_TEST_CASE(blockchain) } } )"; - m_evmHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); + m_evmcHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); + m_evmcHost->newBlock(); + m_evmcHost->newBlock(); + m_evmcHost->newBlock(); + m_evmcHost->newBlock(); + m_evmcHost->newBlock(); compileAndRun(sourceCode, 27); ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); } @@ -1038,6 +1094,8 @@ BOOST_AUTO_TEST_CASE(send_ether) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + u256 amount(250); compileAndRun(sourceCode, amount + 1); u160 address(23); @@ -1070,6 +1128,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "B"); u160 const nonPayableRecipient = m_contractAddress; compileAndRun(sourceCode, 0, "C"); @@ -1110,6 +1170,8 @@ BOOST_AUTO_TEST_CASE(log0) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("a()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1129,6 +1191,8 @@ BOOST_AUTO_TEST_CASE(log1) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("a()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1149,6 +1213,8 @@ BOOST_AUTO_TEST_CASE(log2) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("a()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1170,6 +1236,8 @@ BOOST_AUTO_TEST_CASE(log3) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("a()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1191,6 +1259,8 @@ BOOST_AUTO_TEST_CASE(log4) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("a()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1212,6 +1282,8 @@ BOOST_AUTO_TEST_CASE(log_in_constructor) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); @@ -1235,6 +1307,8 @@ BOOST_AUTO_TEST_CASE(selfdestruct) u256 amount(130); u160 address(23); ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, amount); ABI_CHECK(callContractFunction("a(address)", address), bytes()); BOOST_CHECK(!addressHasCode(m_contractAddress)); @@ -1665,6 +1739,8 @@ BOOST_AUTO_TEST_CASE(gaslimit) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto result = callContractFunction("f()"); ABI_CHECK(result, encodeArgs(gasLimit())); @@ -1681,6 +1757,8 @@ BOOST_AUTO_TEST_CASE(gasprice) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice())); ) @@ -1772,6 +1850,8 @@ BOOST_AUTO_TEST_CASE(event) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -1800,6 +1880,8 @@ BOOST_AUTO_TEST_CASE(event_emit) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -1826,6 +1908,8 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1850,6 +1934,8 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("f()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1889,6 +1975,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) u160 const c_loggedAddress = m_contractAddress; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1950,6 +2038,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) u160 const c_loggedAddress = m_contractAddress; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1985,6 +2075,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(numLogTopics(0), 0); @@ -2002,6 +2094,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -2028,6 +2122,8 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -2247,6 +2343,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 x(42); callContractFunction("createEvent(uint256)", x); @@ -2276,6 +2374,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 x(42); callContractFunction("createEvent(uint256)", x); @@ -2365,6 +2465,8 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8)); @@ -2987,6 +3089,8 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_CHECK(storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("fill()"), bytes()); @@ -3010,6 +3114,8 @@ BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_CHECK(storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("fill()"), bytes()); @@ -3037,6 +3143,8 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_CHECK(storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("fill()"), bytes()); @@ -4305,6 +4413,8 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "Test"); for (unsigned i = 0; i < strings.size(); i++) ABI_CHECK(callContractFunction( @@ -5415,6 +5525,8 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); @@ -5527,6 +5639,8 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) contract C2 {} )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "C1"); compileAndRun(sourceCode, 0, "C2"); ) diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 6ad8ddd15..84333269e 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -43,6 +43,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( entry.second = addPreamble(entry.second); m_compiler.reset(); + m_compiler.enableEwasmGeneration(m_compileToEwasm); m_compiler.setSources(sourcesWithPreamble); m_compiler.setLibraries(_libraryAddresses); m_compiler.setRevertStringBehaviour(m_revertStrings); @@ -63,38 +64,40 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( evmasm::LinkerObject obj; if (m_compileViaYul) { - // Try compiling twice: If the first run fails due to stack errors, forcefully enable - // the optimizer. - for (bool forceEnableOptimizer: {false, true}) + if (m_compileToEwasm) + obj = m_compiler.ewasmObject(contractName); + else { - OptimiserSettings optimiserSettings = m_optimiserSettings; - if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser) + // Try compiling twice: If the first run fails due to stack errors, forcefully enable + // the optimizer. + for (bool forceEnableOptimizer: {false, true}) { - // Enable some optimizations on the first run - optimiserSettings.runYulOptimiser = true; - optimiserSettings.yulOptimiserSteps = "uljmul jmul"; - } - else if (forceEnableOptimizer) - optimiserSettings = OptimiserSettings::full(); + OptimiserSettings optimiserSettings = m_optimiserSettings; + if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser) + { + // Enable some optimizations on the first run + optimiserSettings.runYulOptimiser = true; + optimiserSettings.yulOptimiserSteps = "uljmul jmul"; + } + else if (forceEnableOptimizer) + optimiserSettings = OptimiserSettings::full(); - yul::AssemblyStack asmStack( - m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, - optimiserSettings - ); - bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); - solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); + yul::AssemblyStack + asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings); + bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); + solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); - try - { - asmStack.optimize(); - obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); - break; - } - catch (...) - { - if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full()) - throw; + try + { + asmStack.optimize(); + obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + break; + } + catch (...) + { + if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full()) + throw; + } } } } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 9efe22b20..5b375cd14 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -43,8 +43,8 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework public: SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} - explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion): - ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) + explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths): + ExecutionFramework(_evmVersion, _vmPaths), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} bytes const& compileAndRunWithoutCheck( @@ -76,8 +76,10 @@ public: /// the latter only if it is required. static std::string addPreamble(std::string const& _sourceCode); protected: + solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_compileToEwasm = false; bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; }; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 3bdb16b0f..0d6a92e5b 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,6 +161,7 @@ TestTool::Result TestTool::process() m_test = m_testCaseCreator(TestCase::Config{ m_path.string(), m_options.evmVersion(), + m_options.vmPaths, m_options.enforceViaYul }); if (m_test->shouldRun()) @@ -424,14 +425,19 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(solidity::test::CommonOptions::get()); - bool disableSemantics = !solidity::test::EVMHost::getVM(options.evmonePath.string()); - if (disableSemantics) + bool disableSemantics = true; + try { - cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using --evmonepath ." << endl; - cout << "You can download it at" << endl; - cout << solidity::test::evmoneDownloadLink << endl; - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + disableSemantics = !solidity::test::EVMHost::checkVmPaths(options.vmPaths); } + catch (std::runtime_error const& _exception) + { + cerr << "Error: " << _exception.what() << endl; + return 1; + } + + if (disableSemantics) + cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; TestStats global_stats{0, 0}; cout << "Running tests..." << endl << endl; @@ -472,7 +478,7 @@ int main(int argc, char const *argv[]) cout << "." << endl; if (disableSemantics) - cout << "\nNOTE: Skipped semantics tests because " << solidity::test::evmoneFilename << " could not be found.\n" << endl; + cout << "\nNOTE: Skipped semantics tests because no evmc vm could be found.\n" << endl; return global_stats ? 0 : 1; }