Skip to content

Commit

Permalink
Merge pull request #15611 from ethereum/unify-yul-parsing-across-test…
Browse files Browse the repository at this point in the history
…-suites

Unify Yul parsing across test suites
  • Loading branch information
cameel authored Dec 17, 2024
2 parents 10fbfca + 46b0616 commit 6c28421
Show file tree
Hide file tree
Showing 31 changed files with 280 additions and 366 deletions.
7 changes: 7 additions & 0 deletions libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,13 @@ std::shared_ptr<Object> YulStack::parserResult() const
return m_parserResult;
}

Dialect const& YulStack::dialect() const
{
yulAssert(m_stackState >= AnalysisSuccessful);
yulAssert(m_parserResult && m_parserResult->dialect());
return *m_parserResult->dialect();
}

void YulStack::reportUnimplementedFeatureError(UnimplementedFeatureError const& _error)
{
yulAssert(_error.comment(), "Errors must include a message for the user.");
Expand Down
3 changes: 3 additions & 0 deletions libyul/YulStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class YulStack: public langutil::CharStreamProvider
/// @returns the errors generated during parsing, analysis (and potentially assembly).
langutil::ErrorList const& errors() const { return m_errors; }
bool hasErrors() const { return m_errorReporter.hasErrors(); }
bool hasErrorsWarningsOrInfos() const { return m_errorReporter.hasErrorsWarningsOrInfos(); }

/// Pretty-print the input after having parsed it.
std::string print() const;
Expand All @@ -150,6 +151,8 @@ class YulStack: public langutil::CharStreamProvider
/// Return the parsed and analyzed object.
std::shared_ptr<Object> parserResult() const;

Dialect const& dialect() const;

langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; }

private:
Expand Down
29 changes: 14 additions & 15 deletions test/libsolidity/MemoryGuardTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@

#include <test/Common.h>
#include <test/libyul/Common.h>

#include <libsolidity/codegen/ir/Common.h>

#include <libsolutil/Algorithms.h>
#include <libsolutil/StringUtils.h>

#include <libyul/Object.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/AST.h>
#include <libyul/YulStack.h>

#include <fstream>
#include <memory>
#include <stdexcept>
Expand All @@ -38,6 +42,7 @@ using namespace solidity::langutil;
using namespace solidity::frontend;
using namespace solidity::frontend::test;
using namespace solidity::test;
using namespace solidity::yul::test;
using namespace yul;

void MemoryGuardTest::setupCompiler(CompilerStack& _compiler)
Expand All @@ -59,35 +64,29 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con
m_obtainedResult.clear();
for (std::string contractName: compiler().contractNames())
{
auto const& dialect = CommonOptions::get().evmDialect();
ErrorList errors;
std::optional<std::string> const& ir = compiler().yulIR(contractName);
solAssert(ir);
auto [object, analysisInfo] = yul::test::parse(
*ir,
dialect,
errors
);
soltestAssert(ir);

if (!object || !analysisInfo || Error::containsErrors(errors))
YulStack yulStack = parseYul(*ir);
if (yulStack.hasErrors())
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing IR:" << std::endl;
printPrefixed(_stream, formatErrors(filterErrors(errors), _formatted), _linePrefix);
printYulErrors(yulStack, _stream, _linePrefix, _formatted);
return TestResult::FatalError;
}

auto handleObject = [&](std::string const& _kind, Object const& _object) {
m_obtainedResult += contractName + "(" + _kind + ") " + (findFunctionCalls(
_object.code()->root(),
"memoryguard",
dialect
yulStack.dialect()
).empty() ? "false" : "true") + "\n";
};
handleObject("creation", *object);
size_t deployedIndex = object->subIndexByName.at(
handleObject("creation", *yulStack.parserResult());
size_t deployedIndex = yulStack.parserResult()->subIndexByName.at(
IRNames::deployedObject(compiler().contractDefinition(contractName))
);
handleObject("runtime", dynamic_cast<Object const&>(*object->subObjects[deployedIndex]));
handleObject("runtime", dynamic_cast<Object const&>(*yulStack.parserResult()->subObjects[deployedIndex]));
}
return checkResult(_stream, _linePrefix, _formatted);
}
102 changes: 56 additions & 46 deletions test/libyul/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include <test/libyul/Common.h>

#include <test/libsolidity/util/SoltestErrors.h>

#include <test/Common.h>

#include <libyul/optimiser/Disambiguator.h>
Expand All @@ -30,76 +32,69 @@
#include <libyul/AST.h>
#include <libyul/backends/evm/EVMDialect.h>

#include <libsolutil/AnsiColorized.h>

#include <liblangutil/DebugInfoSelection.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/SourceReferenceFormatter.h>

#include <boost/test/unit_test.hpp>

#include <variant>

using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::yul;
using namespace solidity::langutil;
using namespace solidity::util;
using namespace solidity::test;

namespace
{
Dialect const& defaultDialect()
{
return yul::EVMDialect::strictAssemblyForEVM(
solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion()
);
}
}

std::pair<std::shared_ptr<AST const>, std::shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(std::string const& _source)
YulStack yul::test::parseYul(
std::string const& _source,
std::string _sourceUnitName,
std::optional<frontend::OptimiserSettings> _optimiserSettings
)
{
YulStack stack(
solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack yulStack(
CommonOptions::get().evmVersion(),
CommonOptions::get().eofVersion(),
YulStack::Language::StrictAssembly,
solidity::test::CommonOptions::get().optimize ?
solidity::frontend::OptimiserSettings::standard() :
solidity::frontend::OptimiserSettings::minimal(),
_optimiserSettings.has_value() ?
*_optimiserSettings :
(CommonOptions::get().optimize ? OptimiserSettings::standard() : OptimiserSettings::minimal()),
DebugInfoSelection::All()
);
if (!stack.parseAndAnalyze("", _source) || Error::hasErrorsWarningsOrInfos(stack.errors()))
BOOST_FAIL("Invalid source.");
return std::make_pair(stack.parserResult()->code(), stack.parserResult()->analysisInfo);
}

std::pair<std::shared_ptr<Object>, std::shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
std::string const& _source,
Dialect const& _dialect,
ErrorList& _errors
)
{
ErrorReporter errorReporter(_errors);
CharStream stream(_source, "");
std::shared_ptr<Scanner> scanner = std::make_shared<Scanner>(stream);
std::shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false);
if (!parserResult)
return {};
if (!parserResult->hasCode() || errorReporter.hasErrors())
return {};
std::shared_ptr<AsmAnalysisInfo> analysisInfo = std::make_shared<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->summarizeStructure());
// TODO this should be done recursively.
if (!analyzer.analyze(parserResult->code()->root()) || errorReporter.hasErrors())
return {};
return {std::move(parserResult), std::move(analysisInfo)};
bool successful = yulStack.parseAndAnalyze(_sourceUnitName, _source);
if (!successful)
soltestAssert(yulStack.hasErrors());
else
{
soltestAssert(!yulStack.hasErrors());
soltestAssert(yulStack.parserResult());
soltestAssert(yulStack.parserResult()->code());
soltestAssert(yulStack.parserResult()->analysisInfo);
}
return yulStack;
}

yul::Block yul::test::disambiguate(std::string const& _source)
{
auto result = parse(_source);
return std::get<Block>(Disambiguator(defaultDialect(), *result.second, {})(result.first->root()));
YulStack yulStack = parseYul(_source);
soltestAssert(!yulStack.hasErrorsWarningsOrInfos());
return std::get<Block>(Disambiguator(
yulStack.dialect(),
*yulStack.parserResult()->analysisInfo,
{}
)(yulStack.parserResult()->code()->root()));
}

std::string yul::test::format(std::string const& _source)
{
return AsmPrinter::format(*parse(_source).first);
YulStack yulStack = parseYul(_source);
solUnimplementedAssert(yulStack.parserResult()->subObjects.empty(), "Subobjects not supported.");
soltestAssert(!yulStack.hasErrorsWarningsOrInfos());
return AsmPrinter::format(*yulStack.parserResult()->code());
}

namespace
Expand Down Expand Up @@ -134,3 +129,18 @@ yul::Dialect const& yul::test::dialect(std::string const& _name, langutil::EVMVe

return validDialects.at(_name)(_evmVersion, _eofVersion);
}

void yul::test::printYulErrors(
YulStack const& _yulStack,
std::ostream& _stream,
std::string const& _linePrefix,
bool const _formatted
)
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED})
<< _linePrefix
<< "Error parsing source."
<< std::endl;
SourceReferenceFormatter formatter{_stream, _yulStack, true, false};
formatter.printErrorInformation(_yulStack.errors());
}
21 changes: 16 additions & 5 deletions test/libyul/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@

#pragma once

#include <libsolidity/interface/OptimiserSettings.h>

#include <liblangutil/EVMVersion.h>

#include <string>
#include <vector>
#include <memory>
#include <optional>

namespace solidity::langutil
{
Expand All @@ -40,20 +43,28 @@ struct Block;
class Object;
class Dialect;
class AST;
class YulStack;
}

namespace solidity::yul::test
{

std::pair<std::shared_ptr<AST const>, std::shared_ptr<AsmAnalysisInfo>>
parse(std::string const& _source);

std::pair<std::shared_ptr<Object>, std::shared_ptr<AsmAnalysisInfo>>
parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& _errors);
yul::YulStack parseYul(
std::string const& _source,
std::string _sourceUnitName = "",
std::optional<frontend::OptimiserSettings> _optimiserSettings = std::nullopt
);

Block disambiguate(std::string const& _source);
std::string format(std::string const& _source);

solidity::yul::Dialect const& dialect(std::string const& _name, langutil::EVMVersion _evmVersion, std::optional<uint8_t> _eofVersion);

void printYulErrors(
yul::YulStack const& _yulStack,
std::ostream& _stream,
std::string const& _linePrefix,
bool const _formatted
);

}
15 changes: 8 additions & 7 deletions test/libyul/CompilabilityChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@

#include <test/Common.h>

#include <test/libsolidity/util/SoltestErrors.h>

#include <test/libyul/Common.h>
#include <libyul/backends/evm/EVMDialect.h>

#include <libyul/CompilabilityChecker.h>
#include <libyul/YulStack.h>

#include <boost/test/unit_test.hpp>

Expand All @@ -34,12 +36,11 @@ namespace
{
std::string check(std::string const& _input)
{
Object obj;
auto parsingResult = yul::test::parse(_input);
obj.setCode(parsingResult.first, parsingResult.second);
BOOST_REQUIRE(obj.hasCode());
BOOST_REQUIRE(obj.dialect());
auto functions = CompilabilityChecker(obj, true).stackDeficit;
YulStack yulStack = parseYul(_input);
solUnimplementedAssert(yulStack.parserResult()->subObjects.empty(), "Tests with subobjects not supported.");
soltestAssert(!yulStack.hasErrorsWarningsOrInfos());

auto functions = CompilabilityChecker(*yulStack.parserResult(), true).stackDeficit;
std::string out;
for (auto const& function: functions)
out += function.first.str() + ": " + std::to_string(function.second) + " ";
Expand Down
24 changes: 12 additions & 12 deletions test/libyul/ControlFlowGraphTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
#include <libyul/backends/evm/StackHelpers.h>
#include <libyul/Object.h>
#include <libyul/YulStack.h>

#include <libsolutil/AnsiColorized.h>
#include <libsolutil/Visitor.h>

#ifdef ISOLTEST
Expand All @@ -45,11 +45,7 @@ ControlFlowGraphTest::ControlFlowGraphTest(std::string const& _filename):
{
m_source = m_reader.source();
auto dialectName = m_reader.stringSetting("dialect", "evm");
m_dialect = &dialect(
dialectName,
solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion()
);
soltestAssert(dialectName == "evm"); // We only have one dialect now
m_expectation = m_reader.simpleExpectations();
}

Expand Down Expand Up @@ -201,20 +197,24 @@ class ControlFlowGraphPrinter

TestCase::TestResult ControlFlowGraphTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted)
{
ErrorList errors;
auto [object, analysisInfo] = parse(m_source, *m_dialect, errors);
if (!object || !analysisInfo || Error::containsErrors(errors))
YulStack yulStack = parseYul(m_source);
solUnimplementedAssert(yulStack.parserResult()->subObjects.empty(), "Tests with subobjects not supported.");
if (yulStack.hasErrors())
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl;
printYulErrors(yulStack, _stream, _linePrefix, _formatted);
return TestResult::FatalError;
}

std::ostringstream output;

std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, object->code()->root());
std::unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(
*yulStack.parserResult()->analysisInfo,
yulStack.dialect(),
yulStack.parserResult()->code()->root()
);

output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n";
ControlFlowGraphPrinter printer{output, *m_dialect};
ControlFlowGraphPrinter printer{output, yulStack.dialect()};
printer(*cfg->entry);
for (auto function: cfg->functions)
printer(cfg->functionInfo.at(function));
Expand Down
Loading

0 comments on commit 6c28421

Please sign in to comment.