From 3078ab2d9c060d27e917d055df8fe5d660e5f93b Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sun, 17 Apr 2022 18:11:23 +0300 Subject: [PATCH 01/23] yulopti: Add support for Yul Objects --- libyul/ObjectParser.cpp | 3 + test/tools/yulopti.cpp | 209 +++++++++++++++++++++++++++++++--------- 2 files changed, 166 insertions(+), 46 deletions(-) diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 2038959de..a511e33f3 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -99,7 +99,10 @@ std::shared_ptr ObjectParser::parseObject(Object* _containingObject) fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\"."); } if (_containingObject) + { + ret->subId = _containingObject->subObjects.size(); addNamedSubObject(*_containingObject, ret->name, ret); + } expectToken(Token::RBrace); diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 20491afe1..b17cd1f5d 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -86,32 +87,44 @@ public: }.printErrorInformation(_errors); } - void parse(string const& _input) + shared_ptr getSubObject(shared_ptr const& _rootObject, string const& _path) + { + if (_path.empty()) + return _rootObject; + + auto pathToSubObject = _rootObject->pathToSubObject(YulString(_path)); + auto subObject = _rootObject; + + for (auto const& i: pathToSubObject) + subObject = dynamic_pointer_cast(subObject->subObjects[i]); + + return subObject; + } + + void parse(string const& _input, string const& _objectPath) { ErrorList errors; ErrorReporter errorReporter(errors); CharStream _charStream(_input, ""); + try { - m_ast = yul::Parser(errorReporter, m_dialect).parse(_charStream); - if (!m_ast || !errorReporter.errors().empty()) + ObjectParser parser(errorReporter, m_dialect); + + auto scanner = make_shared(_charStream); + auto content = parser.parse(scanner, false); + + if (content != nullptr) + m_object = getSubObject(content, _objectPath); + + if (!m_object || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; printErrors(_charStream, errors); throw std::runtime_error("Could not parse source."); } - m_analysisInfo = make_unique(); - AsmAnalyzer analyzer( - *m_analysisInfo, - errorReporter, - m_dialect - ); - if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) - { - cerr << "Error analyzing source." << endl; - printErrors(_charStream, errors); - throw std::runtime_error("Could not analyze source."); - } + + analyze(errorReporter); } catch(...) { @@ -170,28 +183,118 @@ public: } } + void objectApply(function _fn) + { + vector> stack; + stack.push_back(m_object); + + while (!stack.empty()) { + auto object = stack.back(); + stack.pop_back(); + + for (auto const& subObjectNode: object->subObjects) { + auto subObject = dynamic_pointer_cast(subObjectNode); + + if (subObject != nullptr) + stack.push_back(subObject); + } + + _fn(*object); + } + } + + void analyze(ErrorReporter& errorReporter) + { + objectApply([&](Object& object) -> void { + object.analysisInfo = make_shared(); + + AsmAnalyzer analyzer( + *object.analysisInfo, + errorReporter, + m_dialect, + {}, + object.qualifiedDataNames() + ); + + bool success = analyzer.analyze(*object.code); + yulAssert(success && !errorReporter.hasErrors(), "Invalid assembly/yul code."); + }); + } + void disambiguate() { - *m_ast = std::get(Disambiguator(m_dialect, *m_analysisInfo)(*m_ast)); - m_analysisInfo.reset(); - m_nameDispenser.reset(*m_ast); + objectApply([&](Object& object) -> void { + object.code = make_shared( + std::get(Disambiguator(m_dialect, *object.analysisInfo)(*object.code)) + ); + + object.analysisInfo.reset(); + }); } - void runSteps(string _source, string _steps) + void runSequence(string_view _steps) { - parse(_source); + objectApply([&](Object& object) -> void { + OptimiserSuite{*m_context}.runSequence(_steps, *object.code); + }); + } + + void runVarNameCleaner() + { + objectApply([&](Object& object) -> void { + VarNameCleaner::run(*m_context, *object.code); + }); + } + + void runStackCompressor() + { + objectApply([&](Object& object) -> void { + StackCompressor::run(m_dialect, object, true, 16); + }); + } + + void parseAndPrint(string _source, string _objectPath) + { + parse(_source, _objectPath); + cout << m_object->toString(&m_dialect) << endl; + } + + void resetNameDispenser() + { + m_nameDispenser = make_shared( + m_dialect, + m_reservedIdentifiers + ); + + m_context = make_shared( + OptimiserStepContext{ + m_dialect, + *m_nameDispenser, + m_reservedIdentifiers, + solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment + } + ); + } + + void runSteps(string _source, string _objectPath, string _steps) + { + parse(_source, _objectPath); disambiguate(); - OptimiserSuite{m_context}.runSequence(_steps, *m_ast); - cout << AsmPrinter{m_dialect}(*m_ast) << endl; + runSequence(_steps); } - void runInteractive(string _source, bool _disambiguated = false) + void runInteractive(string _source, string const& _objectPath, bool _disambiguated = false) { + ErrorList errors; + ErrorReporter errorReporter(errors); bool disambiguated = _disambiguated; + + parse(_source, _objectPath); + while (true) { - parse(_source); disambiguated = disambiguated || (disambiguate(), true); + map const& extraOptions = { // QUIT starts with a non-letter character on purpose to get it to show up on top of the list {'#', ">>> QUIT <<<"}, @@ -213,24 +316,21 @@ public: case '#': return; case ',': - VarNameCleaner::run(m_context, *m_ast); + runVarNameCleaner(); // VarNameCleaner destroys the unique names guarantee of the disambiguator. disambiguated = false; break; case ';': { - Object obj; - obj.code = m_ast; - StackCompressor::run(m_dialect, obj, true, 16); + runStackCompressor(); break; } default: - OptimiserSuite{m_context}.runSequence( - std::string_view(&option, 1), - *m_ast - ); + runSequence(std::string_view(&option, 1)); } - _source = AsmPrinter{m_dialect}(*m_ast); + + resetNameDispenser(); + analyze(errorReporter); } catch (...) { @@ -238,22 +338,26 @@ public: cerr << boost::current_exception_diagnostic_information() << endl; } cout << "----------------------" << endl; - cout << _source << endl; + cout << m_object->toString(&m_dialect) << endl; } } private: - shared_ptr m_ast; + shared_ptr m_object; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; - unique_ptr m_analysisInfo; set const m_reservedIdentifiers = {}; - NameDispenser m_nameDispenser{m_dialect, m_reservedIdentifiers}; - OptimiserStepContext m_context{ + shared_ptr m_nameDispenser = make_shared( m_dialect, - m_nameDispenser, - m_reservedIdentifiers, - solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment - }; + m_reservedIdentifiers + ); + shared_ptr m_context = make_shared( + OptimiserStepContext{ + m_dialect, + *m_nameDispenser, + m_reservedIdentifiers, + solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment + } + ); }; int main(int argc, char** argv) @@ -264,10 +368,11 @@ int main(int argc, char** argv) po::options_description options( R"(yulopti, yul optimizer exploration tool. Usage: yulopti [Options] - Reads as yul code and applies optimizer steps to it, + Reads containing a yul object and applies optimizer steps to it, interactively read from stdin. In non-interactive mode a list of steps has to be provided. If is -, yul code is read from stdin and run non-interactively. + If is provided then only the object matching the given path will be read. Allowed options)", po::options_description::m_default_line_length, @@ -283,6 +388,11 @@ int main(int argc, char** argv) po::value(), "steps to execute non-interactively" ) + ( + "object", + po::value(), + "path to a yul object in the input" + ) ( "non-interactive,n", po::bool_switch(&nonInteractive)->default_value(false), @@ -307,6 +417,8 @@ int main(int argc, char** argv) } string input; + string objectPath; + if (arguments.count("input-file")) { string filename = arguments["input-file"].as(); @@ -330,20 +442,25 @@ int main(int argc, char** argv) return 1; } + if (arguments.count("object")) + objectPath = arguments["object"].as(); + YulOpti yulOpti; bool disambiguated = false; + if (!nonInteractive) - cout << input << endl; + yulOpti.parseAndPrint(input, objectPath); + if (arguments.count("steps")) { string sequence = arguments["steps"].as(); if (!nonInteractive) cout << "----------------------" << endl; - yulOpti.runSteps(input, sequence); + yulOpti.runSteps(input, objectPath, sequence); disambiguated = true; } if (!nonInteractive) - yulOpti.runInteractive(input, disambiguated); + yulOpti.runInteractive(input, objectPath, disambiguated); return 0; } From 97fc74f74d70a747204053a1aaf5522bfe2083ad Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:11:59 +0300 Subject: [PATCH 02/23] do not use Object::pathToSubObject in getSubObject --- libyul/ObjectParser.cpp | 1 - test/tools/yulopti.cpp | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index a511e33f3..1993f6b77 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -100,7 +100,6 @@ std::shared_ptr ObjectParser::parseObject(Object* _containingObject) } if (_containingObject) { - ret->subId = _containingObject->subObjects.size(); addNamedSubObject(*_containingObject, ret->name, ret); } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index b17cd1f5d..74851d333 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -87,18 +87,38 @@ public: }.printErrorInformation(_errors); } - shared_ptr getSubObject(shared_ptr const& _rootObject, string const& _path) + shared_ptr getSubObject(shared_ptr const& _object, string const& _path) { - if (_path.empty()) - return _rootObject; + if (_path.empty() || _path == _object->name.str()) + return _object; - auto pathToSubObject = _rootObject->pathToSubObject(YulString(_path)); - auto subObject = _rootObject; + yulAssert( + boost::algorithm::starts_with(_path, _object->name.str() + "."), + "Assembly object not found." + ); - for (auto const& i: pathToSubObject) - subObject = dynamic_pointer_cast(subObject->subObjects[i]); + const auto subObjectPath = _path.substr(_object->name.str().length() + 1); + const auto subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); - return subObject; + auto subObjectIt = find_if( + _object->subObjects.begin(), + _object->subObjects.end(), + [subObjectName](auto const& subObject) { return subObject->name.str() == subObjectName; } + ); + + yulAssert( + subObjectIt != _object->subObjects.end(), + "Assembly object not found." + ); + + auto subObject = dynamic_pointer_cast(*subObjectIt); + + yulAssert( + subObject != nullptr, + "Assembly object may not contain code." + ); + + return getSubObject(subObject, subObjectPath); } void parse(string const& _input, string const& _objectPath) From 1a2ea45f450aa1f73b4dcc8c5cd78a4c5c523a68 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:14:56 +0300 Subject: [PATCH 03/23] if input was code block, only print code --- test/tools/yulopti.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 74851d333..d36d03701 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -132,6 +132,11 @@ public: ObjectParser parser(errorReporter, m_dialect); auto scanner = make_shared(_charStream); + + if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) { + m_inputWasCodeBlock = true; + } + auto content = parser.parse(scanner, false); if (content != nullptr) @@ -276,7 +281,15 @@ public: void parseAndPrint(string _source, string _objectPath) { parse(_source, _objectPath); - cout << m_object->toString(&m_dialect) << endl; + printObject(); + } + + void printObject() + { + if (!m_inputWasCodeBlock) + cout << m_object->toString(&m_dialect) << endl; + else + cout << AsmPrinter{m_dialect}(*m_object->code) << endl; } void resetNameDispenser() @@ -358,12 +371,14 @@ public: cerr << boost::current_exception_diagnostic_information() << endl; } cout << "----------------------" << endl; - cout << m_object->toString(&m_dialect) << endl; + printObject(); } } private: shared_ptr m_object; + bool m_inputWasCodeBlock; + Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; set const m_reservedIdentifiers = {}; shared_ptr m_nameDispenser = make_shared( From 67e987b53f87eac517fe4448d009208be0d652bc Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:15:39 +0300 Subject: [PATCH 04/23] make objectApply recursive --- test/tools/yulopti.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index d36d03701..61c2541fc 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -208,29 +208,21 @@ public: } } - void objectApply(function _fn) + void objectApply(shared_ptr _object, function _fn) { - vector> stack; - stack.push_back(m_object); + for (auto const& subObjectNode: _object->subObjects) { + auto subObject = dynamic_pointer_cast(subObjectNode); - while (!stack.empty()) { - auto object = stack.back(); - stack.pop_back(); - - for (auto const& subObjectNode: object->subObjects) { - auto subObject = dynamic_pointer_cast(subObjectNode); - - if (subObject != nullptr) - stack.push_back(subObject); - } - - _fn(*object); + if (subObject != nullptr) + objectApply(subObject, _fn); } + + _fn(*_object); } void analyze(ErrorReporter& errorReporter) { - objectApply([&](Object& object) -> void { + objectApply(m_object, [&](Object& object) { object.analysisInfo = make_shared(); AsmAnalyzer analyzer( @@ -248,7 +240,7 @@ public: void disambiguate() { - objectApply([&](Object& object) -> void { + objectApply(m_object, [&](Object& object) { object.code = make_shared( std::get(Disambiguator(m_dialect, *object.analysisInfo)(*object.code)) ); @@ -259,21 +251,21 @@ public: void runSequence(string_view _steps) { - objectApply([&](Object& object) -> void { + objectApply(m_object, [&](Object& object) { OptimiserSuite{*m_context}.runSequence(_steps, *object.code); }); } void runVarNameCleaner() { - objectApply([&](Object& object) -> void { + objectApply(m_object, [&](Object& object) { VarNameCleaner::run(*m_context, *object.code); }); } void runStackCompressor() { - objectApply([&](Object& object) -> void { + objectApply(m_object, [&](Object& object) { StackCompressor::run(m_dialect, object, true, 16); }); } From 66e314b7a01c464db0f99ca2d9416bafbdfe0ae8 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:16:05 +0300 Subject: [PATCH 05/23] modify flag description --- test/tools/yulopti.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 61c2541fc..bf89e6250 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -399,7 +399,7 @@ int main(int argc, char** argv) interactively read from stdin. In non-interactive mode a list of steps has to be provided. If is -, yul code is read from stdin and run non-interactively. - If is provided then only the object matching the given path will be read. + An flag may be provided, specifying a dotted path to an object in the input. Allowed options)", po::options_description::m_default_line_length, From d8173fb158263733fe865a28882fe869c8d076b9 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:19:02 +0300 Subject: [PATCH 06/23] slightly improve a var name --- test/tools/yulopti.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index bf89e6250..53504e22f 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -87,17 +87,17 @@ public: }.printErrorInformation(_errors); } - shared_ptr getSubObject(shared_ptr const& _object, string const& _path) + shared_ptr getSubObject(shared_ptr const& _object, string const& _qualifiedPath) { - if (_path.empty() || _path == _object->name.str()) + if (_qualifiedPath.empty() || _qualifiedPath == _object->name.str()) return _object; yulAssert( - boost::algorithm::starts_with(_path, _object->name.str() + "."), + boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + "."), "Assembly object not found." ); - const auto subObjectPath = _path.substr(_object->name.str().length() + 1); + const auto subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); const auto subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); auto subObjectIt = find_if( From fc50f8ff96508aa2035c75500b760645eb0535e4 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 30 Apr 2022 18:34:19 +0300 Subject: [PATCH 07/23] remove unnecessary brackets --- libyul/ObjectParser.cpp | 2 -- test/tools/yulopti.cpp | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 1993f6b77..2038959de 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -99,9 +99,7 @@ std::shared_ptr ObjectParser::parseObject(Object* _containingObject) fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\"."); } if (_containingObject) - { addNamedSubObject(*_containingObject, ret->name, ret); - } expectToken(Token::RBrace); diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 53504e22f..710dab61d 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -133,9 +133,8 @@ public: auto scanner = make_shared(_charStream); - if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) { + if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) m_inputWasCodeBlock = true; - } auto content = parser.parse(scanner, false); From 98b598fdc42a503c91dd7d9fc87924b4ac26f596 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Fri, 28 Oct 2022 20:45:26 +0300 Subject: [PATCH 08/23] fix: apply coding style suggestions --- test/tools/yulopti.cpp | 114 ++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 710dab61d..57a473ce6 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -100,10 +101,9 @@ public: const auto subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); const auto subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); - auto subObjectIt = find_if( - _object->subObjects.begin(), - _object->subObjects.end(), - [subObjectName](auto const& subObject) { return subObject->name.str() == subObjectName; } + auto subObjectIt = ranges::find_if( + _object->subObjects, + [subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } ); yulAssert( @@ -125,13 +125,13 @@ public: { ErrorList errors; ErrorReporter errorReporter(errors); - CharStream _charStream(_input, ""); + CharStream charStream(_input, ""); try { ObjectParser parser(errorReporter, m_dialect); - auto scanner = make_shared(_charStream); + auto scanner = make_shared(charStream); if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) m_inputWasCodeBlock = true; @@ -144,16 +144,16 @@ public: if (!m_object || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; - printErrors(_charStream, errors); - throw std::runtime_error("Could not parse source."); + printErrors(charStream, errors); + throw runtime_error("Could not parse source."); } - analyze(errorReporter); + runCodeAnalyzer(errorReporter); } catch(...) { cerr << "Fatal error during parsing: " << endl; - printErrors(_charStream, errors); + printErrors(charStream, errors); throw; } } @@ -166,7 +166,7 @@ public: yulAssert(_columns > 0); auto const& optimiserSteps = OptimiserSuite::stepAbbreviationToNameMap(); auto hasShorterString = [](auto const& a, auto const& b) { return a.second.size() < b.second.size(); }; - size_t longestDescriptionLength = std::max( + size_t longestDescriptionLength = max( max_element(optimiserSteps.begin(), optimiserSteps.end(), hasShorterString)->second.size(), max_element(_extraOptions.begin(), _extraOptions.end(), hasShorterString)->second.size() ); @@ -207,66 +207,86 @@ public: } } - void objectApply(shared_ptr _object, function _fn) + void objectApplyFunction(shared_ptr _object, function _fn) { for (auto const& subObjectNode: _object->subObjects) { auto subObject = dynamic_pointer_cast(subObjectNode); if (subObject != nullptr) - objectApply(subObject, _fn); + objectApplyFunction(subObject, _fn); } _fn(*_object); } - void analyze(ErrorReporter& errorReporter) + void runCodeAnalyzer(ErrorReporter& _errorReporter) { - objectApply(m_object, [&](Object& object) { - object.analysisInfo = make_shared(); + objectApplyFunction( + m_object, + [&](Object& _object) + { + _object.analysisInfo = make_shared(); - AsmAnalyzer analyzer( - *object.analysisInfo, - errorReporter, - m_dialect, - {}, - object.qualifiedDataNames() - ); + AsmAnalyzer analyzer( + *_object.analysisInfo, + _errorReporter, + m_dialect, + {}, + _object.qualifiedDataNames() + ); - bool success = analyzer.analyze(*object.code); - yulAssert(success && !errorReporter.hasErrors(), "Invalid assembly/yul code."); - }); + bool success = analyzer.analyze(*_object.code); + yulAssert(success && !_errorReporter.hasErrors(), "Invalid assembly/yul code."); + } + ); } - void disambiguate() + void runCodeDisambiguator() { - objectApply(m_object, [&](Object& object) { - object.code = make_shared( - std::get(Disambiguator(m_dialect, *object.analysisInfo)(*object.code)) - ); + objectApplyFunction( + m_object, + [&](Object& _object) + { + _object.code = make_shared( + get(Disambiguator(m_dialect, *_object.analysisInfo)(*_object.code)) + ); - object.analysisInfo.reset(); - }); + _object.analysisInfo.reset(); + } + ); } void runSequence(string_view _steps) { - objectApply(m_object, [&](Object& object) { - OptimiserSuite{*m_context}.runSequence(_steps, *object.code); - }); + objectApplyFunction( + m_object, + [&](Object& _object) + { + OptimiserSuite{*m_context}.runSequence(_steps, *_object.code); + } + ); } void runVarNameCleaner() { - objectApply(m_object, [&](Object& object) { - VarNameCleaner::run(*m_context, *object.code); - }); + objectApplyFunction( + m_object, + [&](Object& _object) + { + VarNameCleaner::run(*m_context, *_object.code); + } + ); } void runStackCompressor() { - objectApply(m_object, [&](Object& object) { - StackCompressor::run(m_dialect, object, true, 16); - }); + objectApplyFunction( + m_object, + [&](Object& _object) + { + StackCompressor::run(m_dialect, _object, true, 16); + } + ); } void parseAndPrint(string _source, string _objectPath) @@ -303,7 +323,7 @@ public: void runSteps(string _source, string _objectPath, string _steps) { parse(_source, _objectPath); - disambiguate(); + runCodeDisambiguator(); runSequence(_steps); } @@ -317,7 +337,7 @@ public: while (true) { - disambiguated = disambiguated || (disambiguate(), true); + disambiguated = disambiguated || (runCodeDisambiguator(), true); map const& extraOptions = { // QUIT starts with a non-letter character on purpose to get it to show up on top of the list @@ -345,16 +365,14 @@ public: disambiguated = false; break; case ';': - { runStackCompressor(); break; - } default: - runSequence(std::string_view(&option, 1)); + runSequence(string_view(&option, 1)); } resetNameDispenser(); - analyze(errorReporter); + runCodeAnalyzer(errorReporter); } catch (...) { From 8b99ac4054b4f5e4692250f42e981f77a5801212 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Mon, 7 Nov 2022 20:20:52 +0200 Subject: [PATCH 09/23] fix: apply suggestions + small fix --- test/tools/yulopti.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 57a473ce6..dfff6cb7c 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -98,12 +98,12 @@ public: "Assembly object not found." ); - const auto subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); - const auto subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); + string const subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); + string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); auto subObjectIt = ranges::find_if( _object->subObjects, - [subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } + [&subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } ); yulAssert( @@ -136,18 +136,16 @@ public: if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) m_inputWasCodeBlock = true; - auto content = parser.parse(scanner, false); + shared_ptr object = parser.parse(scanner, false); - if (content != nullptr) - m_object = getSubObject(content, _objectPath); - - if (!m_object || !errorReporter.errors().empty()) + if (!object || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; printErrors(charStream, errors); throw runtime_error("Could not parse source."); } + m_object = getSubObject(object, _objectPath); runCodeAnalyzer(errorReporter); } catch(...) @@ -207,21 +205,21 @@ public: } } - void objectApplyFunction(shared_ptr _object, function _fn) + void applyFunctionToObject(shared_ptr _object, function _function) { for (auto const& subObjectNode: _object->subObjects) { auto subObject = dynamic_pointer_cast(subObjectNode); if (subObject != nullptr) - objectApplyFunction(subObject, _fn); + applyFunctionToObject(subObject, _function); } - _fn(*_object); + _function(*_object); } void runCodeAnalyzer(ErrorReporter& _errorReporter) { - objectApplyFunction( + applyFunctionToObject( m_object, [&](Object& _object) { @@ -243,7 +241,7 @@ public: void runCodeDisambiguator() { - objectApplyFunction( + applyFunctionToObject( m_object, [&](Object& _object) { @@ -258,7 +256,7 @@ public: void runSequence(string_view _steps) { - objectApplyFunction( + applyFunctionToObject( m_object, [&](Object& _object) { @@ -269,7 +267,7 @@ public: void runVarNameCleaner() { - objectApplyFunction( + applyFunctionToObject( m_object, [&](Object& _object) { @@ -280,7 +278,7 @@ public: void runStackCompressor() { - objectApplyFunction( + applyFunctionToObject( m_object, [&](Object& _object) { @@ -386,7 +384,7 @@ public: private: shared_ptr m_object; - bool m_inputWasCodeBlock; + bool m_inputWasCodeBlock = false; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; set const m_reservedIdentifiers = {}; From 4095ea50c252644618b13a4b31db8f5d22cc8058 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 26 Nov 2022 19:32:50 +0200 Subject: [PATCH 10/23] process some of the feedback --- test/tools/yulopti.cpp | 91 ++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index dfff6cb7c..34d9696b3 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -88,15 +88,16 @@ public: }.printErrorInformation(_errors); } - shared_ptr getSubObject(shared_ptr const& _object, string const& _qualifiedPath) + /// Recursively searches for an object at @param _qualifiedPath within @param _object. + /// @returns the object at @param qualifiedPath or a nullptr if it was not found. + shared_ptr getObject(shared_ptr const& _object, string const& _qualifiedPath) { if (_qualifiedPath.empty() || _qualifiedPath == _object->name.str()) return _object; - yulAssert( - boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + "."), - "Assembly object not found." - ); + if (!boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + ".")) { + return nullptr; + } string const subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); @@ -106,19 +107,18 @@ public: [&subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } ); - yulAssert( - subObjectIt != _object->subObjects.end(), - "Assembly object not found." - ); + if (subObjectIt == _object->subObjects.end()) { + return nullptr; + } auto subObject = dynamic_pointer_cast(*subObjectIt); yulAssert( subObject != nullptr, - "Assembly object may not contain code." + "Assembly object does not contain code." ); - return getSubObject(subObject, subObjectPath); + return getObject(subObject, subObjectPath); } void parse(string const& _input, string const& _objectPath) @@ -133,8 +133,8 @@ public: auto scanner = make_shared(charStream); - if (!m_inputWasCodeBlock && scanner->currentToken() == Token::LBrace) - m_inputWasCodeBlock = true; + if (!m_inputIsCodeBlock && scanner->currentToken() == Token::LBrace) + m_inputIsCodeBlock = true; shared_ptr object = parser.parse(scanner, false); @@ -145,7 +145,12 @@ public: throw runtime_error("Could not parse source."); } - m_object = getSubObject(object, _objectPath); + m_object = getObject(object, _objectPath); + + if (m_object == nullptr) { + throw runtime_error("Assembly object not found."); + } + runCodeAnalyzer(errorReporter); } catch(...) @@ -205,22 +210,22 @@ public: } } - void applyFunctionToObject(shared_ptr _object, function _function) + void applyFunctionToObjectAndSubobjects(Object& _object, function _function) { - for (auto const& subObjectNode: _object->subObjects) { + for (auto const& subObjectNode: _object.subObjects) { auto subObject = dynamic_pointer_cast(subObjectNode); if (subObject != nullptr) - applyFunctionToObject(subObject, _function); + applyFunctionToObjectAndSubobjects(*subObject, _function); } - _function(*_object); + _function(_object); } void runCodeAnalyzer(ErrorReporter& _errorReporter) { - applyFunctionToObject( - m_object, + applyFunctionToObjectAndSubobjects( + *m_object, [&](Object& _object) { _object.analysisInfo = make_shared(); @@ -241,8 +246,8 @@ public: void runCodeDisambiguator() { - applyFunctionToObject( - m_object, + applyFunctionToObjectAndSubobjects( + *m_object, [&](Object& _object) { _object.code = make_shared( @@ -256,8 +261,8 @@ public: void runSequence(string_view _steps) { - applyFunctionToObject( - m_object, + applyFunctionToObjectAndSubobjects( + *m_object, [&](Object& _object) { OptimiserSuite{*m_context}.runSequence(_steps, *_object.code); @@ -267,8 +272,8 @@ public: void runVarNameCleaner() { - applyFunctionToObject( - m_object, + applyFunctionToObjectAndSubobjects( + *m_object, [&](Object& _object) { VarNameCleaner::run(*m_context, *_object.code); @@ -278,8 +283,8 @@ public: void runStackCompressor() { - applyFunctionToObject( - m_object, + applyFunctionToObjectAndSubobjects( + *m_object, [&](Object& _object) { StackCompressor::run(m_dialect, _object, true, 16); @@ -287,18 +292,12 @@ public: ); } - void parseAndPrint(string _source, string _objectPath) - { - parse(_source, _objectPath); - printObject(); - } - void printObject() { - if (!m_inputWasCodeBlock) + if (!m_inputIsCodeBlock) cout << m_object->toString(&m_dialect) << endl; else - cout << AsmPrinter{m_dialect}(*m_object->code) << endl; + cout << AsmPrinter{m_dialect}(*m_object->code) << endl; } void resetNameDispenser() @@ -318,7 +317,7 @@ public: ); } - void runSteps(string _source, string _objectPath, string _steps) + void parseAndRunSteps(string const& _source, string const& _objectPath, string const& _steps) { parse(_source, _objectPath); runCodeDisambiguator(); @@ -384,7 +383,7 @@ public: private: shared_ptr m_object; - bool m_inputWasCodeBlock = false; + bool m_inputIsCodeBlock = false; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; set const m_reservedIdentifiers = {}; @@ -410,11 +409,10 @@ int main(int argc, char** argv) po::options_description options( R"(yulopti, yul optimizer exploration tool. Usage: yulopti [Options] - Reads containing a yul object and applies optimizer steps to it, + Reads containing a either a yul object or a yul code block and applies optimizer steps to it, interactively read from stdin. In non-interactive mode a list of steps has to be provided. If is -, yul code is read from stdin and run non-interactively. - An flag may be provided, specifying a dotted path to an object in the input. Allowed options)", po::options_description::m_default_line_length, @@ -432,8 +430,10 @@ int main(int argc, char** argv) ) ( "object", - po::value(), - "path to a yul object in the input" + po::value()->value_name("path"), + "Dotted path to a specific Yul object in the source. " + "If not specified, the top-level object is used. " + "Only valid if the source actually contains objects (rather than a block of Yul code)." ) ( "non-interactive,n", @@ -491,14 +491,17 @@ int main(int argc, char** argv) bool disambiguated = false; if (!nonInteractive) - yulOpti.parseAndPrint(input, objectPath); + { + yulOpti.parse(input, objectPath); + yulOpti.printObject(); + } if (arguments.count("steps")) { string sequence = arguments["steps"].as(); if (!nonInteractive) cout << "----------------------" << endl; - yulOpti.runSteps(input, objectPath, sequence); + yulOpti.parseAndRunSteps(input, objectPath, sequence); disambiguated = true; } if (!nonInteractive) From 9012ade27ff653c96ef2d9e3a846561cac03d827 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 26 Nov 2022 21:49:03 +0200 Subject: [PATCH 11/23] getObject -> Object::objectAt --- libyul/Object.cpp | 26 ++++++++++++++++++++++++++ libyul/Object.h | 8 ++++++++ test/tools/yulopti.cpp | 36 +----------------------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 83ee66a3b..9263e4696 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -33,6 +33,7 @@ #include #include +#include #include using namespace solidity; @@ -173,3 +174,28 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const return path; } + +shared_ptr Object::objectAt(shared_ptr const& _object, string const& _qualifiedPath) +{ + if (_qualifiedPath.empty() || _qualifiedPath == _object->name.str()) + return _object; + + if (!boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + ".")) + return nullptr; + + string const subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); + string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); + + auto subObjectIt = ranges::find_if( + _object->subObjects, + [&subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } + ); + + if (subObjectIt == _object->subObjects.end()) + return nullptr; + + auto subObject = dynamic_pointer_cast(*subObjectIt); + yulAssert(subObject, "Assembly object <" + subObject->name.str() + "> does not contain code."); + + return objectAt(subObject, subObjectPath); +} diff --git a/libyul/Object.h b/libyul/Object.h index 5f88d3bc7..2bbcf1bd7 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -128,6 +129,13 @@ public: /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } + + /// Recursively searches for an Object at @param _qualifiedPath within @param _object. + /// @returns a shared_ptr to the Object or a nullptr if it was not found. + static std::shared_ptr objectAt( + std::shared_ptr const& _object, + std::string const& _qualifiedPath + ); }; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 34d9696b3..55b8b1ca9 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -51,7 +51,6 @@ #include #include -#include #include #include #include @@ -88,39 +87,6 @@ public: }.printErrorInformation(_errors); } - /// Recursively searches for an object at @param _qualifiedPath within @param _object. - /// @returns the object at @param qualifiedPath or a nullptr if it was not found. - shared_ptr getObject(shared_ptr const& _object, string const& _qualifiedPath) - { - if (_qualifiedPath.empty() || _qualifiedPath == _object->name.str()) - return _object; - - if (!boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + ".")) { - return nullptr; - } - - string const subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); - string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); - - auto subObjectIt = ranges::find_if( - _object->subObjects, - [&subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } - ); - - if (subObjectIt == _object->subObjects.end()) { - return nullptr; - } - - auto subObject = dynamic_pointer_cast(*subObjectIt); - - yulAssert( - subObject != nullptr, - "Assembly object does not contain code." - ); - - return getObject(subObject, subObjectPath); - } - void parse(string const& _input, string const& _objectPath) { ErrorList errors; @@ -145,7 +111,7 @@ public: throw runtime_error("Could not parse source."); } - m_object = getObject(object, _objectPath); + m_object = Object::objectAt(object, _objectPath); if (m_object == nullptr) { throw runtime_error("Assembly object not found."); From 284adee620709934a9b4f680ae55fe39e63ef588 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sun, 27 Nov 2022 17:52:52 +0200 Subject: [PATCH 12/23] process some of the feedback --- test/tools/yulopti.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 55b8b1ca9..db39edb07 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -100,8 +100,13 @@ public: auto scanner = make_shared(charStream); if (!m_inputIsCodeBlock && scanner->currentToken() == Token::LBrace) + { m_inputIsCodeBlock = true; + if (!_objectPath.empty()) + throw runtime_error("Object path argument cannot be used. Input is a code block."); + } + shared_ptr object = parser.parse(scanner, false); if (!object || !errorReporter.errors().empty()) @@ -262,8 +267,14 @@ public: { if (!m_inputIsCodeBlock) cout << m_object->toString(&m_dialect) << endl; - else + else { + yulAssert( + m_object->subObjects.empty(), + "Unexpected subObjects found." + ); + cout << AsmPrinter{m_dialect}(*m_object->code) << endl; + } } void resetNameDispenser() From feb4880b5084c4e5e41d3c0e76f605945d24eac7 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Fri, 23 Dec 2022 16:42:02 +0200 Subject: [PATCH 13/23] mostly style changes --- test/tools/yulopti.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index db39edb07..50feb1245 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -104,7 +104,7 @@ public: m_inputIsCodeBlock = true; if (!_objectPath.empty()) - throw runtime_error("Object path argument cannot be used. Input is a code block."); + solThrow(Exception, "Object path argument cannot be used. Input is a code block."); } shared_ptr object = parser.parse(scanner, false); @@ -113,14 +113,13 @@ public: { cerr << "Error parsing source." << endl; printErrors(charStream, errors); - throw runtime_error("Could not parse source."); + solThrow(Exception, "Could not parse source."); } m_object = Object::objectAt(object, _objectPath); - if (m_object == nullptr) { - throw runtime_error("Assembly object not found."); - } + if (m_object == nullptr) + solThrow(Exception, "Assembly object not found."); runCodeAnalyzer(errorReporter); } @@ -183,7 +182,8 @@ public: void applyFunctionToObjectAndSubobjects(Object& _object, function _function) { - for (auto const& subObjectNode: _object.subObjects) { + for (auto const& subObjectNode: _object.subObjects) + { auto subObject = dynamic_pointer_cast(subObjectNode); if (subObject != nullptr) @@ -234,10 +234,7 @@ public: { applyFunctionToObjectAndSubobjects( *m_object, - [&](Object& _object) - { - OptimiserSuite{*m_context}.runSequence(_steps, *_object.code); - } + [&](Object& _object) { OptimiserSuite{*m_context}.runSequence(_steps, *_object.code); } ); } @@ -245,10 +242,7 @@ public: { applyFunctionToObjectAndSubobjects( *m_object, - [&](Object& _object) - { - VarNameCleaner::run(*m_context, *_object.code); - } + [&](Object& _object) { VarNameCleaner::run(*m_context, *_object.code); } ); } @@ -256,10 +250,7 @@ public: { applyFunctionToObjectAndSubobjects( *m_object, - [&](Object& _object) - { - StackCompressor::run(m_dialect, _object, true, 16); - } + [&](Object& _object) { StackCompressor::run(m_dialect, _object, true, 16); } ); } @@ -267,7 +258,8 @@ public: { if (!m_inputIsCodeBlock) cout << m_object->toString(&m_dialect) << endl; - else { + else + { yulAssert( m_object->subObjects.empty(), "Unexpected subObjects found." From 83fec48f54abd097149d4598c86eb07944061a07 Mon Sep 17 00:00:00 2001 From: Daniel Lupu Date: Sat, 24 Dec 2022 00:32:24 +0200 Subject: [PATCH 14/23] set proper execution num for creation objects --- test/tools/yulopti.cpp | 90 ++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 50feb1245..cd0f9caf6 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -87,7 +87,7 @@ public: }.printErrorInformation(_errors); } - void parse(string const& _input, string const& _objectPath) + void parse(string const& _input) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -100,27 +100,17 @@ public: auto scanner = make_shared(charStream); if (!m_inputIsCodeBlock && scanner->currentToken() == Token::LBrace) - { m_inputIsCodeBlock = true; - if (!_objectPath.empty()) - solThrow(Exception, "Object path argument cannot be used. Input is a code block."); - } + m_object = parser.parse(scanner, false); - shared_ptr object = parser.parse(scanner, false); - - if (!object || !errorReporter.errors().empty()) + if (!m_object || !errorReporter.errors().empty()) { cerr << "Error parsing source." << endl; printErrors(charStream, errors); solThrow(Exception, "Could not parse source."); } - m_object = Object::objectAt(object, _objectPath); - - if (m_object == nullptr) - solThrow(Exception, "Assembly object not found."); - runCodeAnalyzer(errorReporter); } catch(...) @@ -234,7 +224,11 @@ public: { applyFunctionToObjectAndSubobjects( *m_object, - [&](Object& _object) { OptimiserSuite{*m_context}.runSequence(_steps, *_object.code); } + [&](Object& _object) + { + OptimiserStepContext context = createOptimiserStepContext(_object); + OptimiserSuite{context}.runSequence(_steps, *_object.code); + } ); } @@ -242,7 +236,11 @@ public: { applyFunctionToObjectAndSubobjects( *m_object, - [&](Object& _object) { VarNameCleaner::run(*m_context, *_object.code); } + [&](Object& _object) + { + OptimiserStepContext context = createOptimiserStepContext(_object); + VarNameCleaner::run(context, *_object.code); + } ); } @@ -254,10 +252,17 @@ public: ); } - void printObject() + void printObject(string const& _objectPath) { if (!m_inputIsCodeBlock) - cout << m_object->toString(&m_dialect) << endl; + { + shared_ptr subObject = Object::objectAt(m_object, _objectPath); + + if (subObject == nullptr) + solThrow(Exception, "Assembly object not found."); + + cout << subObject->toString(&m_dialect) << endl; + } else { yulAssert( @@ -265,6 +270,9 @@ public: "Unexpected subObjects found." ); + if (!_objectPath.empty()) + solThrow(Exception, "Object path argument cannot be used. Input is a code block."); + cout << AsmPrinter{m_dialect}(*m_object->code) << endl; } } @@ -275,31 +283,34 @@ public: m_dialect, m_reservedIdentifiers ); - - m_context = make_shared( - OptimiserStepContext{ - m_dialect, - *m_nameDispenser, - m_reservedIdentifiers, - solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment - } - ); } - void parseAndRunSteps(string const& _source, string const& _objectPath, string const& _steps) + void parseAndRunSteps(string const& _source, string const& _steps) { - parse(_source, _objectPath); + parse(_source); runCodeDisambiguator(); runSequence(_steps); } + OptimiserStepContext createOptimiserStepContext(Object& _object) + { + bool isCreation = m_object.get() == &_object || !boost::ends_with(_object.name.str(), "_deployed"); + + return OptimiserStepContext{ + m_dialect, + *m_nameDispenser, + m_reservedIdentifiers, + isCreation ? nullopt : make_optional(OptimiserSettings::standard().expectedExecutionsPerDeployment) + }; + } + void runInteractive(string _source, string const& _objectPath, bool _disambiguated = false) { ErrorList errors; ErrorReporter errorReporter(errors); bool disambiguated = _disambiguated; - parse(_source, _objectPath); + parse(_source); while (true) { @@ -346,7 +357,7 @@ public: cerr << boost::current_exception_diagnostic_information() << endl; } cout << "----------------------" << endl; - printObject(); + printObject(_objectPath); } } @@ -360,14 +371,6 @@ private: m_dialect, m_reservedIdentifiers ); - shared_ptr m_context = make_shared( - OptimiserStepContext{ - m_dialect, - *m_nameDispenser, - m_reservedIdentifiers, - solidity::frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment - } - ); }; int main(int argc, char** argv) @@ -400,8 +403,9 @@ int main(int argc, char** argv) ( "object", po::value()->value_name("path"), - "Dotted path to a specific Yul object in the source. " - "If not specified, the top-level object is used. " + "Dotted path to a specific Yul object in the source to print after running the steps. " + "If not specified, the top-level object will be selected. " + "The object will be printed recursively. " "Only valid if the source actually contains objects (rather than a block of Yul code)." ) ( @@ -461,8 +465,8 @@ int main(int argc, char** argv) if (!nonInteractive) { - yulOpti.parse(input, objectPath); - yulOpti.printObject(); + yulOpti.parse(input); + yulOpti.printObject(objectPath); } if (arguments.count("steps")) @@ -470,7 +474,7 @@ int main(int argc, char** argv) string sequence = arguments["steps"].as(); if (!nonInteractive) cout << "----------------------" << endl; - yulOpti.parseAndRunSteps(input, objectPath, sequence); + yulOpti.parseAndRunSteps(input, sequence); disambiguated = true; } if (!nonInteractive) From ce6abc6e4246c0bf24eaf21d8986ea85a1872c20 Mon Sep 17 00:00:00 2001 From: r0qs Date: Thu, 3 Aug 2023 20:27:41 +0200 Subject: [PATCH 15/23] Rename _qualifiedPath to _qualifiedName --- libyul/Object.cpp | 8 ++++---- libyul/Object.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 9263e4696..5c03c3b8c 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -175,15 +175,15 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const return path; } -shared_ptr Object::objectAt(shared_ptr const& _object, string const& _qualifiedPath) +shared_ptr Object::objectAt(shared_ptr const& _object, string const& _qualifiedName) { - if (_qualifiedPath.empty() || _qualifiedPath == _object->name.str()) + if (_qualifiedName.empty() || _qualifiedName == _object->name.str()) return _object; - if (!boost::algorithm::starts_with(_qualifiedPath, _object->name.str() + ".")) + if (!boost::algorithm::starts_with(_qualifiedName, _object->name.str() + ".")) return nullptr; - string const subObjectPath = _qualifiedPath.substr(_object->name.str().length() + 1); + string const subObjectPath = _qualifiedName.substr(_object->name.str().length() + 1); string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); auto subObjectIt = ranges::find_if( diff --git a/libyul/Object.h b/libyul/Object.h index 2bbcf1bd7..fc824a785 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -130,11 +130,11 @@ public: /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } - /// Recursively searches for an Object at @param _qualifiedPath within @param _object. + /// Recursively searches for an Object at @param _qualifiedName within @param _object. /// @returns a shared_ptr to the Object or a nullptr if it was not found. static std::shared_ptr objectAt( std::shared_ptr const& _object, - std::string const& _qualifiedPath + std::string const& _qualifiedName ); }; From e61843428a77228f397937850c1054e4b167c1f5 Mon Sep 17 00:00:00 2001 From: r0qs Date: Fri, 4 Aug 2023 12:27:27 +0200 Subject: [PATCH 16/23] Apply suggestions --- test/tools/yulopti.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index cd0f9caf6..f4be2f53b 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -98,10 +98,7 @@ public: ObjectParser parser(errorReporter, m_dialect); auto scanner = make_shared(charStream); - - if (!m_inputIsCodeBlock && scanner->currentToken() == Token::LBrace) - m_inputIsCodeBlock = true; - + m_inputIsCodeBlock = (scanner->currentToken() == Token::LBrace); m_object = parser.parse(scanner, false); if (!m_object || !errorReporter.errors().empty()) @@ -349,7 +346,6 @@ public: } resetNameDispenser(); - runCodeAnalyzer(errorReporter); } catch (...) { @@ -381,7 +377,7 @@ int main(int argc, char** argv) po::options_description options( R"(yulopti, yul optimizer exploration tool. Usage: yulopti [Options] - Reads containing a either a yul object or a yul code block and applies optimizer steps to it, + Reads containing either a yul object or a yul code block and applies optimizer steps to it, interactively read from stdin. In non-interactive mode a list of steps has to be provided. If is -, yul code is read from stdin and run non-interactively. From 3b2805b7609cb078644fdbbec432bcbaf0ba56e1 Mon Sep 17 00:00:00 2001 From: r0qs Date: Thu, 10 Aug 2023 21:32:31 +0200 Subject: [PATCH 17/23] Traverse object tree --- libyul/Object.cpp | 83 ++++++++++++++++++++++++----------------------- libyul/Object.h | 10 +++++- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 5c03c3b8c..247a90a5b 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -144,8 +144,36 @@ std::set Object::qualifiedDataNames() const return qualifiedNames; } +void Object::traverseObjectTree(Object const* _object, YulString _qualifiedName, std::function _visitor) +{ + yulAssert(!_qualifiedName.empty(), ""); + + std::vector subObjectPathComponents; + boost::algorithm::split(subObjectPathComponents, _qualifiedName.str(), boost::is_any_of(".")); + + for (std::string const& currentSubObjectName: subObjectPathComponents) + { + yulAssert(!currentSubObjectName.empty(), ""); + + auto subObjectIt = ranges::find_if( + _object->subObjects, + [¤tSubObjectName](auto const& _subObject) { return _subObject->name.str() == currentSubObjectName; } + ); + + if (subObjectIt != _object->subObjects.end()) + { + _object = dynamic_cast(subObjectIt->get()); + yulAssert(_object, "Assembly object <" + _object->name.str() + "> not found or does not contain code."); + yulAssert(_object->subId != std::numeric_limits::max(), ""); + if (_visitor(_object)) + break; + } + } +} + std::vector Object::pathToSubObject(YulString _qualifiedName) const { + std::vector path; yulAssert(_qualifiedName != name, ""); yulAssert(subIndexByName.count(name) == 0, ""); @@ -153,49 +181,24 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const _qualifiedName = YulString{_qualifiedName.str().substr(name.str().length() + 1)}; yulAssert(!_qualifiedName.empty(), ""); - std::vector subObjectPathComponents; - boost::algorithm::split(subObjectPathComponents, _qualifiedName.str(), boost::is_any_of(".")); - - std::vector path; - Object const* object = this; - for (std::string const& currentSubObjectName: subObjectPathComponents) - { - yulAssert(!currentSubObjectName.empty(), ""); - auto subIndexIt = object->subIndexByName.find(YulString{currentSubObjectName}); - yulAssert( - subIndexIt != object->subIndexByName.end(), - "Assembly object <" + _qualifiedName.str() + "> not found or does not contain code." - ); - object = dynamic_cast(object->subObjects[subIndexIt->second].get()); - yulAssert(object, "Assembly object <" + _qualifiedName.str() + "> not found or does not contain code."); - yulAssert(object->subId != std::numeric_limits::max(), ""); - path.push_back({object->subId}); - } - + traverseObjectTree(this, _qualifiedName, [&](Object const* _object) -> bool { + path.push_back({_object->subId}); + return false; + }); return path; } -shared_ptr Object::objectAt(shared_ptr const& _object, string const& _qualifiedName) +std::shared_ptr Object::objectAt(std::shared_ptr const& _object, std::string const& _qualifiedName) { - if (_qualifiedName.empty() || _qualifiedName == _object->name.str()) - return _object; + std::shared_ptr object = nullptr; + traverseObjectTree(_object.get(), YulString(_qualifiedName), [&](Object const* _obj) -> bool { + if (_qualifiedName.empty() || _qualifiedName == _obj->name.str()) + { + object = std::make_shared(*_obj); + return true; + } + return false; + }); - if (!boost::algorithm::starts_with(_qualifiedName, _object->name.str() + ".")) - return nullptr; - - string const subObjectPath = _qualifiedName.substr(_object->name.str().length() + 1); - string const subObjectName = subObjectPath.substr(0, subObjectPath.find_first_of('.')); - - auto subObjectIt = ranges::find_if( - _object->subObjects, - [&subObjectName](auto const& _subObject) { return _subObject->name.str() == subObjectName; } - ); - - if (subObjectIt == _object->subObjects.end()) - return nullptr; - - auto subObject = dynamic_pointer_cast(*subObjectIt); - yulAssert(subObject, "Assembly object <" + subObject->name.str() + "> does not contain code."); - - return objectAt(subObject, subObjectPath); + return object; } diff --git a/libyul/Object.h b/libyul/Object.h index fc824a785..d37d6c70b 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -117,6 +117,14 @@ public: /// The path must not lead to a @a Data object (will throw in that case). std::vector pathToSubObject(YulString _qualifiedName) const; + /// Traverses the @a _object tree applying the function @a _visitor at every subobject + /// in the path given by the @a _qualifiedName. + static void traverseObjectTree( + Object const* _object, + YulString _qualifiedName, + std::function _visitor + ); + /// sub id for object if it is subobject of another object, max value if it is not subobject size_t subId = std::numeric_limits::max(); @@ -130,7 +138,7 @@ public: /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } - /// Recursively searches for an Object at @param _qualifiedName within @param _object. + /// Searches for an Object at @param _qualifiedName within @param _object. /// @returns a shared_ptr to the Object or a nullptr if it was not found. static std::shared_ptr objectAt( std::shared_ptr const& _object, From 4d8d6fe44759c65565492378a028e2591ce7677d Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 28 Aug 2023 11:57:07 +0200 Subject: [PATCH 18/23] Rename traverseObjectTree to visitPath and make it a member function --- libyul/Object.cpp | 27 ++++++++++++++------------- libyul/Object.h | 11 ++++++----- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 247a90a5b..0ea9009fb 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -144,28 +144,29 @@ std::set Object::qualifiedDataNames() const return qualifiedNames; } -void Object::traverseObjectTree(Object const* _object, YulString _qualifiedName, std::function _visitor) +void Object::visitPath(YulString _qualifiedName, std::function _visitor) const { yulAssert(!_qualifiedName.empty(), ""); std::vector subObjectPathComponents; boost::algorithm::split(subObjectPathComponents, _qualifiedName.str(), boost::is_any_of(".")); + Object const* object = this; for (std::string const& currentSubObjectName: subObjectPathComponents) { yulAssert(!currentSubObjectName.empty(), ""); auto subObjectIt = ranges::find_if( - _object->subObjects, + object->subObjects, [¤tSubObjectName](auto const& _subObject) { return _subObject->name.str() == currentSubObjectName; } ); - if (subObjectIt != _object->subObjects.end()) + if (subObjectIt != object->subObjects.end()) { - _object = dynamic_cast(subObjectIt->get()); - yulAssert(_object, "Assembly object <" + _object->name.str() + "> not found or does not contain code."); - yulAssert(_object->subId != std::numeric_limits::max(), ""); - if (_visitor(_object)) + object = dynamic_cast(subObjectIt->get()); + yulAssert(object, "Assembly object <" + object->name.str() + "> not found or does not contain code."); + yulAssert(object->subId != std::numeric_limits::max(), ""); + if (_visitor(object)) break; } } @@ -181,7 +182,7 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const _qualifiedName = YulString{_qualifiedName.str().substr(name.str().length() + 1)}; yulAssert(!_qualifiedName.empty(), ""); - traverseObjectTree(this, _qualifiedName, [&](Object const* _object) -> bool { + this->visitPath(_qualifiedName, [&](Object const* _object) -> bool { path.push_back({_object->subId}); return false; }); @@ -190,15 +191,15 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const std::shared_ptr Object::objectAt(std::shared_ptr const& _object, std::string const& _qualifiedName) { - std::shared_ptr object = nullptr; - traverseObjectTree(_object.get(), YulString(_qualifiedName), [&](Object const* _obj) -> bool { - if (_qualifiedName.empty() || _qualifiedName == _obj->name.str()) + std::shared_ptr foundObject = nullptr; + _object->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { + if (_qualifiedName.empty() || _qualifiedName == _subObject->name.str()) { - object = std::make_shared(*_obj); + foundObject = std::make_shared(*_subObject); return true; } return false; }); - return object; + return foundObject; } diff --git a/libyul/Object.h b/libyul/Object.h index d37d6c70b..37d802527 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -117,13 +117,14 @@ public: /// The path must not lead to a @a Data object (will throw in that case). std::vector pathToSubObject(YulString _qualifiedName) const; - /// Traverses the @a _object tree applying the function @a _visitor at every subobject - /// in the path given by the @a _qualifiedName. - static void traverseObjectTree( - Object const* _object, + /// Visits all subobjects in the path given by the @a _qualifiedName + /// of the current object applying the function @a _visitor. + /// If @a _visitor returns `true` the visiting stops, otherwise, + /// it continues until the last subobject in the path is reached. + void visitPath( YulString _qualifiedName, std::function _visitor - ); + ) const; /// sub id for object if it is subobject of another object, max value if it is not subobject size_t subId = std::numeric_limits::max(); From 61162deea76d9e32bde00b6f5d0a399cc6997413 Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 28 Aug 2023 13:46:25 +0200 Subject: [PATCH 19/23] Fix subObjectAt --- libyul/Object.cpp | 29 ++++++++++++++++++++++------- libyul/Object.h | 11 ++++------- test/tools/yulopti.cpp | 2 +- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 0ea9009fb..e871b70db 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -164,10 +164,12 @@ void Object::visitPath(YulString _qualifiedName, std::functionsubObjects.end()) { object = dynamic_cast(subObjectIt->get()); - yulAssert(object, "Assembly object <" + object->name.str() + "> not found or does not contain code."); - yulAssert(object->subId != std::numeric_limits::max(), ""); - if (_visitor(object)) - break; + if (object) + { + yulAssert(object, "Assembly object <" + object->name.str() + "> not found or does not contain code."); + if (_visitor(object)) + break; + } } } } @@ -183,17 +185,30 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const yulAssert(!_qualifiedName.empty(), ""); this->visitPath(_qualifiedName, [&](Object const* _object) -> bool { + yulAssert(_object->subId != std::numeric_limits::max(), ""); path.push_back({_object->subId}); return false; }); return path; } -std::shared_ptr Object::objectAt(std::shared_ptr const& _object, std::string const& _qualifiedName) +std::shared_ptr Object::subObjectAt(std::string const& _qualifiedName) { + + if (_qualifiedName.empty()) + return nullptr; + + // If there is no `.` in the given `_qualifiedName`, the target + // object name is considered to be the `_qualifiedName`, otherwise, + // the target object name is the last element in the path given by `_qualifiedName`. + std::string targetObjectName = _qualifiedName; + size_t targetObjectPos = _qualifiedName.find_last_of("."); + if (targetObjectPos != std::string::npos) + targetObjectName = _qualifiedName.substr(targetObjectPos + 1); + std::shared_ptr foundObject = nullptr; - _object->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { - if (_qualifiedName.empty() || _qualifiedName == _subObject->name.str()) + this->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { + if (targetObjectName == _subObject->name.str()) { foundObject = std::make_shared(*_subObject); return true; diff --git a/libyul/Object.h b/libyul/Object.h index 37d802527..a095ec599 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -117,6 +117,10 @@ public: /// The path must not lead to a @a Data object (will throw in that case). std::vector pathToSubObject(YulString _qualifiedName) const; + /// Searches for a subobject at @param _qualifiedName within the current object. + /// @returns a shared_ptr to the subobject or a nullptr if it was not found. + std::shared_ptr subObjectAt(std::string const& _qualifiedName); + /// Visits all subobjects in the path given by the @a _qualifiedName /// of the current object applying the function @a _visitor. /// If @a _visitor returns `true` the visiting stops, otherwise, @@ -138,13 +142,6 @@ public: /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } - - /// Searches for an Object at @param _qualifiedName within @param _object. - /// @returns a shared_ptr to the Object or a nullptr if it was not found. - static std::shared_ptr objectAt( - std::shared_ptr const& _object, - std::string const& _qualifiedName - ); }; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index f4be2f53b..c783a5e9f 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -253,7 +253,7 @@ public: { if (!m_inputIsCodeBlock) { - shared_ptr subObject = Object::objectAt(m_object, _objectPath); + shared_ptr subObject = m_object->subObjectAt(_objectPath); if (subObject == nullptr) solThrow(Exception, "Assembly object not found."); From 98b5b6c916f77c684c68fd6f43698a3f46301a7f Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 28 Aug 2023 14:06:06 +0200 Subject: [PATCH 20/23] Avoid creating copy of the object --- libyul/Object.cpp | 7 +++---- libyul/Object.h | 4 ++-- test/tools/yulopti.cpp | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index e871b70db..1088f21c7 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -192,9 +192,8 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const return path; } -std::shared_ptr Object::subObjectAt(std::string const& _qualifiedName) +Object const* Object::subObjectAt(std::string const& _qualifiedName) { - if (_qualifiedName.empty()) return nullptr; @@ -206,11 +205,11 @@ std::shared_ptr Object::subObjectAt(std::string const& _qualifiedName) if (targetObjectPos != std::string::npos) targetObjectName = _qualifiedName.substr(targetObjectPos + 1); - std::shared_ptr foundObject = nullptr; + Object const* foundObject = nullptr; this->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { if (targetObjectName == _subObject->name.str()) { - foundObject = std::make_shared(*_subObject); + foundObject = _subObject; return true; } return false; diff --git a/libyul/Object.h b/libyul/Object.h index a095ec599..2b92e60bb 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -118,8 +118,8 @@ public: std::vector pathToSubObject(YulString _qualifiedName) const; /// Searches for a subobject at @param _qualifiedName within the current object. - /// @returns a shared_ptr to the subobject or a nullptr if it was not found. - std::shared_ptr subObjectAt(std::string const& _qualifiedName); + /// @returns a pointer to the subobject or a nullptr if it was not found. + Object const* subObjectAt(std::string const& _qualifiedName); /// Visits all subobjects in the path given by the @a _qualifiedName /// of the current object applying the function @a _visitor. diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index c783a5e9f..b8bba823d 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -253,7 +253,7 @@ public: { if (!m_inputIsCodeBlock) { - shared_ptr subObject = m_object->subObjectAt(_objectPath); + Object const* subObject = m_object->subObjectAt(_objectPath); if (subObject == nullptr) solThrow(Exception, "Assembly object not found."); From 1ec239c63f7536005c84d16560b9a1166c2379a4 Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 28 Aug 2023 16:04:39 +0200 Subject: [PATCH 21/23] Restore efficient subobject search --- libyul/Object.cpp | 28 +++++++++++++--------------- libyul/Object.h | 2 ++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 1088f21c7..2c6ed0329 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -33,7 +33,6 @@ #include #include -#include #include using namespace solidity; @@ -53,6 +52,12 @@ std::string indent(std::string const& _input) } +Object const* Object::at(YulString _name) const +{ + size_t subIndex = this->subIndexByName.at(_name); + return dynamic_cast(this->subObjects[subIndex].get()); +} + std::string Data::toString(Dialect const*, DebugInfoSelection const&, CharStreamProvider const*) const { return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\""; @@ -156,21 +161,14 @@ void Object::visitPath(YulString _qualifiedName, std::functionsubObjects, - [¤tSubObjectName](auto const& _subObject) { return _subObject->name.str() == currentSubObjectName; } + YulString subObjectName = YulString{currentSubObjectName}; + yulAssert( + object->subIndexByName.count(subObjectName), + "Assembly object not found or does not contain code." ); - - if (subObjectIt != object->subObjects.end()) - { - object = dynamic_cast(subObjectIt->get()); - if (object) - { - yulAssert(object, "Assembly object <" + object->name.str() + "> not found or does not contain code."); - if (_visitor(object)) - break; - } - } + object = object->at(subObjectName); + if (object && _visitor(object)) + break; } } diff --git a/libyul/Object.h b/libyul/Object.h index 2b92e60bb..ded9953ad 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -130,6 +130,8 @@ public: std::function _visitor ) const; + Object const* at(YulString _name) const; + /// sub id for object if it is subobject of another object, max value if it is not subobject size_t subId = std::numeric_limits::max(); From 84d3af4a29ba425404a4a2017f25c93d53f2bc12 Mon Sep 17 00:00:00 2001 From: r0qs Date: Fri, 22 Sep 2023 14:17:09 +0200 Subject: [PATCH 22/23] Apply suggestions --- libyul/Object.cpp | 14 ++++++-------- libyul/Object.h | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 2c6ed0329..032e828bd 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -149,7 +149,7 @@ std::set Object::qualifiedDataNames() const return qualifiedNames; } -void Object::visitPath(YulString _qualifiedName, std::function _visitor) const +void Object::visitPath(YulString _qualifiedName, std::function const& _visitor) const { yulAssert(!_qualifiedName.empty(), ""); @@ -184,7 +184,7 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const this->visitPath(_qualifiedName, [&](Object const* _object) -> bool { yulAssert(_object->subId != std::numeric_limits::max(), ""); - path.push_back({_object->subId}); + path.push_back(_object->subId); return false; }); return path; @@ -205,12 +205,10 @@ Object const* Object::subObjectAt(std::string const& _qualifiedName) Object const* foundObject = nullptr; this->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { - if (targetObjectName == _subObject->name.str()) - { - foundObject = _subObject; - return true; - } - return false; + if (targetObjectName != _subObject->name.str()) + return false; + foundObject = _subObject; + return true; }); return foundObject; diff --git a/libyul/Object.h b/libyul/Object.h index ded9953ad..c9fd65826 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -127,7 +127,7 @@ public: /// it continues until the last subobject in the path is reached. void visitPath( YulString _qualifiedName, - std::function _visitor + std::function const& _visitor ) const; Object const* at(YulString _name) const; From b5d4e1d6855d67f155792a0f22e6ca4156c4cd09 Mon Sep 17 00:00:00 2001 From: r0qs Date: Fri, 22 Sep 2023 14:29:14 +0200 Subject: [PATCH 23/23] Pass YulString instead of std::string to Object::subObjectAt --- libyul/Object.cpp | 12 ++++++------ libyul/Object.h | 2 +- test/tools/yulopti.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 032e828bd..12b964e80 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -190,7 +190,7 @@ std::vector Object::pathToSubObject(YulString _qualifiedName) const return path; } -Object const* Object::subObjectAt(std::string const& _qualifiedName) +Object const* Object::subObjectAt(YulString _qualifiedName) { if (_qualifiedName.empty()) return nullptr; @@ -198,14 +198,14 @@ Object const* Object::subObjectAt(std::string const& _qualifiedName) // If there is no `.` in the given `_qualifiedName`, the target // object name is considered to be the `_qualifiedName`, otherwise, // the target object name is the last element in the path given by `_qualifiedName`. - std::string targetObjectName = _qualifiedName; - size_t targetObjectPos = _qualifiedName.find_last_of("."); + YulString targetObjectName = _qualifiedName; + size_t targetObjectPos = _qualifiedName.str().find_last_of("."); if (targetObjectPos != std::string::npos) - targetObjectName = _qualifiedName.substr(targetObjectPos + 1); + targetObjectName = YulString(_qualifiedName.str().substr(targetObjectPos + 1)); Object const* foundObject = nullptr; - this->visitPath(YulString(_qualifiedName), [&](Object const* _subObject) -> bool { - if (targetObjectName != _subObject->name.str()) + this->visitPath(_qualifiedName, [&](Object const* _subObject) -> bool { + if (targetObjectName != _subObject->name) return false; foundObject = _subObject; return true; diff --git a/libyul/Object.h b/libyul/Object.h index c9fd65826..4a97870a5 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -119,7 +119,7 @@ public: /// Searches for a subobject at @param _qualifiedName within the current object. /// @returns a pointer to the subobject or a nullptr if it was not found. - Object const* subObjectAt(std::string const& _qualifiedName); + Object const* subObjectAt(YulString _qualifiedName); /// Visits all subobjects in the path given by the @a _qualifiedName /// of the current object applying the function @a _visitor. diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index b8bba823d..300d9d940 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -253,7 +253,7 @@ public: { if (!m_inputIsCodeBlock) { - Object const* subObject = m_object->subObjectAt(_objectPath); + Object const* subObject = m_object->subObjectAt(YulString(_objectPath)); if (subObject == nullptr) solThrow(Exception, "Assembly object not found.");