From e016cb99e679c53708ca7294442e04f80df477cc Mon Sep 17 00:00:00 2001 From: chriseth Date: Sun, 4 Nov 2018 09:34:21 +0100 Subject: Yul objects. --- libsolidity/interface/AssemblyStack.cpp | 14 +-- libsolidity/interface/AssemblyStack.h | 10 +-- libyul/CMakeLists.txt | 2 + libyul/Object.cpp | 61 +++++++++++++ libyul/Object.h | 72 ++++++++++++++++ libyul/ObjectParser.cpp | 146 ++++++++++++++++++++++++++++++++ libyul/ObjectParser.h | 72 ++++++++++++++++ 7 files changed, 359 insertions(+), 18 deletions(-) create mode 100644 libyul/Object.cpp create mode 100644 libyul/Object.h create mode 100644 libyul/ObjectParser.cpp create mode 100644 libyul/ObjectParser.h diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 31959d93..ef4e32b0 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -69,7 +70,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared(CharStream(_source), _sourceName); - m_parserResult = yul::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); + m_parserResult = yul::YulObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); @@ -77,17 +78,6 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string return analyzeParsed(); } -bool AssemblyStack::analyze(yul::Block const& _block, Scanner const* _scanner) -{ - m_errors.clear(); - m_analysisSuccessful = false; - if (_scanner) - m_scanner = make_shared(*_scanner); - m_parserResult = make_shared(_block); - - return analyzeParsed(); -} - bool AssemblyStack::analyzeParsed() { m_analysisInfo = make_shared(); diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 03b811db..b89faca0 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -24,6 +24,9 @@ #include #include +#include +#include + #include #include @@ -72,10 +75,6 @@ public: /// Multiple calls overwrite the previous state. bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); - /// Runs analysis step on the supplied block, returns false if input cannot be assembled. - /// Multiple calls overwrite the previous state. - bool analyze(yul::Block const& _block, langutil::Scanner const* _scanner = nullptr); - /// Run the assembly step (should only be called after parseAndAnalyze). MachineAssemblyObject assemble(Machine _machine) const; @@ -94,8 +93,7 @@ private: std::shared_ptr m_scanner; bool m_analysisSuccessful = false; - std::shared_ptr m_parserResult; - std::shared_ptr m_analysisInfo; + std::shared_ptr m_parserResult; langutil::ErrorList m_errors; langutil::ErrorReporter m_errorReporter; }; diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 8fbea689..7ed84ff5 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -6,6 +6,8 @@ add_library(yul AsmPrinter.cpp AsmScope.cpp AsmScopeFiller.cpp + Object.cpp + ObjectParser.cpp backends/evm/EVMAssembly.cpp backends/evm/EVMCodeTransform.cpp optimiser/ASTCopier.cpp diff --git a/libyul/Object.cpp b/libyul/Object.cpp new file mode 100644 index 00000000..a5228793 --- /dev/null +++ b/libyul/Object.cpp @@ -0,0 +1,61 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Yul code and data object container. + */ + +#include + +#include +#include + +#include +#include + +#include + +using namespace dev; +using namespace yul; +using namespace std; + +namespace +{ + +string indent(std::string const& _input) +{ + if (_input.empty()) + return _input; + return boost::replace_all_copy(" " + _input, "\n", "\n "); +} + +} + +string Data::toString(bool) const +{ + return "data \"" + name.str() + "\" hex\"" + dev::toHex(data) + "\""; +} + +string Object::toString(bool _yul) const +{ + yulAssert(code, "No code"); + string inner = "code " + AsmPrinter{_yul}(*code); + + for (auto const& obj: subObjects) + inner += "\n" + obj->toString(_yul); + + return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; +} diff --git a/libyul/Object.h b/libyul/Object.h new file mode 100644 index 00000000..cfd8d02d --- /dev/null +++ b/libyul/Object.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Yul code and data object container. + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace yul +{ +struct AsmAnalysisInfo; + + +/** + * Generic base class for both Yul objects and Yul data. + */ +struct ObjectNode +{ + virtual ~ObjectNode() {} + virtual std::string toString(bool _yul) const = 0; + + YulString name; +}; + +/** + * Named data in Yul objects. + */ +struct Data: ObjectNode +{ + Data(YulString _name, dev::bytes _data): data(std::move(_data)) { name = _name; } + std::string toString(bool _yul) const override; + + dev::bytes data; +}; + +/** + * Yul code and data object container. + */ +struct Object: ObjectNode +{ +public: + /// @returns a (parseable) string representation. Includes types if @a _yul is set. + std::string toString(bool _yul) const override; + + std::shared_ptr code; + std::vector> subObjects; + std::map subIndexByName; + std::shared_ptr analysisInfo; +}; + +} diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp new file mode 100644 index 00000000..43dd4be9 --- /dev/null +++ b/libyul/ObjectParser.cpp @@ -0,0 +1,146 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Parser for Yul code and data object container. + */ + +#include + +#include +#include + +#include + +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace std; + + +shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool _reuseScanner) +{ + m_recursionDepth = 0; + try + { + shared_ptr object; + m_scanner = _scanner; + if (currentToken() == Token::LBrace) + { + // Special case: Code-only form. + object = make_shared(); + object->name = YulString{"object"}; + object->code = parseBlock(); + if (!object->code) + return nullptr; + } + else + object = parseObject(); + if (object && !_reuseScanner) + expectToken(Token::EOS); + return object; + } + catch (FatalError const&) + { + if (m_errorReporter.errors().empty()) + throw; // Something is weird here, rather throw again. + } + return nullptr; +} + +shared_ptr ObjectParser::parseObject(Object* _containingObject) +{ + RecursionGuard guard(*this); + + if (currentToken() != Token::Identifier || currentLiteral() != "object") + fatalParserError("Expected keyword \"object\"."); + advance(); + + shared_ptr ret = make_shared(); + ret->name = parseUniqueName(_containingObject); + + expectToken(Token::LBrace); + + ret->code = parseCode(); + + while (currentToken() != Token::RBrace) + { + if (currentToken() == Token::Identifier && currentLiteral() == "object") + parseObject(ret.get()); + else if (currentToken() == Token::Identifier && currentLiteral() == "data") + parseData(*ret); + else + fatalParserError("Expected keyword \"data\" or \"object\" or \"}\"."); + } + if (_containingObject) + addNamedSubObject(*_containingObject, ret->name, ret); + + expectToken(Token::RBrace); + + return ret; +} + +shared_ptr ObjectParser::parseCode() +{ + if (currentToken() != Token::Identifier || currentLiteral() != "code") + fatalParserError("Expected keyword \"code\"."); + advance(); + + return parseBlock(); +} + +shared_ptr ObjectParser::parseBlock() +{ + Parser parser(m_errorReporter, m_flavour); + shared_ptr block = parser.parse(m_scanner, true); + yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); + return block; +} + +void ObjectParser::parseData(Object& _containingObject) +{ + solAssert( + currentToken() == Token::Identifier && currentLiteral() == "data", + "parseData called on wrong input." + ); + advance(); + + YulString name = parseUniqueName(&_containingObject); + + expectToken(Token::StringLiteral, false); + addNamedSubObject(_containingObject, name, make_shared(name, asBytes(currentLiteral()))); + advance(); +} + +YulString ObjectParser::parseUniqueName(Object const* _containingObject) +{ + expectToken(Token::StringLiteral, false); + YulString name{currentLiteral()}; + if (name.empty()) + parserError("Object name cannot be empty."); + else if (_containingObject && _containingObject->name == name) + parserError("Object name cannot be the same as the name of the containing object."); + else if (_containingObject && _containingObject->subIndexByName.count(name)) + parserError("Object name \"" + name.str() + "\" already exists inside the containing object."); + advance(); + return name; +} + +void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr _subObject) +{ + _container.subIndexByName[_name] = _container.subObjects.size(); + _container.subObjects.emplace_back(std::move(_subObject)); +} diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h new file mode 100644 index 00000000..1d88a119 --- /dev/null +++ b/libyul/ObjectParser.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Parser for Yul code and data object container. + */ + +#pragma once + +#include +#include + +#include +#include + +#include + +#include + +namespace langutil +{ +class Scanner; +} + +namespace yul +{ + +/** + * Yul object parser. Invokes the inline assembly parser. + */ +class ObjectParser: public langutil::ParserBase +{ +public: + explicit ObjectParser( + langutil::ErrorReporter& _errorReporter, + yul::AsmFlavour _flavour = yul::AsmFlavour::Loose + ): + ParserBase(_errorReporter), m_flavour(_flavour) {} + + /// Parses a Yul object. + /// Falls back to code-only parsing if the source starts with `{`. + /// @param _reuseScanner if true, do check for end of input after the last `}`. + /// @returns an empty shared pointer on error. + std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); + +private: + std::shared_ptr parseObject(Object* _containingObject = nullptr); + std::shared_ptr parseCode(); + std::shared_ptr parseBlock(); + void parseData(Object& _containingObject); + + /// Tries to parse a name that is non-empty and unique inside the containing object. + YulString parseUniqueName(Object const* _containingObject); + void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr _subObject); + + yul::AsmFlavour m_flavour; +}; + +} -- cgit v1.2.3