mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8422 from imapp-pl/yul-phaser-error-handling
[yul-phaser] Error handling
This commit is contained in:
commit
cfd315e17d
@ -36,7 +36,7 @@ class FitnessMetricFixture
|
||||
protected:
|
||||
FitnessMetricFixture():
|
||||
m_sourceStream(SampleSourceCode, ""),
|
||||
m_program(Program::load(m_sourceStream)) {}
|
||||
m_program(get<Program>(Program::load(m_sourceStream))) {}
|
||||
|
||||
static constexpr char SampleSourceCode[] =
|
||||
"{\n"
|
||||
|
@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
Program programCopy(program);
|
||||
|
||||
@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream)
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
sourceStream.setPosition(5);
|
||||
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
BOOST_TEST(CodeSize::codeSize(program.ast()) == 2);
|
||||
}
|
||||
@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(load_should_disambiguate)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
// skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not.
|
||||
Block const& parentBlock = skipRedundantBlocks(program.ast());
|
||||
@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
BOOST_TEST(program.ast().statements.size() == 3);
|
||||
BOOST_TEST(holds_alternative<Block>(program.ast().statements[0]));
|
||||
@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
// skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not.
|
||||
Block const& parentBlock = skipRedundantBlocks(program.ast());
|
||||
@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed)
|
||||
string sourceCode("invalid program\n");
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
|
||||
BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram);
|
||||
BOOST_TEST(holds_alternative<ErrorList>(Program::load(sourceStream)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed)
|
||||
@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyze
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
|
||||
BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram);
|
||||
BOOST_TEST(holds_alternative<ErrorList>(Program::load(sourceStream)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(optimise)
|
||||
@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(optimise)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
[[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast());
|
||||
assert(parentBlockBefore.statements.size() == 2);
|
||||
@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(output_operator)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
// NOTE: The snippet above was chosen so that the few optimisations applied automatically by load()
|
||||
// as of now do not change the code significantly. If that changes, you may have to update it.
|
||||
@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(toJson)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
Json::Value parsingResult;
|
||||
string errors;
|
||||
@ -270,7 +270,7 @@ BOOST_AUTO_TEST_CASE(codeSize)
|
||||
"}\n"
|
||||
);
|
||||
CharStream sourceStream(sourceCode, current_test_case().p_name);
|
||||
auto program = Program::load(sourceStream);
|
||||
Program program = get<Program>(Program::load(sourceStream));
|
||||
|
||||
BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast()));
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
namespace solidity::phaser
|
||||
{
|
||||
|
||||
struct InvalidProgram: virtual util::Exception {};
|
||||
struct BadInput: virtual util::Exception {};
|
||||
struct InvalidProgram: virtual BadInput {};
|
||||
struct NoInputFiles: virtual BadInput {};
|
||||
struct MissingFile: virtual BadInput {};
|
||||
|
||||
}
|
||||
|
@ -125,27 +125,32 @@ ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_m
|
||||
Program ProgramFactory::build(Options const& _options)
|
||||
{
|
||||
CharStream sourceCode = loadSource(_options.inputFile);
|
||||
return Program::load(sourceCode);
|
||||
variant<Program, ErrorList> programOrErrors = Program::load(sourceCode);
|
||||
if (holds_alternative<ErrorList>(programOrErrors))
|
||||
{
|
||||
cerr << get<ErrorList>(programOrErrors) << endl;
|
||||
assertThrow(false, InvalidProgram, "Failed to load program " + _options.inputFile);
|
||||
}
|
||||
return move(get<Program>(programOrErrors));
|
||||
}
|
||||
|
||||
CharStream ProgramFactory::loadSource(string const& _sourcePath)
|
||||
{
|
||||
assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist");
|
||||
assertThrow(boost::filesystem::exists(_sourcePath), MissingFile, "Source file does not exist: " + _sourcePath);
|
||||
|
||||
string sourceCode = readFileAsString(_sourcePath);
|
||||
return CharStream(sourceCode, _sourcePath);
|
||||
}
|
||||
|
||||
int Phaser::main(int _argc, char** _argv)
|
||||
void Phaser::main(int _argc, char** _argv)
|
||||
{
|
||||
CommandLineParsingResult parsingResult = parseCommandLine(_argc, _argv);
|
||||
if (parsingResult.exitCode != 0)
|
||||
return parsingResult.exitCode;
|
||||
optional<po::variables_map> arguments = parseCommandLine(_argc, _argv);
|
||||
if (!arguments.has_value())
|
||||
return;
|
||||
|
||||
initialiseRNG(parsingResult.arguments);
|
||||
initialiseRNG(arguments.value());
|
||||
|
||||
runAlgorithm(parsingResult.arguments);
|
||||
return 0;
|
||||
runAlgorithm(arguments.value());
|
||||
}
|
||||
|
||||
Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
||||
@ -192,38 +197,27 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
||||
return {keywordDescription, positionalDescription};
|
||||
}
|
||||
|
||||
Phaser::CommandLineParsingResult Phaser::parseCommandLine(int _argc, char** _argv)
|
||||
optional<po::variables_map> Phaser::parseCommandLine(int _argc, char** _argv)
|
||||
{
|
||||
auto [keywordDescription, positionalDescription] = buildCommandLineDescription();
|
||||
|
||||
po::variables_map arguments;
|
||||
po::notify(arguments);
|
||||
|
||||
try
|
||||
{
|
||||
po::command_line_parser parser(_argc, _argv);
|
||||
parser.options(keywordDescription).positional(positionalDescription);
|
||||
po::store(parser.run(), arguments);
|
||||
}
|
||||
catch (po::error const & _exception)
|
||||
{
|
||||
cerr << _exception.what() << endl;
|
||||
return {1, move(arguments)};
|
||||
}
|
||||
po::command_line_parser parser(_argc, _argv);
|
||||
parser.options(keywordDescription).positional(positionalDescription);
|
||||
po::store(parser.run(), arguments);
|
||||
|
||||
if (arguments.count("help") > 0)
|
||||
{
|
||||
cout << keywordDescription << endl;
|
||||
return {2, move(arguments)};
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
if (arguments.count("input-file") == 0)
|
||||
{
|
||||
cerr << "Missing argument: input-file." << endl;
|
||||
return {1, move(arguments)};
|
||||
}
|
||||
assertThrow(false, NoInputFiles, "Missing argument: input-file.");
|
||||
|
||||
return {0, arguments};
|
||||
return arguments;
|
||||
}
|
||||
|
||||
void Phaser::initialiseRNG(po::variables_map const& _arguments)
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
@ -128,7 +129,7 @@ private:
|
||||
class Phaser
|
||||
{
|
||||
public:
|
||||
static int main(int argc, char** argv);
|
||||
static void main(int argc, char** argv);
|
||||
|
||||
private:
|
||||
struct CommandLineDescription
|
||||
@ -137,14 +138,8 @@ private:
|
||||
boost::program_options::positional_options_description positionalDescription;
|
||||
};
|
||||
|
||||
struct CommandLineParsingResult
|
||||
{
|
||||
int exitCode;
|
||||
boost::program_options::variables_map arguments;
|
||||
};
|
||||
|
||||
static CommandLineDescription buildCommandLineDescription();
|
||||
static CommandLineParsingResult parseCommandLine(int _argc, char** _argv);
|
||||
static std::optional<boost::program_options::variables_map> parseCommandLine(int _argc, char** _argv);
|
||||
static void initialiseRNG(boost::program_options::variables_map const& _arguments);
|
||||
|
||||
static void runAlgorithm(boost::program_options::variables_map const& _arguments);
|
||||
|
@ -17,11 +17,9 @@
|
||||
|
||||
#include <tools/yulPhaser/Program.h>
|
||||
|
||||
#include <tools/yulPhaser/Exceptions.h>
|
||||
|
||||
#include <liblangutil/CharStream.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
@ -57,6 +55,16 @@ ostream& operator<<(ostream& _stream, Program const& _program);
|
||||
|
||||
}
|
||||
|
||||
ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors)
|
||||
{
|
||||
SourceReferenceFormatter formatter(_outputStream);
|
||||
|
||||
for (auto const& error: _errors)
|
||||
formatter.printErrorInformation(*error);
|
||||
|
||||
return _outputStream;
|
||||
}
|
||||
|
||||
Program::Program(Program const& program):
|
||||
m_ast(make_unique<Block>(get<Block>(ASTCopier{}(*program.m_ast)))),
|
||||
m_dialect{program.m_dialect},
|
||||
@ -64,16 +72,29 @@ Program::Program(Program const& program):
|
||||
{
|
||||
}
|
||||
|
||||
Program Program::load(CharStream& _sourceCode)
|
||||
variant<Program, ErrorList> Program::load(CharStream& _sourceCode)
|
||||
{
|
||||
// ASSUMPTION: parseSource() rewinds the stream on its own
|
||||
Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{});
|
||||
unique_ptr<Block> ast = parseSource(dialect, _sourceCode);
|
||||
unique_ptr<AsmAnalysisInfo> analysisInfo = analyzeAST(dialect, *ast);
|
||||
|
||||
variant<unique_ptr<Block>, ErrorList> astOrErrors = parseSource(dialect, _sourceCode);
|
||||
if (holds_alternative<ErrorList>(astOrErrors))
|
||||
return get<ErrorList>(astOrErrors);
|
||||
|
||||
variant<unique_ptr<AsmAnalysisInfo>, ErrorList> analysisInfoOrErrors = analyzeAST(
|
||||
dialect,
|
||||
*get<unique_ptr<Block>>(astOrErrors)
|
||||
);
|
||||
if (holds_alternative<ErrorList>(analysisInfoOrErrors))
|
||||
return get<ErrorList>(analysisInfoOrErrors);
|
||||
|
||||
Program program(
|
||||
dialect,
|
||||
disambiguateAST(dialect, *ast, *analysisInfo)
|
||||
disambiguateAST(
|
||||
dialect,
|
||||
*get<unique_ptr<Block>>(astOrErrors),
|
||||
*get<unique_ptr<AsmAnalysisInfo>>(analysisInfoOrErrors)
|
||||
)
|
||||
);
|
||||
program.optimise({
|
||||
FunctionHoister::name,
|
||||
@ -100,7 +121,7 @@ string Program::toJson() const
|
||||
return jsonPrettyPrint(serializedAst);
|
||||
}
|
||||
|
||||
unique_ptr<Block> Program::parseSource(Dialect const& _dialect, CharStream _source)
|
||||
variant<unique_ptr<Block>, ErrorList> Program::parseSource(Dialect const& _dialect, CharStream _source)
|
||||
{
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
@ -108,13 +129,14 @@ unique_ptr<Block> Program::parseSource(Dialect const& _dialect, CharStream _sour
|
||||
Parser parser(errorReporter, _dialect);
|
||||
|
||||
unique_ptr<Block> ast = parser.parse(scanner, false);
|
||||
assertThrow(ast != nullptr, InvalidProgram, "Error parsing source");
|
||||
assert(errorReporter.errors().empty());
|
||||
if (ast == nullptr)
|
||||
return errors;
|
||||
|
||||
return ast;
|
||||
assert(errorReporter.errors().empty());
|
||||
return variant<unique_ptr<Block>, ErrorList>(move(ast));
|
||||
}
|
||||
|
||||
unique_ptr<AsmAnalysisInfo> Program::analyzeAST(Dialect const& _dialect, Block const& _ast)
|
||||
variant<unique_ptr<AsmAnalysisInfo>, ErrorList> Program::analyzeAST(Dialect const& _dialect, Block const& _ast)
|
||||
{
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
@ -122,10 +144,11 @@ unique_ptr<AsmAnalysisInfo> Program::analyzeAST(Dialect const& _dialect, Block c
|
||||
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect);
|
||||
|
||||
bool analysisSuccessful = analyzer.analyze(_ast);
|
||||
assertThrow(analysisSuccessful, InvalidProgram, "Error analyzing source");
|
||||
assert(errorReporter.errors().empty());
|
||||
if (!analysisSuccessful)
|
||||
return errors;
|
||||
|
||||
return analysisInfo;
|
||||
assert(errorReporter.errors().empty());
|
||||
return variant<unique_ptr<AsmAnalysisInfo>, ErrorList>(move(analysisInfo));
|
||||
}
|
||||
|
||||
unique_ptr<Block> Program::disambiguateAST(
|
||||
|
@ -20,10 +20,13 @@
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace solidity::langutil
|
||||
@ -41,6 +44,13 @@ struct Dialect;
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors);
|
||||
|
||||
}
|
||||
|
||||
namespace solidity::phaser
|
||||
{
|
||||
|
||||
@ -65,7 +75,7 @@ public:
|
||||
Program operator=(Program const& program) = delete;
|
||||
Program operator=(Program&& program) = delete;
|
||||
|
||||
static Program load(langutil::CharStream& _sourceCode);
|
||||
static std::variant<Program, langutil::ErrorList> load(langutil::CharStream& _sourceCode);
|
||||
void optimise(std::vector<std::string> const& _optimisationSteps);
|
||||
|
||||
size_t codeSize() const { return computeCodeSize(*m_ast); }
|
||||
@ -84,11 +94,11 @@ private:
|
||||
m_nameDispenser(_dialect, *m_ast, {})
|
||||
{}
|
||||
|
||||
static std::unique_ptr<yul::Block> parseSource(
|
||||
static std::variant<std::unique_ptr<yul::Block>, langutil::ErrorList> parseSource(
|
||||
yul::Dialect const& _dialect,
|
||||
langutil::CharStream _source
|
||||
);
|
||||
static std::unique_ptr<yul::AsmAnalysisInfo> analyzeAST(
|
||||
static std::variant<std::unique_ptr<yul::AsmAnalysisInfo>, langutil::ErrorList> analyzeAST(
|
||||
yul::Dialect const& _dialect,
|
||||
yul::Block const& _ast
|
||||
);
|
||||
|
@ -18,17 +18,72 @@
|
||||
#include <tools/yulPhaser/Exceptions.h>
|
||||
#include <tools/yulPhaser/Phaser.h>
|
||||
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
return solidity::phaser::Phaser::main(argc, argv);
|
||||
solidity::phaser::Phaser::main(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
catch (solidity::phaser::InvalidProgram const& exception)
|
||||
catch (boost::program_options::error const& exception)
|
||||
{
|
||||
// Bad input data. Invalid command-line parameters.
|
||||
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "ERROR: " << exception.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
catch (solidity::phaser::BadInput const& exception)
|
||||
{
|
||||
// Bad input data. Syntax errors in the input program, semantic errors in command-line
|
||||
// parameters, etc.
|
||||
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "ERROR: " << exception.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
catch (solidity::util::Exception const& exception)
|
||||
{
|
||||
// Something's seriously wrong. Probably a bug in the program or a missing handler (which
|
||||
// is really also a bug). The exception should have been handled gracefully by this point
|
||||
// if it's something that can happen in normal usage. E.g. an error in the input or a
|
||||
// failure of some part of the system that's outside of control of the application (disk,
|
||||
// network, etc.). The bug should be reported and investigated so our job here is just to
|
||||
// provide as much useful information about it as possible.
|
||||
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "UNCAUGHT EXCEPTION!" << std::endl;
|
||||
|
||||
// We can print some useful diagnostic info for this particular exception type.
|
||||
std::cerr << "Location: " << exception.lineInfo() << std::endl;
|
||||
|
||||
char const* const* function = boost::get_error_info<boost::throw_function>(exception);
|
||||
if (function != nullptr)
|
||||
std::cerr << "Function: " << *function << std::endl;
|
||||
|
||||
// Let it crash. The terminate() will print some more stuff useful for debugging like
|
||||
// what() and the actual exception type.
|
||||
throw;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
// Again, probably a bug but this time it's just plain std::exception so there's no point
|
||||
// in doing anything special. terminate() will do an adequate job.
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "UNCAUGHT EXCEPTION!" << std::endl;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Some people don't believe these exist.
|
||||
// I have no idea what this is and it's flying towards me so technically speaking it's an
|
||||
// unidentified flying object.
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "UFO SPOTTED!" << std::endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user