aboutsummaryrefslogtreecommitdiffstats
path: root/test/tools
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-11-14 02:33:35 +0800
committerGitHub <noreply@github.com>2018-11-14 02:33:35 +0800
commit1d4f565a64988a3400847d2655ca24f73f234bc6 (patch)
treecaaa6c26e307513505349b50ca4f2a8a9506752b /test/tools
parent59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (diff)
parent91b6b8a88e76016e0324036cb7a7f9300a1e2439 (diff)
downloaddexon-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.txt7
-rw-r--r--test/tools/fuzzer.cpp103
-rw-r--r--test/tools/isoltest.cpp305
-rw-r--r--test/tools/yulopti.cpp233
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;
+}