diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index a0711cd41756..d27e26c3e7b4 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -43,9 +43,9 @@ #include #include -#include #include #include +#include #include using namespace solidity; diff --git a/libevmasm/EVMAssemblyStack.cpp b/libevmasm/EVMAssemblyStack.cpp index 7cb05bf8bf00..bed4ff744ec4 100644 --- a/libevmasm/EVMAssemblyStack.cpp +++ b/libevmasm/EVMAssemblyStack.cpp @@ -74,13 +74,13 @@ void EVMAssemblyStack::assemble() LinkerObject const& EVMAssemblyStack::object(std::string const& _contractName) const { solAssert(_contractName == m_name); - return m_object; + return object(); } LinkerObject const& EVMAssemblyStack::runtimeObject(std::string const& _contractName) const { solAssert(_contractName == m_name); - return m_runtimeObject; + return runtimeObject(); } std::map EVMAssemblyStack::sourceIndices() const @@ -95,13 +95,13 @@ std::map EVMAssemblyStack::sourceIndices() const std::string const* EVMAssemblyStack::sourceMapping(std::string const& _contractName) const { solAssert(_contractName == m_name); - return &m_sourceMapping; + return &sourceMapping(); } std::string const* EVMAssemblyStack::runtimeSourceMapping(std::string const& _contractName) const { solAssert(_contractName == m_name); - return &m_runtimeSourceMapping; + return &runtimeSourceMapping(); } Json EVMAssemblyStack::ethdebug(std::string const& _contractName) const @@ -123,20 +123,30 @@ Json EVMAssemblyStack::ethdebug() const return {}; } -Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const +Json EVMAssemblyStack::assemblyJSON() const { - solAssert(_contractName == m_name); solAssert(m_evmAssembly); return m_evmAssembly->assemblyJSON(sourceIndices()); } -std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const +Json EVMAssemblyStack::assemblyJSON(std::string const& _contractName) const { solAssert(_contractName == m_name); + return assemblyJSON(); +} + +std::string EVMAssemblyStack::assemblyString(StringMap const& _sourceCodes) const +{ solAssert(m_evmAssembly); return m_evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); } +std::string EVMAssemblyStack::assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const +{ + solAssert(_contractName == m_name); + return assemblyString(_sourceCodes); +} + std::string const EVMAssemblyStack::filesystemFriendlyName(std::string const& _contractName) const { solAssert(_contractName == m_name); diff --git a/libevmasm/EVMAssemblyStack.h b/libevmasm/EVMAssemblyStack.h index 2fe63c11c2ec..444d444ba9e2 100644 --- a/libevmasm/EVMAssemblyStack.h +++ b/libevmasm/EVMAssemblyStack.h @@ -58,20 +58,26 @@ class EVMAssemblyStack: public AbstractAssemblyStack std::string const& name() const { return m_name; } + LinkerObject const& object() const { return m_object; } LinkerObject const& object(std::string const& _contractName) const override; + LinkerObject const& runtimeObject() const { return m_runtimeObject; } LinkerObject const& runtimeObject(std::string const& _contractName) const override; std::shared_ptr const& evmAssembly() const { return m_evmAssembly; } std::shared_ptr const& evmRuntimeAssembly() const { return m_evmRuntimeAssembly; } + std::string const& sourceMapping() const { return m_sourceMapping; } std::string const* sourceMapping(std::string const& _contractName) const override; + std::string const& runtimeSourceMapping() const { return m_runtimeSourceMapping; } std::string const* runtimeSourceMapping(std::string const& _contractName) const override; Json ethdebug(std::string const& _contractName) const override; Json ethdebugRuntime(std::string const& _contractName) const override; Json ethdebug() const override; + Json assemblyJSON() const; Json assemblyJSON(std::string const& _contractName) const override; + std::string assemblyString(StringMap const& _sourceCodes) const; std::string assemblyString(std::string const& _contractName, StringMap const& _sourceCodes) const override; std::string const filesystemFriendlyName(std::string const& _contractName) const override; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index dad9804d09a8..b5568763573a 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -26,7 +26,7 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::evmasm; -std::map const solidity::evmasm::c_instructions = +std::map> const solidity::evmasm::c_instructions = { { "STOP", Instruction::STOP }, { "ADD", Instruction::ADD }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 137144715296..cfdb85d2fa4c 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -322,6 +322,6 @@ InstructionInfo instructionInfo(Instruction _inst, langutil::EVMVersion _evmVers bool isValidInstruction(Instruction _inst); /// Convert from string mnemonic to Instruction type. -extern const std::map c_instructions; +extern const std::map> c_instructions; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da115f0d4a5b..c66963c190d4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -51,7 +51,11 @@ detect_stray_source_files("${libsolutil_sources}" "libsolutil/") set(libevmasm_sources libevmasm/Assembler.cpp + libevmasm/EVMAssemblyTest.cpp + libevmasm/EVMAssemblyTest.h libevmasm/Optimiser.cpp + libevmasm/PlainAssemblyParser.cpp + libevmasm/PlainAssemblyParser.h ) detect_stray_source_files("${libevmasm_sources}" "libevmasm/") diff --git a/test/Common.cpp b/test/Common.cpp index d39c6db1ea9e..3a6546d796d3 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -33,7 +34,6 @@ #include #include -#include namespace fs = boost::filesystem; namespace po = boost::program_options; @@ -110,10 +110,10 @@ CommonOptions::CommonOptions(std::string _caption): void CommonOptions::addOptions() { options.add_options() - ("evm-version", po::value(&evmVersionString), "which EVM version to use") + ("evm-version", po::value(&m_evmVersionString), "which EVM version to use") // "eof-version" is declared as uint64_t, since uint8_t will be parsed as character by boost. ("eof-version", po::value()->implicit_value(1u), "which EOF version to use") - ("testpath", po::value(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") + ("testpath", po::value(&this->testPath)->default_value(test::testPath()), "path to test files") ("vm", po::value>(&vmPaths), "path to evmc library, can be supplied multiple times.") ("batches", po::value(&this->batches)->default_value(1), "set number of batches to split the tests into") ("selected-batch", po::value(&this->selectedBatch)->default_value(0), "zero-based number of batch to execute") @@ -185,8 +185,7 @@ bool CommonOptions::parse(int argc, char const* const* argv) { // Request as uint64_t, since uint8_t will be parsed as character by boost. uint64_t eofVersion = arguments["eof-version"].as(); - if (eofVersion != 1) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid EOF version: " + std::to_string(eofVersion))); + solRequire(eofVersion == 1, ConfigException, "Invalid EOF version: " + std::to_string(eofVersion)); m_eofVersion = 1; } @@ -198,11 +197,7 @@ bool CommonOptions::parse(int argc, char const* const* argv) (parsedOption.original_tokens.size() == 1 && parsedOption.original_tokens.front().empty()) ) continue; // ignore empty options - std::stringstream errorMessage; - errorMessage << "Unrecognized option: "; - for (auto const& token: parsedOption.original_tokens) - errorMessage << token; - BOOST_THROW_EXCEPTION(std::runtime_error(errorMessage.str())); + solThrow(ConfigException, "Unrecognized option: " + util::joinHumanReadable(parsedOption.original_tokens, "")); } } catch (po::error const& exception) @@ -259,11 +254,10 @@ void CommonOptions::printSelectedOptions(std::ostream& _stream, std::string cons langutil::EVMVersion CommonOptions::evmVersion() const { - if (!evmVersionString.empty()) + if (!m_evmVersionString.empty()) { - auto version = langutil::EVMVersion::fromString(evmVersionString); - if (!version) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid EVM version: " + evmVersionString)); + auto version = langutil::EVMVersion::fromString(m_evmVersionString); + solRequire(version, ConfigException, "Invalid EVM version: " + m_evmVersionString); return *version; } else @@ -279,7 +273,7 @@ yul::EVMDialect const& CommonOptions::evmDialect() const CommonOptions const& CommonOptions::get() { if (!m_singleton) - BOOST_THROW_EXCEPTION(std::runtime_error("Options not yet constructed!")); + soltestAssert(false, "Options not yet constructed!"); return *m_singleton; } @@ -306,10 +300,21 @@ bool isValidSemanticTestPath(boost::filesystem::path const& _testPath) return true; } +std::set testFileExtensions() +{ + return { + ".sol", + ".yul", + ".asm", + ".asmjson", // Not .json because JSON files that do not represent test cases exist in some test dirs. + ".stack", + }; +} + boost::unit_test::precondition::predicate_t nonEOF() { return [](boost::unit_test::test_unit_id) { - return !solidity::test::CommonOptions::get().eofVersion().has_value(); + return !CommonOptions::get().eofVersion().has_value(); }; } @@ -325,13 +330,13 @@ bool loadVMs(CommonOptions const& _options) if (_options.disableSemanticTests) return true; - bool evmSupported = solidity::test::EVMHost::checkVmPaths(_options.vmPaths); + bool evmSupported = EVMHost::checkVmPaths(_options.vmPaths); if (!_options.disableSemanticTests && !evmSupported) { - std::cerr << "Unable to find " << solidity::test::evmoneFilename; + std::cerr << "Unable to find " << evmoneFilename; std::cerr << ". Please disable semantics tests with --no-semantic-tests or provide a path using --vm ." << std::endl; std::cerr << "You can download it at" << std::endl; - std::cerr << solidity::test::evmoneDownloadLink << std::endl; + std::cerr << evmoneDownloadLink << std::endl; return false; } return true; diff --git a/test/Common.h b/test/Common.h index 27c2081346fc..01dbf86f6914 100644 --- a/test/Common.h +++ b/test/Common.h @@ -48,7 +48,10 @@ static constexpr auto evmoneFilename = "libevmone.so"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz"; #endif -struct ConfigException: public util::Exception {}; +struct SoltestError: public util::Exception {}; +struct ConfigException: public SoltestError {}; +struct ValidationError: public SoltestError {}; +struct ExecutionError: public SoltestError {}; struct CommonOptions { @@ -76,7 +79,7 @@ struct CommonOptions virtual void addOptions(); // @returns true if the program should continue, false if it should exit immediately without // reporting an error. - // Throws ConfigException or std::runtime_error if parsing fails. + // Throws ConfigException if parsing fails. virtual bool parse(int argc, char const* const* argv); // Throws a ConfigException on error virtual void validate() const; @@ -97,7 +100,7 @@ struct CommonOptions boost::program_options::options_description options; private: - std::string evmVersionString; + std::string m_evmVersionString; std::optional m_eofVersion; static std::unique_ptr m_singleton; }; @@ -107,6 +110,9 @@ struct CommonOptions /// Note: @p _testPath can be relative but must include at least the `/test/libsolidity/semanticTests/` part bool isValidSemanticTestPath(boost::filesystem::path const& _testPath); +/// Returns a list of file extensions allowed for test files. +std::set testFileExtensions(); + /// Helper that can be used to skip tests when the EVM version selected on the command line /// is older than @p _minEVMVersion. /// @return A predicate (function) that can be passed into @a boost::unit_test::precondition(). diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp index ccf4added41e..e53b9d803552 100644 --- a/test/CommonSyntaxTest.cpp +++ b/test/CommonSyntaxTest.cpp @@ -30,9 +30,8 @@ #include #include -#include -#include -#include +#include +#include using namespace solidity; using namespace solidity::util; @@ -42,7 +41,6 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace solidity::test; using namespace boost::unit_test; -namespace fs = boost::filesystem; namespace { @@ -50,7 +48,7 @@ namespace int parseUnsignedInteger(std::string::iterator& _it, std::string::iterator _end) { if (_it == _end || !util::isDigit(*_it)) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid test expectation. Source location expected.")); + solThrow(ValidationError, "Invalid test expectation. Source location expected."); int result = 0; while (_it != _end && util::isDigit(*_it)) { @@ -258,8 +256,7 @@ std::vector CommonSyntaxTest::parseExpectations(std::istream& _ std::string errorTypeStr(typeBegin, it); std::optional errorType = Error::parseErrorType(errorTypeStr); - if (!errorType.has_value()) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid error type: " + errorTypeStr)); + solRequire(errorType.has_value(), ValidationError, "Invalid error type: " + errorTypeStr); skipWhitespace(it, line.end()); diff --git a/test/CommonSyntaxTest.h b/test/CommonSyntaxTest.h index fb46c37a6401..28aa27a5f07f 100644 --- a/test/CommonSyntaxTest.h +++ b/test/CommonSyntaxTest.h @@ -27,7 +27,6 @@ #include #include #include -#include namespace solidity::test { diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 983615312efd..1bfbeec08a5a 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -29,6 +29,8 @@ #endif #include + +#include #include #if defined(__GNUC__) && !defined(__clang__) // GCC-specific pragma @@ -91,8 +93,7 @@ bool EVMHost::checkVmPaths(std::vector const& _vmPaths) if (vm.has_capability(EVMC_CAPABILITY_EVM1)) { - if (evmVmFound) - BOOST_THROW_EXCEPTION(std::runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.")); + solRequire(!evmVmFound, ConfigException, "Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm."); evmVmFound = true; } } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 44812e230ab0..3dad1fd43114 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -34,8 +34,6 @@ #include #include -#include - #include #include diff --git a/test/FilesystemUtils.cpp b/test/FilesystemUtils.cpp index edd9a0d6d11d..f87a73e43e08 100644 --- a/test/FilesystemUtils.cpp +++ b/test/FilesystemUtils.cpp @@ -18,8 +18,12 @@ #include +#include + #include +#include + #include using namespace solidity; @@ -37,19 +41,19 @@ void solidity::test::createFilesWithParentDirs(std::set newFile << _content; if (newFile.fail() || !boost::filesystem::exists(path)) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create an empty file: \"" + path.string() + "\".")); + solThrow(ExecutionError, "Failed to create an empty file: \"" + path.string() + "\"."); } } void solidity::test::createFileWithContent(boost::filesystem::path const& _path, std::string const& _content) { if (boost::filesystem::is_regular_file(_path)) - BOOST_THROW_EXCEPTION(std::runtime_error("File already exists: \"" + _path.string() + "\".")); + solThrow(ExecutionError, "File already exists: \"" + _path.string() + "\"."); // Use binary mode to avoid line ending conversion on Windows. std::ofstream newFile(_path.string(), std::ofstream::binary); if (newFile.fail() || !boost::filesystem::is_regular_file(_path)) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create a file: \"" + _path.string() + "\".")); + solThrow(ExecutionError, "Failed to create a file: \"" + _path.string() + "\"."); newFile << _content; } @@ -79,9 +83,10 @@ bool solidity::test::createSymlinkIfSupportedByFilesystem( ) return false; else - BOOST_THROW_EXCEPTION(std::runtime_error( - "Failed to create a symbolic link: \"" + _linkName.string() + "\"" - " -> " + _targetPath.string() + "\"." - " " + symlinkCreationError.message() + "." + solThrow(ExecutionError, fmt::format( + "Failed to create a symbolic link: \"{}\" -> \"{}\". {}.", + _linkName.string(), + _targetPath.string(), + symlinkCreationError.message() )); } diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index e043fcdbe676..de6d6c5e9b8d 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -19,10 +19,10 @@ #pragma once #include + #include #include #include -#include #include #include #include @@ -30,6 +30,7 @@ #include #include #include + #include #include #include @@ -42,6 +43,10 @@ #include #include +#include + +#include + #include namespace solidity::frontend::test @@ -64,6 +69,7 @@ struct Testsuite Testsuite const g_interactiveTestsuites[] = { /* Title Path Subpath SMT NeedsVM Creator function */ + {"EVM Assembly", "libevmasm", "evmAssemblyTests", false, false, &evmasm::test::EVMAssemblyTest::create}, {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, diff --git a/test/TestCase.cpp b/test/TestCase.cpp index da47c26fde90..13a40f5dd78e 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -20,17 +20,20 @@ #include #include +#include #include #include +#include #include -#include +using namespace std::string_literals; using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace solidity::util; +using namespace solidity::test; void TestCase::printSettings(std::ostream& _stream, const std::string& _linePrefix, const bool) { @@ -50,10 +53,10 @@ void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _l bool TestCase::isTestFilename(boost::filesystem::path const& _filename) { - std::string extension = _filename.extension().string(); - return (extension == ".sol" || extension == ".yul" || extension == ".stack") && - !boost::starts_with(_filename.string(), "~") && - !boost::starts_with(_filename.string(), "."); + return + testFileExtensions().contains(_filename.extension().string()) && + !boost::starts_with(_filename.string(), "~") && + !boost::starts_with(_filename.string(), "."); } bool TestCase::shouldRun() @@ -65,7 +68,7 @@ bool TestCase::shouldRun() void TestCase::expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c) { if (_it == _end || *_it != _c) - BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Invalid test expectation. Expected: \"") + _c + "\".")); + solThrow(ValidationError, "Invalid test expectation. Expected: \""s + _c + "\"."); ++_it; } @@ -81,7 +84,9 @@ void TestCase::printUpdatedExpectations(std::ostream& _stream, std::string const TestCase::TestResult TestCase::checkResult(std::ostream& _stream, const std::string& _linePrefix, bool const _formatted) { - if (m_expectation != m_obtainedResult) + // NOTE: Test cases usually use parseSimpleExpectations(), which ensures that m_expectations ends + // with a newline, so count m_obtainedResult as a match even if it does not. + if (m_expectation != m_obtainedResult && m_expectation != m_obtainedResult + '\n') { std::string nextIndentLevel = _linePrefix + " "; util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) @@ -91,6 +96,13 @@ TestCase::TestResult TestCase::checkResult(std::ostream& _stream, const std::str util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) << _linePrefix << "Obtained result:" << std::endl; printPrefixed(_stream, m_obtainedResult, nextIndentLevel); + + if (boost::trim_all_copy(m_expectation) == boost::trim_all_copy(m_obtainedResult)) + // NOTE: This will catch `a b` vs `a b` and ` ab ` vs `ab` but not `ab` vs `a b`. + util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::YELLOW}) + << "\n" + << _linePrefix << "NOTE: Results differ only in the amount of whitespace." << std::endl; + return TestResult::Failure; } return TestResult::Success; @@ -119,10 +131,9 @@ void EVMVersionRestrictedTestCase::processEVMVersionSetting() version = std::make_optional(); else version = langutil::EVMVersion::fromString(versionString); - if (!version) - BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid EVM version: \"" + versionString + "\""}); + solRequire(version, ValidationError, "Invalid EVM version: \"" + versionString + "\""); - langutil::EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); + langutil::EVMVersion evmVersion = CommonOptions::get().evmVersion(); bool comparisonResult; if (comparator == ">") comparisonResult = evmVersion > version; @@ -137,7 +148,7 @@ void EVMVersionRestrictedTestCase::processEVMVersionSetting() else if (comparator == "!") comparisonResult = !(evmVersion == version); else - BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid EVM comparator: \"" + comparator + "\""}); + solThrow(ValidationError, "Invalid EVM comparator: \"" + comparator + "\""); if (!comparisonResult) m_shouldRun = false; @@ -145,9 +156,9 @@ void EVMVersionRestrictedTestCase::processEVMVersionSetting() void EVMVersionRestrictedTestCase::processBytecodeFormatSetting() { - std::optional eofVersion = solidity::test::CommonOptions::get().eofVersion(); + std::optional eofVersion = CommonOptions::get().eofVersion(); // EOF only available since Osaka - solAssert(!eofVersion.has_value() || solidity::test::CommonOptions::get().evmVersion().supportsEOF()); + solAssert(!eofVersion.has_value() || CommonOptions::get().evmVersion().supportsEOF()); std::string bytecodeFormatString = m_reader.stringSetting("bytecodeFormat", "legacy,>=EOFv1"); if (bytecodeFormatString == "legacy,>=EOFv1" || bytecodeFormatString == ">=EOFv1,legacy") @@ -159,7 +170,7 @@ void EVMVersionRestrictedTestCase::processBytecodeFormatSetting() else if (bytecodeFormatString == ">=EOFv1" && !eofVersion.has_value()) m_shouldRun = false; else if (bytecodeFormatString != "legacy" && bytecodeFormatString != ">=EOFv1" ) - BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid bytecodeFormat flag: \"" + bytecodeFormatString + "\""}); + solThrow(ValidationError, "Invalid bytecodeFormat flag: \"" + bytecodeFormatString + "\""); } EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename): diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp index da6174cbe9bd..95c3aa0147f8 100644 --- a/test/TestCaseReader.cpp +++ b/test/TestCaseReader.cpp @@ -24,13 +24,15 @@ #include using namespace solidity::frontend::test; +using namespace solidity::test; namespace fs = boost::filesystem; -TestCaseReader::TestCaseReader(std::string const& _filename): m_fileStream(_filename), m_fileName(_filename) +TestCaseReader::TestCaseReader(std::string const& _filename): + m_fileStream(_filename), + m_fileName(_filename) { - if (!m_fileStream) - BOOST_THROW_EXCEPTION(std::runtime_error("Cannot open file: \"" + _filename + "\".")); + solRequire(m_fileStream, ValidationError, "Cannot open file: \"" + _filename + "\"."); m_fileStream.exceptions(std::ios::badbit); std::tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream); @@ -47,7 +49,7 @@ TestCaseReader::TestCaseReader(std::istringstream const& _str) std::string const& TestCaseReader::source() const { if (m_sources.sources.size() != 1) - BOOST_THROW_EXCEPTION(std::runtime_error("Expected single source definition, but got multiple sources.")); + solThrow(ValidationError, "Expected single source definition, but got multiple sources."); return m_sources.sources.at(m_sources.mainSourceFile); } @@ -68,7 +70,7 @@ bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue) if (value == "true") return true; - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid Boolean value: " + value + ".")); + solThrow(ValidationError, "Invalid Boolean value: " + value + "."); } size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue) @@ -94,10 +96,10 @@ std::string TestCaseReader::stringSetting(std::string const& _name, std::string void TestCaseReader::ensureAllSettingsRead() const { if (!m_unreadSettings.empty()) - BOOST_THROW_EXCEPTION(std::runtime_error( - "Unknown setting(s): " + - util::joinHumanReadable(m_unreadSettings | ranges::views::keys) - )); + solThrow( + ValidationError, + "Unknown setting(s): " + util::joinHumanReadable(m_unreadSettings | ranges::views::keys) + ); } std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(std::istream& _stream) @@ -137,7 +139,7 @@ std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumb line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() )); if (sources.count(currentSourceName)) - BOOST_THROW_EXCEPTION(std::runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".")); + solThrow(ValidationError, "Multiple definitions of test source \"" + currentSourceName + "\"."); } else if (boost::algorithm::starts_with(line, externalSourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) { @@ -163,16 +165,16 @@ std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumb if (!externalSourceTarget.is_relative() || !externalSourceTarget.root_path().empty()) // NOTE: UNC paths (ones starting with // or \\) are considered relative by Boost // since they have an empty root directory (but non-empty root name). - BOOST_THROW_EXCEPTION(std::runtime_error("External Source paths need to be relative to the location of the test case.")); + solThrow(ValidationError, "External Source paths need to be relative to the location of the test case."); fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget; std::string externalSourceContent; if (!fs::exists(externalSourceFullPath)) - BOOST_THROW_EXCEPTION(std::runtime_error("External Source '" + externalSourceTarget.string() + "' not found.")); + solThrow(ValidationError, "External Source '" + externalSourceTarget.string() + "' not found."); else externalSourceContent = util::readFileAsString(externalSourceFullPath); if (sources.count(externalSourceName)) - BOOST_THROW_EXCEPTION(std::runtime_error("Multiple definitions of test source \"" + externalSourceName + "\".")); + solThrow(ValidationError, "Multiple definitions of test source \"" + externalSourceName + "\"."); sources[externalSourceName] = externalSourceContent; externalSources[externalSourceName] = externalSourceTarget; } @@ -183,7 +185,7 @@ std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumb { size_t colon = line.find(':'); if (colon == std::string::npos) - BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Expected \":\" inside setting."))); + solThrow(ValidationError, "Expected \":\" inside setting."); std::string key = line.substr(comment.size(), colon - comment.size()); std::string value = line.substr(colon + 1); boost::algorithm::trim(key); @@ -191,7 +193,7 @@ std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumb m_settings[key] = value; } else - BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Expected \"//\" or \"// ---\" to terminate settings and source."))); + solThrow(ValidationError, "Expected \"//\" or \"// ---\" to terminate settings and source."); } // Register the last source as the main one sources[currentSourceName] = currentSource; @@ -208,6 +210,6 @@ std::string TestCaseReader::parseSimpleExpectations(std::istream& _file) else if (line == "//") result += "\n"; else - BOOST_THROW_EXCEPTION(std::runtime_error("Test expectations must start with \"// \".")); + solThrow(ValidationError, "Test expectations must start with \"// \"."); return result; } diff --git a/test/TestCaseReader.h b/test/TestCaseReader.h index d1ef95be3c2c..5397020c80cb 100644 --- a/test/TestCaseReader.h +++ b/test/TestCaseReader.h @@ -18,15 +18,19 @@ #pragma once +#include + #include #include -#include - #include #include +#include + +#include + #include #include #include @@ -59,6 +63,7 @@ class TestCaseReader std::size_t lineNumber() const { return m_lineNumber; } std::map const& settings() const { return m_settings; } std::ifstream& stream() { return m_fileStream; } + boost::filesystem::path const& fileName() const { return m_fileName; } std::string simpleExpectations(); @@ -90,10 +95,12 @@ E TestCaseReader::enumSetting(std::string const& _name, std::map std::string value = stringSetting(_name, _defaultChoice); - if (_choices.count(value) == 0) - BOOST_THROW_EXCEPTION(std::runtime_error( - "Invalid Enum value: " + value + ". Available choices: " + util::joinHumanReadable(_choices | ranges::views::keys) + "." - )); + solRequire(_choices.count(value) != 0, solidity::test::ValidationError, fmt::format( + "Invalid choice in '{}' setting: {}.\nAvailable choices: {}.", + _name, + value, + util::joinHumanReadable(_choices | ranges::views::keys) + )); return _choices.at(value); } diff --git a/test/libevmasm/EVMAssemblyTest.cpp b/test/libevmasm/EVMAssemblyTest.cpp new file mode 100644 index 000000000000..efb737cc47eb --- /dev/null +++ b/test/libevmasm/EVMAssemblyTest.cpp @@ -0,0 +1,179 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +using namespace std::string_literals; +using namespace solidity; +using namespace solidity::test; +using namespace solidity::evmasm; +using namespace solidity::evmasm::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace solidity::langutil; +using namespace solidity::util; + +std::vector const EVMAssemblyTest::c_outputLabels = { + "InputAssemblyJSON", + "Assembly", + "Bytecode", + "Opcodes", + "SourceMappings", +}; + +std::unique_ptr EVMAssemblyTest::create(Config const& _config) +{ + return std::make_unique(_config.filename); +} + +EVMAssemblyTest::EVMAssemblyTest(std::string const& _filename): + EVMVersionRestrictedTestCase(_filename) +{ + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); + + if (boost::algorithm::ends_with(_filename, ".asmjson")) + m_assemblyFormat = AssemblyFormat::JSON; + else if (boost::algorithm::ends_with(_filename, ".asm")) + m_assemblyFormat = AssemblyFormat::Plain; + else + solThrow(ValidationError, "Not an assembly test: \"" + _filename + "\". Allowed extensions: .asm, .asmjson."); + + m_selectedOutputs = m_reader.stringSetting("outputs", "Assembly,Bytecode,Opcodes,SourceMappings"); + OptimisationPreset optimizationPreset = m_reader.enumSetting( + "optimizationPreset", + { + {"none", OptimisationPreset::None}, + {"minimal", OptimisationPreset::Minimal}, + {"standard", OptimisationPreset::Standard}, + {"full", OptimisationPreset::Full}, + }, + "none" + ); + m_optimizerSettings = Assembly::OptimiserSettings::translateSettings(OptimiserSettings::preset(optimizationPreset)); + m_optimizerSettings.expectedExecutionsPerDeployment = m_reader.sizetSetting( + "optimizer.expectedExecutionsPerDeployment", + m_optimizerSettings.expectedExecutionsPerDeployment + ); + + auto const optimizerComponentSetting = [&](std::string const& _component, bool& _setting) { + _setting = m_reader.boolSetting("optimizer." + _component, _setting); + }; + optimizerComponentSetting("inliner", m_optimizerSettings.runInliner); + optimizerComponentSetting("jumpdestRemover", m_optimizerSettings.runJumpdestRemover); + optimizerComponentSetting("peephole", m_optimizerSettings.runPeephole); + optimizerComponentSetting("deduplicate", m_optimizerSettings.runDeduplicate); + optimizerComponentSetting("cse", m_optimizerSettings.runCSE); + optimizerComponentSetting("constantOptimizer", m_optimizerSettings.runConstantOptimiser); + + // TODO: Enable when assembly import for EOF is implemented. + if (CommonOptions::get().eofVersion().has_value()) + m_shouldRun = false; +} + +TestCase::TestResult EVMAssemblyTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) +{ + EVMAssemblyStack evmAssemblyStack( + CommonOptions::get().evmVersion(), + CommonOptions::get().eofVersion(), + m_optimizerSettings + ); + + evmAssemblyStack.selectDebugInfo(DebugInfoSelection::AllExceptExperimental()); + + std::string assemblyJSON; + switch (m_assemblyFormat) + { + case AssemblyFormat::JSON: + assemblyJSON = m_source; + break; + case AssemblyFormat::Plain: + assemblyJSON = jsonPrint( + PlainAssemblyParser{}.parse(m_reader.fileName().filename().string(), m_source), + {JsonFormat::Pretty, 4} + ); + break; + } + + try + { + evmAssemblyStack.parseAndAnalyze(m_reader.fileName().filename().string(), assemblyJSON); + } + catch (AssemblyImportException const& _exception) + { + m_obtainedResult = "AssemblyImportException: "s + _exception.what() + "\n"; + return checkResult(_stream, _linePrefix, _formatted); + } + + try + { + evmAssemblyStack.assemble(); + } + catch (Error const& _error) + { + // TODO: EVMAssemblyStack should catch these on its own and provide an error reporter. + soltestAssert(_error.comment(), "Errors must include a message for the user."); + m_obtainedResult = Error::formatErrorType(_error.type()) + ": " + *_error.comment() + "\n"; + return checkResult(_stream, _linePrefix, _formatted); + } + soltestAssert(evmAssemblyStack.compilationSuccessful()); + + auto const produceOutput = [&](std::string const& _output) { + if (_output == "InputAssemblyJSON") + return assemblyJSON; + if (_output == "Assembly") + return evmAssemblyStack.assemblyString({{m_reader.fileName().filename().string(), m_source}}); + if (_output == "Bytecode") + return util::toHex(evmAssemblyStack.object().bytecode); + if (_output == "Opcodes") + return disassemble(evmAssemblyStack.object().bytecode, CommonOptions::get().evmVersion()); + if (_output == "SourceMappings") + return evmAssemblyStack.sourceMapping(); + soltestAssert(false); + unreachable(); + }; + + std::set selectedOutputSet; + boost::split(selectedOutputSet, m_selectedOutputs, boost::is_any_of(",")); + for (std::string const& output: c_outputLabels) + if (selectedOutputSet.contains(output)) + { + if (!m_obtainedResult.empty() && m_obtainedResult.back() != '\n') + m_obtainedResult += "\n"; + + // Don't trim on the left to avoid stripping indentation. + std::string content = produceOutput(output); + boost::trim_right(content); + std::string separator = (content.empty() ? "" : (output == "Assembly" ? "\n" : " ")); + m_obtainedResult += output + ":" + separator + content; + } + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libevmasm/EVMAssemblyTest.h b/test/libevmasm/EVMAssemblyTest.h new file mode 100644 index 000000000000..041409393fdc --- /dev/null +++ b/test/libevmasm/EVMAssemblyTest.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace solidity::evmasm::test +{ + +class EVMAssemblyTest: public frontend::test::EVMVersionRestrictedTestCase +{ +public: + static std::unique_ptr create(Config const& _config); + + EVMAssemblyTest(std::string const& _filename); + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + +private: + enum class AssemblyFormat + { + JSON, + Plain, + }; + + static std::vector const c_outputLabels; + + AssemblyFormat m_assemblyFormat{}; + std::string m_selectedOutputs; + evmasm::Assembly::OptimiserSettings m_optimizerSettings; +}; + +} diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index f4b7034de544..7a4ef0fac909 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -36,7 +36,6 @@ #include #include -#include #include using namespace solidity::langutil; diff --git a/test/libevmasm/PlainAssemblyParser.cpp b/test/libevmasm/PlainAssemblyParser.cpp new file mode 100644 index 000000000000..9eea133c5b50 --- /dev/null +++ b/test/libevmasm/PlainAssemblyParser.cpp @@ -0,0 +1,182 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include + +#include + +#include + +#include + +#include + +#include + +using namespace std::string_literals; +using namespace solidity; +using namespace solidity::test; +using namespace solidity::evmasm; +using namespace solidity::evmasm::test; +using namespace solidity::langutil; + +Json PlainAssemblyParser::parse(std::string _sourceName, std::string const& _source) +{ + m_sourceName = std::move(_sourceName); + Json codeJSON = Json::array(); + std::istringstream sourceStream(_source); + while (getline(sourceStream, m_line)) + { + advanceLine(m_line); + if (m_lineTokens.empty()) + continue; + + if (c_instructions.contains(currentToken().value)) + { + expectNoMoreArguments(); + codeJSON.push_back({{"name", currentToken().value}}); + } + else if (currentToken().value == "PUSH") + { + if (hasMoreTokens() && nextToken().value == "[tag]") + { + advanceToken(); + std::string_view tagID = expectArgument(); + expectNoMoreArguments(); + codeJSON.push_back({{"name", "PUSH [tag]"}, {"value", tagID}}); + } + else + { + std::string_view immediateArgument = expectArgument(); + expectNoMoreArguments(); + + if (!immediateArgument.starts_with("0x")) + solThrow(ValidationError, formatError("The immediate argument to PUSH must be a hex number prefixed with '0x'.")); + + immediateArgument.remove_prefix("0x"s.size()); + codeJSON.push_back({{"name", "PUSH"}, {"value", immediateArgument}}); + } + } + else if (currentToken().value == "tag") + { + std::string_view tagID = expectArgument(); + expectNoMoreArguments(); + + codeJSON.push_back({{"name", "tag"}, {"value", tagID}}); + codeJSON.push_back({{"name", "JUMPDEST"}}); + } + else + solThrow(ValidationError, formatError("Unknown instruction.")); + } + return {{".code", codeJSON}}; +} + +PlainAssemblyParser::Token const& PlainAssemblyParser::currentToken() const +{ + soltestAssert(m_tokenIndex < m_lineTokens.size()); + return m_lineTokens[m_tokenIndex]; +} + +PlainAssemblyParser::Token const& PlainAssemblyParser::nextToken() const +{ + soltestAssert(m_tokenIndex + 1 < m_lineTokens.size()); + return m_lineTokens[m_tokenIndex + 1]; +} + +bool PlainAssemblyParser::advanceToken() +{ + if (!hasMoreTokens()) + return false; + + ++m_tokenIndex; + return true; +} + +std::string_view PlainAssemblyParser::expectArgument() +{ + bool hasArgument = advanceToken(); + if (!hasArgument) + solThrow(ValidationError, formatError("Missing argument(s).")); + + return currentToken().value; +} + +void PlainAssemblyParser::expectNoMoreArguments() +{ + bool hasArgument = advanceToken(); + if (hasArgument) + solThrow(ValidationError, formatError("Too many arguments.")); +} + +void PlainAssemblyParser::advanceLine(std::string_view _line) +{ + ++m_lineNumber; + m_line = _line; + m_lineTokens = tokenizeLine(m_line); + m_tokenIndex = 0; +} + +std::vector PlainAssemblyParser::tokenizeLine(std::string_view _line) +{ + auto const notWhiteSpace = [](char _c) { return !isWhiteSpace(_c); }; + + std::vector tokens; + auto tokenLocation = boost::find_token(_line, notWhiteSpace, boost::token_compress_on); + while (!tokenLocation.empty()) + { + std::string_view value{tokenLocation.begin(), tokenLocation.end()}; + if (value.starts_with("//")) + break; + + tokens.push_back({ + .value = value, + .position = static_cast(std::distance(_line.begin(), tokenLocation.begin())), + }); + soltestAssert(!value.empty()); + soltestAssert(tokens.back().position < _line.size()); + soltestAssert(tokens.back().position + value.size() <= _line.size()); + + std::string_view tail{tokenLocation.end(), _line.end()}; + tokenLocation = boost::find_token(tail, notWhiteSpace, boost::token_compress_on); + } + + return tokens; +} + +std::string PlainAssemblyParser::formatError(std::string_view _message) const +{ + soltestAssert(currentToken().value.size() >= 1); + + std::string lineNumberString = std::to_string(m_lineNumber); + std::string padding(lineNumberString.size(), ' '); + std::string underline = std::string(currentToken().position, ' ') + std::string(currentToken().value.size(), '^'); + return fmt::format( + "Error while parsing plain assembly: {}\n" + "{}--> {}\n" + "{} | \n" + "{} | {}\n" + "{} | {}\n", + _message, + padding, m_sourceName, + padding, + m_lineNumber, m_line, + padding, underline + ); +} diff --git a/test/libevmasm/PlainAssemblyParser.h b/test/libevmasm/PlainAssemblyParser.h new file mode 100644 index 000000000000..abaf1d1dc78a --- /dev/null +++ b/test/libevmasm/PlainAssemblyParser.h @@ -0,0 +1,79 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include +#include + +namespace solidity::evmasm::test +{ + +/// Parser for the plain assembly format. The format is meant to be good enough for humans to read +/// while being sstraightforward to map the assembly JSON format that solc can import. +/// +/// Syntax: +/// - Every line consists of zero or more whitespace-separated tokens. +/// - A token that begins with `//` starts a comment, which extends to the end of the line. +/// - A non-empty line represents a single assembly item. +/// - The name of the item is the first thing on the line and may consist of one or more tokens. +/// - One or more arguments follow the name. +/// +/// Supported items: +/// - All instruction names. +/// - PUSH +/// - PUSH [tag] +/// - tag +class PlainAssemblyParser +{ +public: + /// Parses plain assembly format and returns the equivalent assembly JSON. + /// Errors are reported by throwing ValidationError. + Json parse(std::string _sourceName, std::string const& _source); + +protected: + struct Token + { + std::string_view value; ///< Substring of m_line that represents a complete token. + size_t position; ///< Position of the first character of the token within m_line. + }; + + Token const& currentToken() const; + Token const& nextToken() const; + bool hasMoreTokens() const { return m_tokenIndex + 1 < m_lineTokens.size(); } + + bool advanceToken(); + std::string_view expectArgument(); + void expectNoMoreArguments(); + void advanceLine(std::string_view _line); + + static std::vector tokenizeLine(std::string_view _line); + std::string formatError(std::string_view _message) const; + +private: + std::string m_sourceName; ///< Name of the file the source comes from. + size_t m_lineNumber = 0; ///< The number of the current line within the source, 1-based. + std::string m_line; ///< The current line, unparsed. + std::vector m_lineTokens; ///< Decomposition of the current line into tokens (does not include comments). + size_t m_tokenIndex = 0; ///< Points at a token within m_lineTokens. +}; + +} diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/code_generation_error.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/code_generation_error.asmjson new file mode 100644 index 000000000000..4297c5f35b80 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/code_generation_error.asmjson @@ -0,0 +1,14 @@ +{ + ".code": [ + {"name": "PUSH [$]", "value": "0"} + ], + ".data": { + "0": { + ".code": [ + {"name": "PUSHIMMUTABLE", "value": "x"} + ] + } + } +} +// ---- +// CodeGenerationError: Some immutables were read from but never assigned, possibly because of optimization. diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/comments.asm b/test/libevmasm/evmAssemblyTests/isoltestTesting/comments.asm new file mode 100644 index 000000000000..13dca3ad1b2f --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/comments.asm @@ -0,0 +1,34 @@ +// +//// comment + // comment +CALLVALUE // 0xff +CALLVALUE //0xff + +PUSH 0xff // comment // //0xff +// + +// +// ==== +// outputs: InputAssemblyJSON,Assembly,Bytecode,Opcodes,SourceMappings +// ---- +// InputAssemblyJSON: { +// ".code": [ +// { +// "name": "CALLVALUE" +// }, +// { +// "name": "CALLVALUE" +// }, +// { +// "name": "PUSH", +// "value": "ff" +// } +// ] +// } +// Assembly: +// callvalue +// callvalue +// 0xff +// Bytecode: 343460ff +// Opcodes: CALLVALUE CALLVALUE PUSH1 0xFF +// SourceMappings: :::-:0;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/invalid_json.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/invalid_json.asmjson new file mode 100644 index 000000000000..a834108d1ad7 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/invalid_json.asmjson @@ -0,0 +1,3 @@ +{ +// ---- +// AssemblyImportException: Could not parse JSON file. diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/jumps.asm b/test/libevmasm/evmAssemblyTests/isoltestTesting/jumps.asm new file mode 100644 index 000000000000..be92671d8141 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/jumps.asm @@ -0,0 +1,56 @@ +PUSH [tag] 1 +JUMP +tag 1 +PUSH 0x01 +JUMPI +PUSH [tag] 0x012AB +tag 0x012AB +// ==== +// outputs: InputAssemblyJSON,Assembly,Bytecode,Opcodes,SourceMappings +// ---- +// InputAssemblyJSON: { +// ".code": [ +// { +// "name": "PUSH [tag]", +// "value": "1" +// }, +// { +// "name": "JUMP" +// }, +// { +// "name": "tag", +// "value": "1" +// }, +// { +// "name": "JUMPDEST" +// }, +// { +// "name": "PUSH", +// "value": "01" +// }, +// { +// "name": "JUMPI" +// }, +// { +// "name": "PUSH [tag]", +// "value": "0x012AB" +// }, +// { +// "name": "tag", +// "value": "0x012AB" +// }, +// { +// "name": "JUMPDEST" +// } +// ] +// } +// Assembly: +// jump(tag_1) +// tag_1: +// 0x01 +// jumpi +// tag_4779 +// tag_4779: +// Bytecode: 6003565b60015760095b +// Opcodes: PUSH1 0x3 JUMP JUMPDEST PUSH1 0x1 JUMPI PUSH1 0x9 JUMPDEST +// SourceMappings: :::-:0;;;;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/operations.asm b/test/libevmasm/evmAssemblyTests/isoltestTesting/operations.asm new file mode 100644 index 000000000000..923b0a6b1f37 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/operations.asm @@ -0,0 +1,53 @@ +NUMBER +SLOAD +ADDRESS +ORIGIN +ADD +DUP1 +SWAP1 +MSTORE8 +STOP +// ==== +// outputs: InputAssemblyJSON,Assembly,Bytecode,Opcodes,SourceMappings +// ---- +// InputAssemblyJSON: { +// ".code": [ +// { +// "name": "NUMBER" +// }, +// { +// "name": "SLOAD" +// }, +// { +// "name": "ADDRESS" +// }, +// { +// "name": "ORIGIN" +// }, +// { +// "name": "ADD" +// }, +// { +// "name": "DUP1" +// }, +// { +// "name": "SWAP1" +// }, +// { +// "name": "MSTORE8" +// }, +// { +// "name": "STOP" +// } +// ] +// } +// Assembly: +// sload(number) +// add(origin, address) +// dup1 +// swap1 +// mstore8 +// stop +// Bytecode: 435430320180905300 +// Opcodes: NUMBER SLOAD ADDRESS ORIGIN ADD DUP1 SWAP1 MSTORE8 STOP +// SourceMappings: :::-:0;;;;;;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/push.asm b/test/libevmasm/evmAssemblyTests/isoltestTesting/push.asm new file mode 100644 index 000000000000..51bc160507c7 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/push.asm @@ -0,0 +1,35 @@ +PUSH 0x0 +PUSH 0x1 +PUSH 0x0123456789ABCDEF +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// ==== +// outputs: InputAssemblyJSON,Assembly,Bytecode,Opcodes,SourceMappings +// ---- +// InputAssemblyJSON: { +// ".code": [ +// { +// "name": "PUSH", +// "value": "0" +// }, +// { +// "name": "PUSH", +// "value": "1" +// }, +// { +// "name": "PUSH", +// "value": "0123456789ABCDEF" +// }, +// { +// "name": "PUSH", +// "value": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" +// } +// ] +// } +// Assembly: +// 0x00 +// 0x01 +// 0x0123456789abcdef +// 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// Bytecode: 5f6001670123456789abcdef7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// Opcodes: PUSH0 PUSH1 0x1 PUSH8 0x123456789ABCDEF PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// SourceMappings: :::-:0;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_eof_version.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_eof_version.asmjson new file mode 100644 index 000000000000..5785b4b8931e --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_eof_version.asmjson @@ -0,0 +1,13 @@ +{ + ".code": [ + {"name": "CODESIZE"} + ] +} +// ==== +// bytecodeFormat: legacy +// ---- +// Assembly: +// codesize +// Bytecode: 38 +// Opcodes: CODESIZE +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_evm_version.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_evm_version.asmjson new file mode 100644 index 000000000000..a0f042b32e88 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_evm_version.asmjson @@ -0,0 +1,13 @@ +{ + ".code": [ + {"name": "BLOBBASEFEE"} + ] +} +// ==== +// EVMVersion: >=cancun +// ---- +// Assembly: +// blobbasefee +// Bytecode: 4a +// Opcodes: BLOBBASEFEE +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_constant_optimizer.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_constant_optimizer.asmjson new file mode 100644 index 000000000000..a3610217fbaf --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_constant_optimizer.asmjson @@ -0,0 +1,14 @@ +{ + ".code": [ + {"name": "PUSH", "value": "ffffffffffffffff"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.constantOptimizer: true +// ---- +// Assembly: +// sub(shl(0x40, 0x01), 0x01) +// Bytecode: 6001600160401b03 +// Opcodes: PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB +// SourceMappings: :::-:0;;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_cse.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_cse.asmjson new file mode 100644 index 000000000000..c8133b43f799 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_cse.asmjson @@ -0,0 +1,16 @@ +{ + ".code": [ + {"name": "PUSH", "value": "0"}, + {"name": "DUP2"}, + {"name": "SUB"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.cse: true +// ---- +// Assembly: +// dup1 +// Bytecode: 80 +// Opcodes: DUP1 +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_deduplicate.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_deduplicate.asmjson new file mode 100644 index 000000000000..67b87e59e9ee --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_deduplicate.asmjson @@ -0,0 +1,26 @@ +{ + ".code": [ + {"name": "PUSH [tag]", "value": "1"}, + {"name": "PUSH [tag]", "value": "2"}, + {"name": "tag", "value": "1"}, + {"name": "JUMPDEST"}, + {"name": "JUMP"}, + {"name": "tag", "value": "2"}, + {"name": "JUMPDEST"}, + {"name": "JUMP"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.deduplicate: true +// ---- +// Assembly: +// tag_1 +// tag_1 +// tag_1: +// jump +// tag_2: +// jump +// Bytecode: 600460045b565b56 +// Opcodes: PUSH1 0x4 PUSH1 0x4 JUMPDEST JUMP JUMPDEST JUMP +// SourceMappings: :::-:0;;;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_expected_executions_per_deployment.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_expected_executions_per_deployment.asmjson new file mode 100644 index 000000000000..b9fd814d5c8e --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_expected_executions_per_deployment.asmjson @@ -0,0 +1,26 @@ +{ + ".code": [ + {"name": "PUSH [$]", "value": "0"} + ], + ".data": { + "0": { + ".code": [ + {"name": "PUSH", "value": "ffffffffffffffff"} + ] + } + } +} +// ==== +// optimizationPreset: standard +// optimizer.expectedExecutionsPerDeployment: 0 +// ---- +// Assembly: +// dataOffset(sub_0) +// stop +// +// sub_0: assembly { +// sub(shl(0x40, 0x01), 0x01) +// } +// Bytecode: 6003fe6001600160401b03 +// Opcodes: PUSH1 0x3 INVALID PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_inliner.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_inliner.asmjson new file mode 100644 index 000000000000..81be6392c444 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_inliner.asmjson @@ -0,0 +1,33 @@ +{ + ".code": [ + {"name": "PUSH [tag]", "value": "1"}, + {"name": "PUSH [tag]", "value": "2"}, + {"name": "JUMP"}, + {"name": "tag", "value": "1"}, + {"name": "JUMPDEST"}, + {"name": "STOP"}, + {"name": "tag", "value": "2"}, + {"name": "JUMPDEST"}, + {"name": "CALLVALUE"}, + {"name": "SWAP1"}, + {"name": "JUMP"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.inliner: true +// ---- +// Assembly: +// tag_1 +// callvalue +// swap1 +// jump +// tag_1: +// stop +// tag_2: +// callvalue +// swap1 +// jump +// Bytecode: 60053490565b005b349056 +// Opcodes: PUSH1 0x5 CALLVALUE SWAP1 JUMP JUMPDEST STOP JUMPDEST CALLVALUE SWAP1 JUMP +// SourceMappings: :::-:0;;;;;;;;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_jumpdest_remover.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_jumpdest_remover.asmjson new file mode 100644 index 000000000000..baca77b992fc --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_jumpdest_remover.asmjson @@ -0,0 +1,14 @@ +{ + ".code": [ + {"name": "tag", "value": "1"}, + {"name": "JUMPDEST"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.jumpdestRemover: true +// ---- +// Assembly: +// Bytecode: +// Opcodes: +// SourceMappings: diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_peephole.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_peephole.asmjson new file mode 100644 index 000000000000..f9e7c3b8d550 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_peephole.asmjson @@ -0,0 +1,15 @@ +{ + ".code": [ + {"name": "STOP"}, + {"name": "STOP"} + ] +} +// ==== +// optimizationPreset: none +// optimizer.peephole: true +// ---- +// Assembly: +// stop +// Bytecode: 00 +// Opcodes: STOP +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_preset.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_preset.asmjson new file mode 100644 index 000000000000..706a3ae8b61a --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_optimizer_preset.asmjson @@ -0,0 +1,18 @@ +{ + ".code": [ + {"name": "PUSH", "value": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"name": "PUSH", "value": "42"}, + {"name": "PUSH", "value": "24"}, + {"name": "SWAP1"}, + {"name": "ADD"} + ] +} +// ==== +// optimizationPreset: full +// ---- +// Assembly: +// not(0x00) +// 0x66 +// Bytecode: 5f196066 +// Opcodes: PUSH0 NOT PUSH1 0x66 +// SourceMappings: :::-:0;; diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_outputs.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_outputs.asmjson new file mode 100644 index 000000000000..2af7b57c3e75 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/settings_outputs.asmjson @@ -0,0 +1,10 @@ +{ + ".code": [ + {"name": "CALLVALUE"} + ] +} +// ==== +// outputs: SourceMappings,Opcodes +// ---- +// Opcodes: CALLVALUE +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke.asmjson b/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke.asmjson new file mode 100644 index 000000000000..99e527a72587 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke.asmjson @@ -0,0 +1,11 @@ +{ + ".code": [ + {"name": "CALLVALUE"} + ] +} +// ---- +// Assembly: +// callvalue +// Bytecode: 34 +// Opcodes: CALLVALUE +// SourceMappings: :::-:0 diff --git a/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke_plain.asm b/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke_plain.asm new file mode 100644 index 000000000000..a1714241fb86 --- /dev/null +++ b/test/libevmasm/evmAssemblyTests/isoltestTesting/smoke_plain.asm @@ -0,0 +1,16 @@ +CALLVALUE +// ==== +// outputs: InputAssemblyJSON,Assembly,Bytecode,Opcodes,SourceMappings +// ---- +// InputAssemblyJSON: { +// ".code": [ +// { +// "name": "CALLVALUE" +// } +// ] +// } +// Assembly: +// callvalue +// Bytecode: 34 +// Opcodes: CALLVALUE +// SourceMappings: :::-:0 diff --git a/test/libsolidity/ABIJsonTest.cpp b/test/libsolidity/ABIJsonTest.cpp index 803445b96ea6..3cc455288cc5 100644 --- a/test/libsolidity/ABIJsonTest.cpp +++ b/test/libsolidity/ABIJsonTest.cpp @@ -26,12 +26,14 @@ #include #include -#include +#include +#include using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; ABIJsonTest::ABIJsonTest(std::string const& _filename): TestCase(_filename) @@ -48,10 +50,9 @@ TestCase::TestResult ABIJsonTest::run(std::ostream& _stream, std::string const& "", "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n" + m_source }}); - compiler.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); - if (!compiler.parseAndAnalyze()) - BOOST_THROW_EXCEPTION(std::runtime_error("Parsing contract failed")); + compiler.setEVMVersion(CommonOptions::get().evmVersion()); + compiler.setOptimiserSettings(CommonOptions::get().optimize); + solRequire(compiler.parseAndAnalyze(), ExecutionError, "Parsing contract failed"); m_obtainedResult.clear(); bool first = true; diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index daf4a9ea44e2..c38e1d66260f 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -33,16 +33,15 @@ #include #include -#include #include using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; using namespace solidity::util::formatting; using namespace solidity::util; -namespace fs = boost::filesystem; using namespace boost::unit_test; using namespace std::string_literals; @@ -68,18 +67,17 @@ std::string compilerStateToString(CompilerStack::State _state) CompilerStack::State stringToCompilerState(const std::string& _state) { for (unsigned int i = CompilerStack::State::Empty; i <= CompilerStack::State::CompilationSuccessful; ++i) - { if (_state == compilerStateToString(CompilerStack::State(i))) return CompilerStack::State(i); - } - BOOST_THROW_EXCEPTION(std::runtime_error("Unsupported compiler state (" + _state + ") in test contract file")); + + solThrow(ValidationError, "Unsupported compiler state (" + _state + ") in test contract file"); } void replaceVersionWithTag(std::string& _input) { boost::algorithm::replace_all( _input, - "\"" + solidity::test::CommonOptions::get().evmVersion().name() + "\"", + "\"" + CommonOptions::get().evmVersion().name() + "\"", "%EVMVERSION%" ); } @@ -89,7 +87,7 @@ void replaceTagWithVersion(std::string& _input) boost::algorithm::replace_all( _input, "%EVMVERSION%", - "\"" + solidity::test::CommonOptions::get().evmVersion().name() + "\"" + "\"" + CommonOptions::get().evmVersion().name() + "\"" ); } @@ -120,8 +118,7 @@ void ASTJSONTest::generateTestVariants(std::string const& _filename) void ASTJSONTest::fillSources(std::string const& _filename) { std::ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(std::runtime_error("Cannot open test contract: \"" + _filename + "\".")); + solRequire(file, ValidationError, "Cannot open test contract: \"" + _filename + "\"."); file.exceptions(std::ios::badbit); std::string sourceName; @@ -147,7 +144,7 @@ void ASTJSONTest::fillSources(std::string const& _filename) std::string state = line.substr(failMarker.size()); boost::algorithm::trim(state); if (m_expectedFailAfter.has_value()) - BOOST_THROW_EXCEPTION(std::runtime_error("Duplicated \"failAfter\" directive")); + solThrow(ValidationError, "Duplicated \"failAfter\" directive"); m_expectedFailAfter = stringToCompilerState(state); } @@ -160,8 +157,7 @@ void ASTJSONTest::fillSources(std::string const& _filename) void ASTJSONTest::validateTestConfiguration() const { - if (m_variants.empty()) - BOOST_THROW_EXCEPTION(std::runtime_error("No file with expected result found.")); + solRequire(!m_variants.empty(), ValidationError, "No file with expected result found."); if (m_expectedFailAfter.has_value()) { @@ -171,13 +167,11 @@ void ASTJSONTest::validateTestConfiguration() const ); if (unexpectedTestVariant != m_variants.end()) - BOOST_THROW_EXCEPTION( - std::runtime_error( - std::string("Unexpected JSON file: ") + unexpectedTestVariant->astFilename() + - " in \"failAfter: " + - compilerStateToString(m_expectedFailAfter.value()) + "\" scenario." - ) - ); + solThrow(ValidationError, fmt::format( + "Unexpected JSON file: {} in \"failAfter: {}\" scenario.", + unexpectedTestVariant->astFilename(), + compilerStateToString(m_expectedFailAfter.value()) + )); } } @@ -185,7 +179,7 @@ ASTJSONTest::ASTJSONTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { if (!boost::algorithm::ends_with(_filename, ".sol")) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid test contract file name: \"" + _filename + "\".")); + solThrow(ValidationError, "Invalid test contract file name: \"" + _filename + "\"."); generateTestVariants(_filename); fillSources(_filename); @@ -210,7 +204,7 @@ TestCase::TestResult ASTJSONTest::run(std::ostream& _stream, std::string const& { c.reset(); c.setSources(sources); - c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + c.setEVMVersion(CommonOptions::get().evmVersion()); if (!c.parseAndAnalyze(variant.stopAfter)) { @@ -310,7 +304,7 @@ void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) co void ASTJSONTest::updateExpectation(std::string const& _filename, std::string const& _expectation, std::string const& _variant) const { std::ofstream file(_filename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(std::runtime_error("Cannot write " + _variant + "AST expectation to \"" + _filename + "\".")); + solRequire(file, ExecutionError, "Cannot write " + _variant + "AST expectation to \"" + _filename + "\"."); file.exceptions(std::ios::badbit); std::string replacedResult = _expectation; diff --git a/test/libsolidity/ASTPropertyTest.cpp b/test/libsolidity/ASTPropertyTest.cpp index 3419d51c577c..9f198d3b6fa0 100644 --- a/test/libsolidity/ASTPropertyTest.cpp +++ b/test/libsolidity/ASTPropertyTest.cpp @@ -36,6 +36,7 @@ #include using namespace solidity::util; +using namespace solidity::test; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; @@ -46,7 +47,7 @@ ASTPropertyTest::ASTPropertyTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { if (!boost::algorithm::ends_with(_filename, ".sol")) - BOOST_THROW_EXCEPTION(std::runtime_error("Not a Solidity file: \"" + _filename + "\".")); + solThrow(ValidationError, "Not a Solidity file: \"" + _filename + "\"."); m_source = m_reader.source(); readExpectations(); @@ -191,13 +192,13 @@ TestCase::TestResult ASTPropertyTest::run(std::ostream& _stream, std::string con "A", "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n" + m_source }}); - compiler.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); + compiler.setEVMVersion(CommonOptions::get().evmVersion()); + compiler.setOptimiserSettings(CommonOptions::get().optimize); if (!compiler.parseAndAnalyze()) - BOOST_THROW_EXCEPTION(std::runtime_error( - "Parsing contract failed" + - SourceReferenceFormatter::formatErrorInformation(compiler.errors(), compiler, _formatted) - )); + solThrow( + ExecutionError, + "Parsing contract failed" + SourceReferenceFormatter::formatErrorInformation(compiler.errors(), compiler, _formatted) + ); Json astJson = ASTJsonExporter(compiler.state()).toJson(compiler.ast("A")); soltestAssert(!astJson.empty()); diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index 39b0e6681abe..5bccd71a06cf 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -27,12 +27,14 @@ #include #include #include -#include -#include + +#include +#include using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; using namespace solidity; using namespace boost::unit_test; @@ -53,7 +55,7 @@ void GasTest::parseExpectations(std::istream& _stream) while (getline(_stream, line)) if (!boost::starts_with(line, "// ")) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid expectation: expected \"// \".")); + solThrow(ValidationError, "Invalid expectation: expected \"// \"."); else if (boost::ends_with(line, ":")) { std::string kind = line.substr(3, line.length() - 4); @@ -61,7 +63,7 @@ void GasTest::parseExpectations(std::istream& _stream) currentKind = &m_expectations[std::move(kind)]; } else if (!currentKind) - BOOST_THROW_EXCEPTION(std::runtime_error("No function kind specified. Expected \"creation:\", \"external:\" or \"internal:\".")); + solThrow(ValidationError, "No function kind specified. Expected \"creation:\", \"external:\" or \"internal:\"."); else { auto it = line.begin() + 3; @@ -74,8 +76,7 @@ void GasTest::parseExpectations(std::istream& _stream) functionName.clear(); expect(it, line.end(), ':'); skipWhitespace(it, line.end()); - if (it == line.end()) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid expectation: expected gas cost.")); + solRequire(it != line.end(), ValidationError, "Invalid expectation: expected gas cost."); (*currentKind)[functionName] = std::string(it, line.end()); } } diff --git a/test/libsolidity/NatspecJSONTest.cpp b/test/libsolidity/NatspecJSONTest.cpp index 7dff638f0de5..17f7789df626 100644 --- a/test/libsolidity/NatspecJSONTest.cpp +++ b/test/libsolidity/NatspecJSONTest.cpp @@ -27,9 +27,8 @@ #include -#include - using namespace solidity::frontend::test; +using namespace solidity::test; using namespace solidity::util; using namespace std::string_literals; @@ -70,7 +69,7 @@ void NatspecJSONTest::parseCustomExpectations(std::istream& _stream) Json parsedJSON; bool jsonParsingSuccessful = jsonParseStrict(rawJSON, parsedJSON, &jsonErrors); if (!jsonParsingSuccessful) - BOOST_THROW_EXCEPTION(std::runtime_error(fmt::format( + solThrow(ValidationError, fmt::format( "Malformed JSON in {} expectation for contract {}.\n" "Note that JSON expectations must be pretty-printed to be split correctly. " "The object is assumed to and at the first unindented closing brace.\n" @@ -78,7 +77,7 @@ void NatspecJSONTest::parseCustomExpectations(std::istream& _stream) toString(kind), contractName, rawJSON - ))); + )); m_expectedNatspecJSON[std::string(contractName)][kind] = parsedJSON; } @@ -128,9 +127,7 @@ std::tuple NatspecJSONTest::parseExpectationH return {_line.substr(0, _line.size() - kindSuffix.size()), kind}; } - BOOST_THROW_EXCEPTION(std::runtime_error( - "Natspec kind (devdoc/userdoc) not present in the expectation: "s.append(_line) - )); + solThrow(ValidationError, "Natspec kind (devdoc/userdoc) not present in the expectation: "s.append(_line)); } std::string NatspecJSONTest::extractExpectationJSON(std::istream& _stream) @@ -154,9 +151,7 @@ std::string_view NatspecJSONTest::expectLinePrefix(std::string_view _line) { size_t startPosition = 0; if (!boost::algorithm::starts_with(_line, "//")) - BOOST_THROW_EXCEPTION(std::runtime_error( - "Expectation line is not a comment: "s.append(_line) - )); + solThrow(ValidationError, "Expectation line is not a comment: "s.append(_line)); startPosition += 2; if (startPosition < _line.size() && _line[startPosition] == ' ') diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index ce099091f69c..39a1225f43fb 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -42,13 +42,13 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (isValidContractName(contract)) m_modelCheckerSettings.contracts.contracts[""] = {contract}; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid contract specified in SMTContract setting.")); + solThrow(ValidationError, "Invalid contract specified in SMTContract setting."); auto extCallsMode = ModelCheckerExtCalls::fromString(m_reader.stringSetting("SMTExtCalls", "untrusted")); if (extCallsMode) m_modelCheckerSettings.externalCalls = *extCallsMode; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT external calls mode.")); + solThrow(ValidationError, "Invalid SMT external calls mode."); auto const& showProvedSafe = m_reader.stringSetting("SMTShowProvedSafe", "no"); if (showProvedSafe == "no") @@ -56,7 +56,7 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (showProvedSafe == "yes") m_modelCheckerSettings.showProvedSafe = true; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT \"show proved safe\" choice.")); + solThrow(ValidationError, "Invalid SMT \"show proved safe\" choice."); auto const& showUnproved = m_reader.stringSetting("SMTShowUnproved", "yes"); if (showUnproved == "no") @@ -64,7 +64,7 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (showUnproved == "yes") m_modelCheckerSettings.showUnproved = true; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT \"show unproved\" choice.")); + solThrow(ValidationError, "Invalid SMT \"show unproved\" choice."); auto const& showUnsupported = m_reader.stringSetting("SMTShowUnsupported", "yes"); if (showUnsupported == "no") @@ -72,14 +72,14 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (showUnsupported == "yes") m_modelCheckerSettings.showUnsupported = true; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT \"show unsupported\" choice.")); + solThrow(ValidationError, "Invalid SMT \"show unsupported\" choice."); m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::None(); auto const& choice = m_reader.stringSetting("SMTSolvers", "z3"); if (choice == "none") m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::None(); else if (!m_modelCheckerSettings.solvers.setSolver(choice)) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT solver choice.")); + solThrow(ValidationError, "Invalid SMT solver choice."); m_modelCheckerSettings.solvers &= ModelChecker::availableSolvers(); @@ -90,13 +90,13 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): if (targets) m_modelCheckerSettings.targets = *targets; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT targets.")); + solThrow(ValidationError, "Invalid SMT targets."); auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); if (engine) m_modelCheckerSettings.engine = *engine; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT engine choice.")); + solThrow(ValidationError, "Invalid SMT engine choice."); if (m_modelCheckerSettings.solvers.none() || m_modelCheckerSettings.engine.none()) m_shouldRun = false; @@ -107,7 +107,7 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (ignoreCex == "yes") m_ignoreCex = true; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT counterexample choice.")); + solThrow(ValidationError, "Invalid SMT counterexample choice."); static auto removeInv = [](std::vector&& errors) { std::vector filtered; @@ -123,7 +123,7 @@ SMTCheckerTest::SMTCheckerTest(std::string const& _filename): else if (ignoreInv == "yes") m_modelCheckerSettings.invariants = ModelCheckerInvariants::None(); else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid SMT invariant choice.")); + solThrow(ValidationError, "Invalid SMT invariant choice."); if (m_modelCheckerSettings.invariants.invariants.empty()) m_expectations = removeInv(std::move(m_expectations)); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 1b66dd60fffb..3150798b85e4 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -24,12 +24,12 @@ #include #include -#include #include -#include +#include #include -#include #include +#include +#include #include #include #include @@ -37,13 +37,13 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::langutil; +using namespace solidity::test; using namespace solidity::util; using namespace solidity::util::formatting; using namespace solidity::frontend::test; using namespace boost::algorithm; using namespace boost::unit_test; using namespace std::string_literals; -namespace fs = boost::filesystem; std::ostream& solidity::frontend::test::operator<<(std::ostream& _output, RequiresYulOptimizer _requiresYulOptimizer) { @@ -88,22 +88,23 @@ SemanticTest::SemanticTest( ); m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); - if (m_runWithABIEncoderV1Only && !solidity::test::CommonOptions::get().useABIEncoderV1) + if (m_runWithABIEncoderV1Only && !CommonOptions::get().useABIEncoderV1) m_shouldRun = false; - auto const eofEnabled = solidity::test::CommonOptions::get().eofVersion().has_value(); + auto const eofEnabled = CommonOptions::get().eofVersion().has_value(); std::string compileViaYul = m_reader.stringSetting("compileViaYul", eofEnabled ? "true" : "also"); if (compileViaYul == "false" && eofEnabled) m_shouldRun = false; if (m_runWithABIEncoderV1Only && compileViaYul != "false") - BOOST_THROW_EXCEPTION(std::runtime_error( + solThrow( + ValidationError, "ABIEncoderV1Only tests cannot be run via yul, " - "so they need to also specify ``compileViaYul: false``" - )); + "so they need to also specify `compileViaYul: false`" + ); if (!util::contains(compileViaYulAllowedValues, compileViaYul)) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid compileViaYul value: " + compileViaYul + ".")); + solThrow(ValidationError, "Invalid compileViaYul value: " + compileViaYul + "."); m_testCaseWantsYulRun = util::contains(yulRunTriggers, compileViaYul); m_testCaseWantsLegacyRun = util::contains(legacyRunTriggers, compileViaYul); @@ -326,14 +327,14 @@ TestCase::TestResult SemanticTest::run(std::ostream& _stream, std::string const& if (m_testCaseWantsYulRun && result == TestResult::Success) { - if (solidity::test::CommonOptions::get().optimize) + if (CommonOptions::get().optimize) result = runTest(_stream, _linePrefix, _formatted, true /* _isYulRun */); else result = tryRunTestWithYulOptimizer(_stream, _linePrefix, _formatted); } if (result != TestResult::Success) - solidity::test::CommonOptions::get().printSelectedOptions( + CommonOptions::get().printSelectedOptions( _stream, _linePrefix, {"evmVersion", "optimize", "useABIEncoderV1", "batch"} @@ -364,7 +365,7 @@ TestCase::TestResult SemanticTest::runTest( for (TestFunctionCall& test: m_tests) test.reset(); - std::map libraries; + std::map libraries; bool constructed = false; @@ -703,7 +704,7 @@ bool SemanticTest::deploy( std::string const& _contractName, u256 const& _value, bytes const& _arguments, - std::map const& _libraries + std::map const& _libraries ) { auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries, m_sources.mainSourceFile); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 8944710542a5..2ed2be01a880 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -25,9 +25,8 @@ #include #include #include -#include + #include -#include using namespace solidity; using namespace solidity::util; @@ -35,8 +34,8 @@ using namespace solidity::util::formatting; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; using namespace boost::unit_test; -namespace fs = boost::filesystem; SyntaxTest::SyntaxTest( std::string const& _filename, @@ -48,11 +47,11 @@ SyntaxTest::SyntaxTest( { static std::set const compileViaYulAllowedValues{"true", "false"}; - auto const eofEnabled = solidity::test::CommonOptions::get().eofVersion().has_value(); + auto const eofEnabled = CommonOptions::get().eofVersion().has_value(); m_compileViaYul = m_reader.stringSetting("compileViaYul", eofEnabled ? "true" : "false"); if (!util::contains(compileViaYulAllowedValues, m_compileViaYul)) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid compileViaYul value: " + m_compileViaYul + ".")); + solThrow(ValidationError, "Invalid compileViaYul value: " + m_compileViaYul + "."); if (m_compileViaYul == "false" && eofEnabled) m_shouldRun = false; @@ -66,7 +65,7 @@ SyntaxTest::SyntaxTest( }; std::string stopAfter = m_reader.stringSetting("stopAfter", "compilation"); if (!pipelineStages.count(stopAfter)) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid stopAfter value: " + stopAfter + ".")); + solThrow(ValidationError, "Invalid stopAfter value: " + stopAfter + "."); m_stopAfter = pipelineStages.at(stopAfter); } @@ -105,10 +104,11 @@ void SyntaxTest::parseAndAnalyze() auto error = ranges::find_if(errors, isInternalError); error != ranges::end(errors) ) - BOOST_THROW_EXCEPTION(std::runtime_error( + solThrow( + ExecutionError, "Unexpected " + Error::formatErrorType((*error)->type()) + " at compilation stage." " This error should NOT be encoded as expectation and should be fixed instead." - )); + ); } filterObtainedErrors(); @@ -133,8 +133,8 @@ void SyntaxTest::filterObtainedErrors() if(m_sources.sources.count(sourceName) == 1) { int preambleSize = - static_cast(compiler().charStream(sourceName).size()) - - static_cast(m_sources.sources[sourceName].size()); + static_cast(compiler().charStream(sourceName).size()) - + static_cast(m_sources.sources[sourceName].size()); solAssert(preambleSize >= 0, ""); // ignore the version & license pragma inserted by the testing tool when calculating locations. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index a9f4c59a74ac..6edebc3aa620 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -41,8 +41,6 @@ #include -#include - using namespace solidity; using namespace solidity::frontend; using namespace solidity::yul; @@ -119,13 +117,11 @@ std::map _eofVersion) { if (!validDialects.count(_name)) - BOOST_THROW_EXCEPTION(std::runtime_error{ - "Invalid Dialect \"" + - _name + - "\". Valid dialects are " + - util::joinHumanReadable(validDialectNames(), ", ", " and ") + - "." - }); + solThrow(ValidationError, fmt::format( + "Invalid Dialect \"{}\". Valid dialects: {}.", + _name, + util::joinHumanReadable(validDialectNames(), ", ", " and ") + )); return validDialects.at(_name)(_evmVersion, _eofVersion); } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index b7e475c91b1b..c5714192ed6e 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -42,9 +42,10 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; ObjectCompilerTest::ObjectCompilerTest(std::string const& _filename): - solidity::frontend::test::EVMVersionRestrictedTestCase(_filename) + EVMVersionRestrictedTestCase(_filename) { m_source = m_reader.source(); m_optimisationPreset = m_reader.enumSetting( @@ -62,7 +63,7 @@ ObjectCompilerTest::ObjectCompilerTest(std::string const& _filename): boost::split(m_outputSetting, m_reader.stringSetting("outputs", "Assembly,Bytecode,Opcodes,SourceMappings"), boost::is_any_of(",")); for (auto const& output: m_outputSetting) if (std::find(allowedOutputs.begin(), allowedOutputs.end(), output) == allowedOutputs.end()) - BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid output type: \"" + output + "\""}); + solThrow(ValidationError, "Invalid output type: \"" + output + "\""); m_expectation = m_reader.simpleExpectations(); } @@ -97,7 +98,7 @@ TestCase::TestResult ObjectCompilerTest::run(std::ostream& _stream, std::string { m_obtainedResult += (!m_obtainedResult.empty() && m_obtainedResult.back() != '\n') ? "\n" : ""; m_obtainedResult += "Opcodes: " + - boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode, solidity::test::CommonOptions::get().evmVersion())); + boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode, CommonOptions::get().evmVersion())); } if (std::find(m_outputSetting.begin(), m_outputSetting.end(), "SourceMappings") != m_outputSetting.end()) { diff --git a/test/libyul/StackShufflingTest.cpp b/test/libyul/StackShufflingTest.cpp index 58ea124fb1df..af76d425ca13 100644 --- a/test/libyul/StackShufflingTest.cpp +++ b/test/libyul/StackShufflingTest.cpp @@ -146,8 +146,7 @@ void StackShufflingTest::processSettings() { std::string depthString = m_reader.stringSetting("maximumStackDepth", "16"); std::optional depth = toUnsignedInt(depthString); - if (!depth.has_value()) - BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid maximum stack depth: \"" + depthString + "\""}); + solRequire(depth.has_value(), ValidationError, "Invalid maximum stack depth: \"" + depthString + "\""); m_maximumStackDepth = *depth; } @@ -182,7 +181,7 @@ TestCase::TestResult StackShufflingTest::run(std::ostream& _stream, std::string if (auto depth = util::findOffset(m_sourceStack | ranges::views::reverse, _slot)) output << "DUP" << *depth + 1 << std::endl; else - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid DUP operation.")); + solThrow(ExecutionError, "Invalid DUP operation."); } }, [&](){ // pop diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 1048fb468854..612d0dfdc5c5 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -35,7 +35,7 @@ #include #include -#include +#include using namespace solidity; using namespace solidity::util; @@ -44,6 +44,7 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; YulOptimizerTest::YulOptimizerTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) @@ -51,7 +52,7 @@ YulOptimizerTest::YulOptimizerTest(std::string const& _filename): boost::filesystem::path path(_filename); if (path.empty() || std::next(path.begin()) == path.end() || std::next(std::next(path.begin())) == path.end()) - BOOST_THROW_EXCEPTION(std::runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); + solThrow(ValidationError, "Filename path has to contain a directory: \"" + _filename + "\"."); m_optimizerStep = std::prev(std::prev(path.end()))->string(); m_source = m_reader.source(); diff --git a/test/soltest.cpp b/test/soltest.cpp index 9bc60d1181f8..ff348c0b3515 100644 --- a/test/soltest.cpp +++ b/test/soltest.cpp @@ -49,6 +49,7 @@ using namespace boost::unit_test; using namespace solidity::frontend::test; +using namespace solidity::test; namespace fs = boost::filesystem; namespace @@ -68,7 +69,7 @@ void removeTestSuite(std::string const& _name) class BoostBatcher: public test_tree_visitor { public: - BoostBatcher(solidity::test::Batcher& _batcher): + BoostBatcher(Batcher& _batcher): m_batcher(_batcher) {} @@ -91,7 +92,7 @@ class BoostBatcher: public test_tree_visitor } private: - solidity::test::Batcher& m_batcher; + Batcher& m_batcher; std::vector m_path; }; @@ -127,18 +128,18 @@ int registerTests( boost::filesystem::path const& _path, std::vector const& _labels, TestCase::TestCaseCreator _testCaseCreator, - solidity::test::Batcher& _batcher + Batcher& _batcher ) { int numTestsAdded = 0; fs::path fullpath = _basepath / _path; TestCase::Config config{ fullpath.string(), - solidity::test::CommonOptions::get().evmVersion(), - solidity::test::CommonOptions::get().eofVersion(), - solidity::test::CommonOptions::get().vmPaths, - solidity::test::CommonOptions::get().enforceGasTest, - solidity::test::CommonOptions::get().enforceGasTestMinValue, + CommonOptions::get().evmVersion(), + CommonOptions::get().eofVersion(), + CommonOptions::get().vmPaths, + CommonOptions::get().enforceGasTest, + CommonOptions::get().enforceGasTestMinValue, }; if (fs::is_directory(fullpath)) { @@ -148,7 +149,7 @@ int registerTests( fs::directory_iterator() )) if ( - solidity::test::isValidSemanticTestPath(entry) && + isValidSemanticTestPath(entry) && (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) ) numTestsAdded += registerTests( @@ -195,13 +196,13 @@ bool initializeOptions() { auto const& suite = boost::unit_test::framework::master_test_suite(); - auto options = std::make_unique(); + auto options = std::make_unique(); bool shouldContinue = options->parse(suite.argc, suite.argv); if (!shouldContinue) return false; options->validate(); - solidity::test::CommonOptions::setSingleton(std::move(options)); + CommonOptions::setSingleton(std::move(options)); return true; } @@ -224,10 +225,10 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) if (!shouldContinue) exit(EXIT_SUCCESS); - if (!solidity::test::loadVMs(solidity::test::CommonOptions::get())) + if (!loadVMs(CommonOptions::get())) exit(EXIT_FAILURE); - if (solidity::test::CommonOptions::get().disableSemanticTests) + if (CommonOptions::get().disableSemanticTests) std::cout << std::endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << std::endl << std::endl; Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches); @@ -241,12 +242,12 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) // Include the interactive tests in the automatic tests as well for (auto const& ts: g_interactiveTestsuites) { - auto const& options = solidity::test::CommonOptions::get(); + auto const& options = CommonOptions::get(); if (ts.smt && options.disableSMT) continue; - if (ts.needsVM && solidity::test::CommonOptions::get().disableSemanticTests) + if (ts.needsVM && CommonOptions::get().disableSemanticTests) continue; //TODO @@ -262,7 +263,7 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) // > 0, std::string("no ") + ts.title + " tests found"); } - if (solidity::test::CommonOptions::get().disableSemanticTests) + if (CommonOptions::get().disableSemanticTests) { for (auto suite: { "ABIDecoderTest", @@ -277,12 +278,7 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) removeTestSuite(suite); } } - catch (solidity::test::ConfigException const& exception) - { - std::cerr << exception.what() << std::endl; - exit(EXIT_FAILURE); - } - catch (std::runtime_error const& exception) + catch (SoltestError const& exception) { std::cerr << exception.what() << std::endl; exit(EXIT_FAILURE); diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 5c1d41b7f595..289f2f2def35 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -18,6 +18,8 @@ add_executable(isoltest ../EVMHost.cpp ../TestCase.cpp ../TestCaseReader.cpp + ../libevmasm/EVMAssemblyTest.cpp + ../libevmasm/PlainAssemblyParser.cpp ../libsolidity/util/BytesUtils.cpp ../libsolidity/util/Common.cpp ../libsolidity/util/ContractABIUtils.cpp diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 57ab4af16034..833e45150aac 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -25,9 +25,14 @@ #include #include +#include #include #include +#include + +#include + #include #include #include @@ -43,13 +48,10 @@ using namespace solidity::util; using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace solidity::util::formatting; +using namespace solidity::test; -namespace po = boost::program_options; namespace fs = boost::filesystem; -using TestCreator = TestCase::TestCaseCreator; -using TestOptions = solidity::test::IsolTestOptions; - struct TestStats { int successCount = 0; @@ -68,19 +70,22 @@ struct TestStats class TestFilter { public: - explicit TestFilter(std::string _filter): m_filter(std::move(_filter)) + explicit TestFilter(std::string _filter): + m_filter(std::move(_filter)) { - std::string filter{m_filter}; - - boost::replace_all(filter, "/", "\\/"); - boost::replace_all(filter, "*", ".*"); - - m_filterExpression = std::regex{"(" + filter + "(\\.sol|\\.yul|\\.stack))"}; + auto const startsWithDot = [](std::string const& _extension) { return boost::starts_with(_extension, "."); }; + soltestAssert(ranges::all_of(testFileExtensions(), startsWithDot)); + + m_filterExpression = std::regex{fmt::format( + "({}({}))", + boost::replace_all_copy(boost::replace_all_copy(m_filter, "/", "\\/"), "*", ".*"), + boost::replace_all_copy(joinHumanReadable(testFileExtensions(), "|"), ".", "\\.") + )}; } bool matches(fs::path const& _path, std::string const& _name) const { - return std::regex_match(_name, m_filterExpression) && solidity::test::isValidSemanticTestPath(_path); + return std::regex_match(_name, m_filterExpression) && isValidSemanticTestPath(_path); } private: @@ -92,8 +97,8 @@ class TestTool { public: TestTool( - TestCreator _testCaseCreator, - TestOptions const& _options, + TestCase::TestCaseCreator _testCaseCreator, + IsolTestOptions const& _options, fs::path _path, std::string _name ): @@ -115,11 +120,11 @@ class TestTool Result process(); static TestStats processPath( - TestCreator _testCaseCreator, - TestOptions const& _options, + TestCase::TestCaseCreator _testCaseCreator, + IsolTestOptions const& _options, fs::path const& _basepath, fs::path const& _path, - solidity::test::Batcher& _batcher + Batcher& _batcher ); private: enum class Request @@ -132,8 +137,8 @@ class TestTool void updateTestCase(); Request handleResponse(bool _exception); - TestCreator m_testCaseCreator; - TestOptions const& m_options; + TestCase::TestCaseCreator m_testCaseCreator; + IsolTestOptions const& m_options; TestFilter m_filter; fs::path const m_path; std::string const m_name; @@ -191,9 +196,14 @@ TestTool::Result TestTool::process() else return Result::Skipped; } + catch (SoltestError const& exception) + { + AnsiColorized(std::cerr, formatted, {BOLD, RED}) << exception.what() << std::endl; + return Result::Exception; + } catch (...) { - AnsiColorized(std::cout, formatted, {BOLD, RED}) << + AnsiColorized(std::cerr, formatted, {BOLD, RED}) << "Unhandled exception during test: " << boost::current_exception_diagnostic_information() << std::endl; return Result::Exception; } @@ -253,11 +263,11 @@ TestTool::Request TestTool::handleResponse(bool _exception) } TestStats TestTool::processPath( - TestCreator _testCaseCreator, - TestOptions const& _options, + TestCase::TestCaseCreator _testCaseCreator, + IsolTestOptions const& _options, fs::path const& _basepath, fs::path const& _path, - solidity::test::Batcher& _batcher + Batcher& _batcher ) { std::queue paths; @@ -362,12 +372,12 @@ void setupTerminal() } std::optional runTestSuite( - TestCreator _testCaseCreator, - TestOptions const& _options, + TestCase::TestCaseCreator _testCaseCreator, + IsolTestOptions const& _options, fs::path const& _basePath, fs::path const& _subdirectory, std::string const& _name, - solidity::test::Batcher& _batcher + Batcher& _batcher ) { fs::path testPath{_basePath / _subdirectory}; @@ -429,7 +439,7 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(CommonOptions::get()); - if (!solidity::test::loadVMs(options)) + if (!loadVMs(options)) return EXIT_FAILURE; if (options.disableSemanticTests) @@ -488,12 +498,7 @@ int main(int argc, char const *argv[]) std::cerr << exception.what() << std::endl; return 2; } - catch (std::runtime_error const& exception) - { - std::cerr << exception.what() << std::endl; - return 2; - } - catch (solidity::test::ConfigException const& exception) + catch (SoltestError const& exception) { std::cerr << exception.what() << std::endl; return 2;