[yul-phaser] Returning an ErrorList from Program::load() if program has errors and printing them in Phaser

This commit is contained in:
Kamil Śliwak 2020-02-28 03:33:46 +01:00
parent 9ef63a9789
commit 8ca0d90aae
5 changed files with 50 additions and 29 deletions

View File

@ -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"

View File

@ -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()));
}

View File

@ -125,7 +125,13 @@ 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)

View File

@ -17,11 +17,8 @@
#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>
@ -75,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,
@ -111,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);
@ -119,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;
assert(errorReporter.errors().empty());
return 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);
@ -133,9 +144,10 @@ 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;
assert(errorReporter.errors().empty());
return analysisInfo;
}

View File

@ -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
@ -72,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); }
@ -91,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
);