diff --git a/test/Common.cpp b/test/Common.cpp index 69bfd21ee..a93f353a7 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -92,6 +92,7 @@ CommonOptions::CommonOptions(std::string _caption): ("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 defined multiple times.") ("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.") @@ -140,6 +141,9 @@ bool CommonOptions::parse(int argc, char const* const* argv) throw std::runtime_error(errorMessage.str()); } + if (!evmonePath.empty()) + vmPaths.emplace_back(evmonePath); + return true; } diff --git a/test/Common.h b/test/Common.h index 119050f39..6da4ea77c 100644 --- a/test/Common.h +++ b/test/Common.h @@ -44,6 +44,7 @@ struct ConfigException : public util::Exception {}; struct CommonOptions: boost::noncopyable { boost::filesystem::path evmonePath; + std::vector vmPaths; boost::filesystem::path testPath; bool optimize = false; bool enforceViaYul = false; diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 108dae4f1..9e0e9a306 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -38,17 +38,17 @@ using namespace evmc::literals; evmc::VM& EVMHost::getVM(string const& _path) { - static evmc::VM theVM; - if (!theVM && !_path.empty()) + static std::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] = std::make_unique(std::move(vm)); else - cerr << "VM loaded does not support EVM1" << endl; + cerr << "VM loaded does not support EVM1 or EWASM" << endl; } else { @@ -58,7 +58,45 @@ evmc::VM& EVMHost::getVM(string const& _path) cerr << endl; } } - return theVM; + + if (vms.count(_path) > 0) + return *vms[_path]; + + static evmc::VM NullVM(nullptr); + return NullVM; +} + +bool EVMHost::checkVmPaths(std::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) + { + cerr << "Multiple evm1 evmc vms where defined. Please only define one evm1 evmc vm." << endl; + assertThrow(false, Exception, ""); + } + evmVmFound = true; + } + + if (vm.has_capability(EVMC_CAPABILITY_EWASM)) + { + if (ewasmVmFound) + { + cerr << "Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm." << endl; + assertThrow(false, Exception, ""); + } + ewasmVmFound = true; + } + } + return evmVmFound; } EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): diff --git a/test/EVMHost.h b/test/EVMHost.h index 1e3540012..9331479bf 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -29,6 +29,8 @@ #include +#include + namespace solidity::test { using Address = util::h160; @@ -44,6 +46,8 @@ public: /// afterwards. static evmc::VM& getVM(std::string const& _path = {}); + static bool checkVmPaths(std::vector const& _vmPaths); + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); void reset() { accounts.clear(); m_currentAddress = {}; } @@ -70,6 +74,12 @@ public: static util::h256 convertFromEVMC(evmc::bytes32 const& _data); static evmc::bytes32 convertToEVMC(util::h256 const& _data); + /// Checks if the 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 ebe63b265..546c76f93 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -28,6 +28,8 @@ #include +#include + #include #include @@ -39,27 +41,54 @@ 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, std::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 : _vmPaths) + { + evmc::VM& vm = EVMHost::getVM(path.string()); + if (!m_evmHost && vm.has_capability(EVMC_CAPABILITY_EVM1)) + m_evmHost = make_shared(m_evmVersion, vm); + if (!m_ewasmHost && vm.has_capability(EVMC_CAPABILITY_EWASM)) + m_ewasmHost = make_shared(m_evmVersion, vm); + } + reset(); } -void ExecutionFramework::reset() +bool ExecutionFramework::supportsEwasm() const { - m_evmHost->reset(); + if (m_ewasmHost) + return m_ewasmHost->hasCapability(EVMC_CAPABILITY_EWASM); + return false; +} + +void ExecutionFramework::reset(bool useEwasm) +{ + if (useEwasm) + { + solAssert(supportsEwasm(), ""); + m_evmcHost = m_ewasmHost; + } + else + { + solAssert(m_evmHost != nullptr, ""); + m_evmcHost = m_evmHost; + } + + 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); } @@ -91,29 +120,29 @@ 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 { - return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; + return {EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const { return {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) { @@ -143,7 +172,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) @@ -162,7 +191,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) { @@ -177,12 +206,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) @@ -200,32 +229,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()}; @@ -233,13 +262,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 e55a12aa6..3e5abfbf5 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -57,7 +57,7 @@ class ExecutionFramework public: ExecutionFramework(); - explicit ExecutionFramework(langutil::EVMVersion _evmVersion); + explicit ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths); virtual ~ExecutionFramework() = default; virtual bytes const& compileAndRunWithoutCheck( @@ -251,7 +251,9 @@ private: } protected: - void reset(); + void reset(bool useEwasm = false); + + bool supportsEwasm() const; void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); void sendEther(Address const& _to, u256 const& _value); @@ -275,7 +277,13 @@ 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_evmcHost; + std::shared_ptr m_evmHost; + std::shared_ptr m_ewasmHost; + + std::vector const& m_vmPaths; bool m_transactionSuccessful = true; Address m_sender = account(0); diff --git a/test/TestCase.h b/test/TestCase.h index f846b7f87..483aebdbe 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -38,6 +38,7 @@ public: { std::string filename; langutil::EVMVersion evmVersion; + std::vector vmPaths; bool enforceCompileViaYul; }; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 5b45cc758..385f9be54 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -70,7 +70,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()); @@ -150,7 +150,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) initializeOptions(); - bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string()); + bool disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths); if (disableSemantics) { cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath ." << endl; diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 71e47aa0c..f1742d140 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 39744fa59..89b9db0d2 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, std::vector const& _vmPaths, bool enforceViaYul): + SolidityExecutionFramework(_evmVersion, _vmPaths), EVMVersionRestrictedTestCase(_filename), m_enforceViaYul(enforceViaYul) { @@ -84,157 +84,164 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { - for (bool compileViaYul: set{!m_runWithoutYul, m_runWithYul || m_enforceViaYul}) - { - try + for (bool compileToEwasm: compileViaYul && supportsEwasm() ? set{false, true} : set{false}) { - reset(); - bool success = true; - - m_compileViaYul = compileViaYul; - m_compileViaYulCanBeSet = false; - - if (compileViaYul) - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; - - for (auto& test: m_tests) - test.reset(); - - map libraries; - - bool constructed = false; - - for (auto& test: m_tests) + try { - 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( - 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; - } + bool success = true; - if (test.call().isConstructor) - { - if (m_transactionSuccessful == test.call().expectations.failure) - success = false; + m_compileViaYul = compileViaYul; + m_compileToEwasm = compileToEwasm; + m_compileViaYulCanBeSet = false; - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(bytes()); - } - else + reset(m_compileToEwasm); + + if (compileViaYul) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) + << _linePrefix << "Running via Yul" << (m_compileToEwasm ? " (ewasm):" : ":") << endl; + + for (auto& test: m_tests) + test.reset(); + + map libraries; + + bool constructed = false; + + for (auto& test: m_tests) { - bytes output; - if (test.call().useCallWithoutSignature) - output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); - else + 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( - 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() - ); + 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 ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) - success = false; + if (test.call().isConstructor) + { + if (m_transactionSuccessful == test.call().expectations.failure) + 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(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"); - 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; - } + output = callContractFunctionWithValueNoEncoding( + test.call().signature, test.call().value.value, test.call().arguments.rawBytes()); + } - if (!success && (m_runWithYul || !compileViaYul)) - { - 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); + 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())); + } } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - for (auto const& test: m_tests) + + if (success && !m_runWithYul && compileViaYul) { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); + 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; } - AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix - << "Attention: Updates on the test will apply the detected format displayed." << endl; - if (compileViaYul && m_runWithoutYul) + + if (!success && (m_runWithYul || !compileViaYul)) { - _stream << _linePrefix << endl << _linePrefix; - AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) - << "Note that the test passed without Yul."; + 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); + } _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 + throw; + } + catch (YulException const&) + { + // this should be an error in yul compilation or translation + throw; + } + catch (boost::exception const&) + { + if (compileViaYul && !m_runWithYul) + continue; + throw; + } + catch (std::exception const&) + { + if (compileViaYul && !m_runWithYul) + continue; + throw; + } + catch (...) + { + if (compileViaYul && !m_runWithYul) + continue; + throw; } } - 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) - continue; - 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 870d61d56..94d14a363 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; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 83e7ef95d..795b491f0 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -53,6 +53,13 @@ using namespace solidity::langutil; reset(); \ m_compileViaYul = true; \ { CODE } \ + if (supportsEwasm()) \ + { \ + reset(true); \ + m_compileViaYul = true; \ + m_compileToEwasm = true; \ + { CODE } \ + } \ } namespace solidity::frontend::test @@ -1015,12 +1022,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)); } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 5e4ebe088..5066da2da 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -46,6 +46,7 @@ bytes SolidityExecutionFramework::compileContract( sourceCode += "pragma experimental ABIEncoderV2;\n"; sourceCode += _sourceCode; m_compiler.reset(); + m_compiler.enableEwasmGeneration(m_compileToEwasm); m_compiler.setSources({{"", sourceCode}}); m_compiler.setLibraries(_libraryAddresses); m_compiler.setRevertStringBehaviour(m_revertStrings); @@ -65,18 +66,21 @@ bytes SolidityExecutionFramework::compileContract( evmasm::LinkerObject obj; if (m_compileViaYul) { - yul::AssemblyStack asmStack( - m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, - // Ignore optimiser settings here because we need Yul optimisation to - // get code that does not exhaust the stack. - OptimiserSettings::full() - ); - bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); - solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); - - asmStack.optimize(); - obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + if (m_compileToEwasm) + obj = m_compiler.ewasmObject(contractName); + else + { + yul::AssemblyStack asmStack( + m_evmVersion, + yul::AssemblyStack::Language::StrictAssembly, + // Ignore optimiser settings here because we need Yul optimisation to + // get code that does not exhaust the stack. + OptimiserSettings::full()); + bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); + solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); + asmStack.optimize(); + obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + } } else obj = m_compiler.object(contractName); diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 2410195fa..901831add 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -42,8 +42,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( @@ -66,11 +66,12 @@ public: ); protected: + solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_compileToEwasm = false; bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; - }; } // end namespaces diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 846a54415..8512e1a36 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -164,6 +164,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()) @@ -427,7 +428,7 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(solidity::test::CommonOptions::get()); - bool disableSemantics = !solidity::test::EVMHost::getVM(options.evmonePath.string()); + bool disableSemantics = !solidity::test::EVMHost::checkVmPaths(options.vmPaths); if (disableSemantics) { cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using --evmonepath ." << endl;