diff options
author | chriseth <chris@ethereum.org> | 2018-11-14 02:33:35 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-14 02:33:35 +0800 |
commit | 1d4f565a64988a3400847d2655ca24f73f234bc6 (patch) | |
tree | caaa6c26e307513505349b50ca4f2a8a9506752b /test/tools | |
parent | 59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (diff) | |
parent | 91b6b8a88e76016e0324036cb7a7f9300a1e2439 (diff) | |
download | dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.gz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.bz2 dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.lz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.xz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.zst dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.zip |
Merge pull request #5416 from ethereum/develop
Merge develop into release for 0.5.0
Diffstat (limited to 'test/tools')
-rw-r--r-- | test/tools/CMakeLists.txt | 7 | ||||
-rw-r--r-- | test/tools/fuzzer.cpp | 103 | ||||
-rw-r--r-- | test/tools/isoltest.cpp | 305 | ||||
-rw-r--r-- | test/tools/yulopti.cpp | 233 |
4 files changed, 457 insertions, 191 deletions
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 11714017..19a1d958 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,5 +1,10 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) -add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp) +add_executable(yulopti yulopti.cpp) +target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) + +add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../libsolidity/TestCase.cpp ../libsolidity/SyntaxTest.cpp + ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp + ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libyul/YulOptimizerTest.cpp) target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/fuzzer.cpp b/test/tools/fuzzer.cpp index 71f38b67..8633454c 100644 --- a/test/tools/fuzzer.cpp +++ b/test/tools/fuzzer.cpp @@ -28,6 +28,7 @@ #include <boost/program_options.hpp> #include <string> +#include <sstream> #include <iostream> using namespace std; @@ -48,15 +49,17 @@ string contains(string const& _haystack, vector<string> const& _needles) return ""; } -void testConstantOptimizer() +void testConstantOptimizer(string const& input) { if (!quiet) cout << "Testing constant optimizer" << endl; vector<u256> numbers; - while (!cin.eof()) + stringstream sin(input); + + while (!sin.eof()) { h256 data; - cin.read(reinterpret_cast<char*>(data.data()), 32); + sin.read(reinterpret_cast<char*>(data.data()), 32); numbers.push_back(u256(data)); } if (!quiet) @@ -84,13 +87,9 @@ void testConstantOptimizer() } } -void testStandardCompiler() +void runCompiler(string input) { - if (!quiet) - cout << "Testing compiler via JSON interface." << endl; - string input = readStandardInput(); - - string outputString(compileStandard(input.c_str(), NULL)); + string outputString(solidity_compile(input.c_str(), nullptr)); Json::Value output; if (!jsonParseStrict(outputString, output)) { @@ -112,48 +111,35 @@ void testStandardCompiler() } } -void testCompiler(bool optimize) +void testStandardCompiler(string const& input) +{ + if (!quiet) + cout << "Testing compiler via JSON interface." << endl; + + runCompiler(input); +} + +void testCompiler(string const& input, bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readStandardInput(); - string outputString(compileJSON(input.c_str(), optimize)); - Json::Value outputJson; - if (!jsonParseStrict(outputString, outputJson)) - { - cout << "Compiler produced invalid JSON output." << endl; - abort(); - } - if (outputJson.isMember("errors")) - { - if (!outputJson["errors"].isArray()) - { - cout << "Output JSON has \"errors\" but it is not an array." << endl; - abort(); - } - for (Json::Value const& error: outputJson["errors"]) - { - string invalid = contains(error.asString(), vector<string>{ - "Internal compiler error", - "Exception during compilation", - "Unknown exception during compilation", - "Unknown exception while generating contract data output", - "Unknown exception while generating source name output", - "Unknown error while generating JSON" - }); - if (!invalid.empty()) - { - cout << "Invalid error: \"" << error.asString() << "\"" << endl; - abort(); - } - } - } - else if (!outputJson.isMember("contracts")) - { - cout << "Output JSON has neither \"errors\" nor \"contracts\"." << endl; - abort(); - } + Json::Value config = Json::objectValue; + config["language"] = "Solidity"; + config["sources"] = Json::objectValue; + config["sources"][""] = Json::objectValue; + config["sources"][""]["content"] = input; + config["settings"] = Json::objectValue; + config["settings"]["optimizer"] = Json::objectValue; + config["settings"]["optimizer"]["enabled"] = optimize; + config["settings"]["optimizer"]["runs"] = 200; + + // Enable all SourceUnit-level outputs. + config["settings"]["outputSelection"]["*"][""][0] = "*"; + // Enable all Contract-level outputs. + config["settings"]["outputSelection"]["*"]["*"][0] = "*"; + + runCompiler(jsonCompactPrint(config)); } } @@ -183,15 +169,24 @@ Allowed options)", "Expects a binary string of up to 32 bytes on stdin." ) ( + "input-file", + po::value<string>(), + "input file" + ) + ( "without-optimizer", "Run without optimizations. Cannot be used together with standard-json." ); + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add("input-file", 1); + po::variables_map arguments; try { po::command_line_parser cmdLineParser(argc, argv); - cmdLineParser.options(options); + cmdLineParser.options(options).positional(filesPositions); po::store(cmdLineParser.run(), arguments); } catch (po::error const& _exception) @@ -200,17 +195,23 @@ Allowed options)", return 1; } + string input; + if (arguments.count("input-file")) + input = readFileAsString(arguments["input-file"].as<string>()); + else + input = readStandardInput(); + if (arguments.count("quiet")) quiet = true; if (arguments.count("help")) cout << options; else if (arguments.count("const-opt")) - testConstantOptimizer(); + testConstantOptimizer(input); else if (arguments.count("standard-json")) - testStandardCompiler(); + testStandardCompiler(input); else - testCompiler(!arguments.count("without-optimizer")); + testCompiler(input, !arguments.count("without-optimizer")); return 0; } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 7a147bd0..1b6fd54a 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -16,8 +16,12 @@ */ #include <libdevcore/CommonIO.h> + +#include <test/Common.h> #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/SyntaxTest.h> +#include <test/libsolidity/ASTJSONTest.h> +#include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> @@ -29,6 +33,10 @@ #include <fstream> #include <queue> +#if defined(_WIN32) +#include <windows.h> +#endif + using namespace dev; using namespace dev::solidity; using namespace dev::solidity::test; @@ -37,18 +45,28 @@ using namespace std; namespace po = boost::program_options; namespace fs = boost::filesystem; -struct SyntaxTestStats +struct TestStats { int successCount; - int runCount; - operator bool() const { return successCount == runCount; } + int testCount; + operator bool() const { return successCount == testCount; } + TestStats& operator+=(TestStats const& _other) noexcept + { + successCount += _other.successCount; + testCount += _other.testCount; + return *this; + } }; -class SyntaxTestTool +class TestTool { public: - SyntaxTestTool(string const& _name, fs::path const& _path, bool _formatted): - m_formatted(_formatted), m_name(_name), m_path(_path) + TestTool( + TestCase::TestCaseCreator _testCaseCreator, + string const& _name, + fs::path const& _path, + bool _formatted + ): m_testCaseCreator(_testCaseCreator), m_formatted(_formatted), m_name(_name), m_path(_path) {} enum class Result @@ -60,7 +78,8 @@ public: Result process(); - static SyntaxTestStats processPath( + static TestStats processPath( + TestCase::TestCaseCreator _testCaseCreator, fs::path const& _basepath, fs::path const& _path, bool const _formatted @@ -77,68 +96,18 @@ private: Request handleResponse(bool const _exception); - void printContract() const; - - bool const m_formatted; + TestCase::TestCaseCreator m_testCaseCreator; + bool const m_formatted = false; string const m_name; fs::path const m_path; - unique_ptr<SyntaxTest> m_test; + unique_ptr<TestCase> m_test; + static bool m_exitRequested; }; -string SyntaxTestTool::editor; +string TestTool::editor; +bool TestTool::m_exitRequested = false; -void SyntaxTestTool::printContract() const -{ - if (m_formatted) - { - string const& source = m_test->source(); - if (source.empty()) - return; - - std::vector<char const*> sourceFormatting(source.length(), formatting::RESET); - for (auto const& error: m_test->errorList()) - if (error.locationStart >= 0 && error.locationEnd >= 0) - { - assert(static_cast<size_t>(error.locationStart) < source.length()); - assert(static_cast<size_t>(error.locationEnd) < source.length()); - bool isWarning = error.type == "Warning"; - for (int i = error.locationStart; i < error.locationEnd; i++) - if (isWarning) - { - if (sourceFormatting[i] == formatting::RESET) - sourceFormatting[i] = formatting::ORANGE_BACKGROUND; - } - else - sourceFormatting[i] = formatting::RED_BACKGROUND; - } - - cout << " " << sourceFormatting.front() << source.front(); - for (size_t i = 1; i < source.length(); i++) - { - if (sourceFormatting[i] != sourceFormatting[i - 1]) - cout << sourceFormatting[i]; - if (source[i] != '\n') - cout << source[i]; - else - { - cout << formatting::RESET << endl; - if (i + 1 < source.length()) - cout << " " << sourceFormatting[i]; - } - } - cout << formatting::RESET << endl; - } - else - { - stringstream stream(m_test->source()); - string line; - while (getline(stream, line)) - cout << " " << line << endl; - cout << endl; - } -} - -SyntaxTestTool::Result SyntaxTestTool::process() +TestTool::Result TestTool::process() { bool success; std::stringstream outputMessages; @@ -147,42 +116,25 @@ SyntaxTestTool::Result SyntaxTestTool::process() try { - m_test = unique_ptr<SyntaxTest>(new SyntaxTest(m_path.string())); + m_test = m_testCaseCreator(m_path.string()); success = m_test->run(outputMessages, " ", m_formatted); } - catch(CompilerError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "Exception: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(InternalCompilerError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "InternalCompilerError: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(FatalError const& _e) + catch(boost::exception const& _e) { FormattedScope(cout, m_formatted, {BOLD, RED}) << - "FatalError: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(UnimplementedFeatureError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl; + "Exception during syntax test: " << boost::diagnostic_information(_e) << endl; return Result::Exception; } catch (std::exception const& _e) { - FormattedScope(cout, m_formatted, {BOLD, RED}) << "Exception: " << _e.what() << endl; + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "Exception during syntax test: " << _e.what() << endl; return Result::Exception; } - catch(...) + catch (...) { FormattedScope(cout, m_formatted, {BOLD, RED}) << - "Unknown Exception" << endl; + "Unknown exception during syntax test." << endl; return Result::Exception; } @@ -196,14 +148,14 @@ SyntaxTestTool::Result SyntaxTestTool::process() FormattedScope(cout, m_formatted, {BOLD, RED}) << "FAIL" << endl; FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; - printContract(); + m_test->printSource(cout, " ", m_formatted); - cout << outputMessages.str() << endl; + cout << endl << outputMessages.str() << endl; return Result::Failure; } } -SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) +TestTool::Request TestTool::handleResponse(bool const _exception) { if (_exception) cout << "(e)dit/(s)kip/(q)uit? "; @@ -225,15 +177,14 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) { cout << endl; ofstream file(m_path.string(), ios::trunc); - file << m_test->source(); + m_test->printSource(file); file << "// ----" << endl; - if (!m_test->errorList().empty()) - m_test->printErrorList(file, m_test->errorList(), "// ", false); + m_test->printUpdatedExpectations(file, "// "); return Request::Rerun; } case 'e': cout << endl << endl; - if (system((editor + " \"" + m_path.string() + "\"").c_str())) + if (system((TestTool::editor + " \"" + m_path.string() + "\"").c_str())) cerr << "Error running editor command." << endl << endl; return Request::Rerun; case 'q': @@ -245,8 +196,8 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) } } - -SyntaxTestStats SyntaxTestTool::processPath( +TestStats TestTool::processPath( + TestCase::TestCaseCreator _testCaseCreator, fs::path const& _basepath, fs::path const& _path, bool const _formatted @@ -255,7 +206,7 @@ SyntaxTestStats SyntaxTestTool::processPath( std::queue<fs::path> paths; paths.push(_path); int successCount = 0; - int runCount = 0; + int testCount = 0; while (!paths.empty()) { @@ -269,13 +220,18 @@ SyntaxTestStats SyntaxTestTool::processPath( fs::directory_iterator(fullpath), fs::directory_iterator() )) - if (fs::is_directory(entry.path()) || SyntaxTest::isTestFilename(entry.path().filename())) + if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) paths.push(currentPath / entry.path().filename()); } + else if (m_exitRequested) + { + ++testCount; + paths.pop(); + } else { - SyntaxTestTool testTool(currentPath.string(), fullpath, _formatted); - ++runCount; + ++testCount; + TestTool testTool(_testCaseCreator, currentPath.string(), fullpath, _formatted); auto result = testTool.process(); switch(result) @@ -285,10 +241,12 @@ SyntaxTestStats SyntaxTestTool::processPath( switch(testTool.handleResponse(result == Result::Exception)) { case Request::Quit: - return { successCount, runCount }; + paths.pop(); + m_exitRequested = true; + break; case Request::Rerun: cout << "Re-running test case..." << endl; - --runCount; + --testCount; break; case Request::Skip: paths.pop(); @@ -303,18 +261,74 @@ SyntaxTestStats SyntaxTestTool::processPath( } } - return { successCount, runCount }; + return { successCount, testCount }; + +} + +namespace +{ + +void setupTerminal() +{ +#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) + // Set output mode to handle virtual terminal (ANSI escape sequences) + // ignore any error, as this is just a "nice-to-have" + // only windows needs to be taken care of, as other platforms (Linux/OSX) support them natively. + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + return; + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + return; +#endif +} + +boost::optional<TestStats> runTestSuite( + string const& _name, + fs::path const& _basePath, + fs::path const& _subdirectory, + TestCase::TestCaseCreator _testCaseCreator, + bool _formatted +) +{ + fs::path testPath = _basePath / _subdirectory; + + if (!fs::exists(testPath) || !fs::is_directory(testPath)) + { + cerr << _name << " tests not found. Use the --testpath argument." << endl; + return {}; + } + + TestStats stats = TestTool::processPath(_testCaseCreator, _basePath, _subdirectory, _formatted); + + cout << endl << _name << " Test Summary: "; + FormattedScope(cout, _formatted, {BOLD, stats ? GREEN : RED}) << + stats.successCount << + "/" << + stats.testCount; + cout << " tests successful." << endl << endl; + + return stats; +} } int main(int argc, char *argv[]) { + setupTerminal(); + if (getenv("EDITOR")) - SyntaxTestTool::editor = getenv("EDITOR"); + TestTool::editor = getenv("EDITOR"); else if (fs::exists("/usr/bin/editor")) - SyntaxTestTool::editor = "/usr/bin/editor"; + TestTool::editor = "/usr/bin/editor"; fs::path testPath; + bool disableSMT = false; bool formatted = true; po::options_description options( R"(isoltest, tool for interactively managing test contracts. @@ -327,8 +341,9 @@ Allowed options)", options.add_options() ("help", "Show this help screen.") ("testpath", po::value<fs::path>(&testPath), "path to test files") + ("no-smt", "disable SMT checker") ("no-color", "don't use colors") - ("editor", po::value<string>(&SyntaxTestTool::editor), "editor for opening contracts"); + ("editor", po::value<string>(&TestTool::editor), "editor for opening contracts"); po::variables_map arguments; try @@ -347,50 +362,62 @@ Allowed options)", formatted = false; po::notify(arguments); + + if (arguments.count("no-smt")) + disableSMT = true; } - catch (po::error const& _exception) + catch (std::exception const& _exception) { cerr << _exception.what() << endl; return 1; } if (testPath.empty()) - { - auto const searchPath = - { - fs::current_path() / ".." / ".." / ".." / "test", - fs::current_path() / ".." / ".." / "test", - fs::current_path() / ".." / "test", - fs::current_path() / "test", - fs::current_path() - }; - for (auto const& basePath : searchPath) - { - fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - testPath = basePath; - break; - } - } - } + testPath = dev::test::discoverTestPath(); - fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests"; + TestStats global_stats{0, 0}; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - auto stats = SyntaxTestTool::processPath(testPath / "libsolidity", "syntaxTests", formatted); + // Actually run the tests. + // If you add new tests here, you also have to add them in boostTest.cpp + if (auto stats = runTestSuite("Syntax", testPath / "libsolidity", "syntaxTests", SyntaxTest::create, formatted)) + global_stats += *stats; + else + return 1; - cout << endl << "Summary: "; - FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << - stats.successCount << "/" << stats.runCount; - cout << " tests successful." << endl; + if (auto stats = runTestSuite("JSON AST", testPath / "libsolidity", "ASTJSON", ASTJSONTest::create, formatted)) + global_stats += *stats; + else + return 1; - return stats ? 0 : 1; - } + if (auto stats = runTestSuite( + "Yul Optimizer", + testPath / "libyul", + "yulOptimizerTests", + yul::test::YulOptimizerTest::create, + formatted + )) + global_stats += *stats; else - { - cerr << "Test path not found. Use the --testpath argument." << endl; return 1; + + if (!disableSMT) + { + if (auto stats = runTestSuite( + "SMT Checker", + testPath / "libsolidity", + "smtCheckerTests", + SyntaxTest::create, + formatted + )) + global_stats += *stats; + else + return 1; } + + cout << endl << "Summary: "; + FormattedScope(cout, formatted, {BOLD, global_stats ? GREEN : RED}) << + global_stats.successCount << "/" << global_stats.testCount; + cout << " tests successful." << endl; + + return global_stats ? 0 : 1; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp new file mode 100644 index 00000000..348c5f4a --- /dev/null +++ b/test/tools/yulopti.cpp @@ -0,0 +1,233 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ +/** + * Interactive yul optimizer + */ + +#include <libdevcore/CommonIO.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/parsing/Parser.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <libyul/optimiser/BlockFlattener.h> +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/MainFunction.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/VarDeclPropagator.h> + +#include <libdevcore/JSON.h> + +#include <boost/program_options.hpp> + +#include <string> +#include <sstream> +#include <iostream> + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::assembly; +using namespace dev::yul; + +namespace po = boost::program_options; + +class YulOpti +{ +public: + void printErrors(Scanner const& _scanner) + { + SourceReferenceFormatter formatter(cout, [&](string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: m_errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); + } + + bool parse(string const& _input) + { + ErrorReporter errorReporter(m_errors); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input), ""); + m_ast = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); + if (!m_ast || !errorReporter.errors().empty()) + { + cout << "Error parsing source." << endl; + printErrors(*scanner); + return false; + } + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + AsmAnalyzer analyzer( + *m_analysisInfo, + errorReporter, + EVMVersion::byzantium(), + boost::none, + AsmFlavour::Strict + ); + if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) + { + cout << "Error analyzing source." << endl; + printErrors(*scanner); + return false; + } + return true; + } + + void runInteractive(string source) + { + bool disambiguated = false; + while (true) + { + cout << "----------------------" << endl; + cout << source << endl; + if (!parse(source)) + return; + if (!disambiguated) + { + *m_ast = boost::get<assembly::Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + m_analysisInfo.reset(); + m_nameDispenser = make_shared<NameDispenser>(*m_ast); + disambiguated = true; + } + cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; + cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; + cout << " (r)edundant assign elim./re(m)aterializer? "; + cout.flush(); + int option = readStandardInputChar(); + cout << ' ' << char(option) << endl; + switch (option) + { + case 'q': + return; + case 'f': + BlockFlattener{}(*m_ast); + break; + case 'c': + (CommonSubexpressionEliminator{})(*m_ast); + break; + case 'd': + (VarDeclPropagator{})(*m_ast); + break; + case 'x': + ExpressionSplitter{*m_nameDispenser}(*m_ast); + break; + case 'j': + ExpressionJoiner::run(*m_ast); + break; + case 'g': + (FunctionGrouper{})(*m_ast); + break; + case 'h': + (FunctionHoister{})(*m_ast); + break; + case 'e': + ExpressionInliner{*m_ast}.run(); + break; + case 'i': + FullInliner(*m_ast, *m_nameDispenser).run(); + break; + case 's': + ExpressionSimplifier::run(*m_ast); + break; + case 'u': + UnusedPruner::runUntilStabilised(*m_ast); + break; + case 'a': + SSATransform::run(*m_ast, *m_nameDispenser); + break; + case 'r': + RedundantAssignEliminator::run(*m_ast); + break; + case 'm': + Rematerialiser{}(*m_ast); + break; + default: + cout << "Unknown option." << endl; + } + source = AsmPrinter{}(*m_ast); + } + } + +private: + ErrorList m_errors; + shared_ptr<assembly::Block> m_ast; + shared_ptr<AsmAnalysisInfo> m_analysisInfo; + shared_ptr<NameDispenser> m_nameDispenser; +}; + +int main(int argc, char** argv) +{ + po::options_description options( + R"(yulopti, yul optimizer exploration tool. +Usage: yulopti [Options] <file> +Reads <file> as yul code and applies optimizer steps to it, +interactively read from stdin. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ( + "input-file", + po::value<string>(), + "input file" + ) + ("help", "Show this help screen."); + + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add("input-file", 1); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options).positional(filesPositions); + po::store(cmdLineParser.run(), arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return 1; + } + + string input; + if (arguments.count("input-file")) + YulOpti{}.runInteractive(readFileAsString(arguments["input-file"].as<string>())); + else + cout << options; + + return 0; +} |