diff options
Diffstat (limited to 'libyul')
-rw-r--r-- | libyul/CMakeLists.txt | 2 | ||||
-rw-r--r-- | libyul/Object.cpp | 61 | ||||
-rw-r--r-- | libyul/Object.h | 72 | ||||
-rw-r--r-- | libyul/ObjectParser.cpp | 146 | ||||
-rw-r--r-- | libyul/ObjectParser.h | 72 |
5 files changed, 353 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>. +*/ +/** + * Yul code and data object container. + */ + +#include <libyul/Object.h> + +#include <libyul/AsmPrinter.h> +#include <libyul/Exceptions.h> + +#include <libdevcore/Visitor.h> +#include <libdevcore/CommonData.h> + +#include <boost/algorithm/string/replace.hpp> + +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 <http://www.gnu.org/licenses/>. +*/ +/** + * Yul code and data object container. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> +#include <libyul/YulString.h> + +#include <libdevcore/Common.h> + +#include <memory> + +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<Block> code; + std::vector<std::shared_ptr<ObjectNode>> subObjects; + std::map<YulString, size_t> subIndexByName; + std::shared_ptr<yul::AsmAnalysisInfo> 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 <http://www.gnu.org/licenses/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#include <libyul/ObjectParser.h> + +#include <libyul/AsmParser.h> +#include <libyul/Exceptions.h> + +#include <liblangutil/Token.h> + +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace std; + + +shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool _reuseScanner) +{ + m_recursionDepth = 0; + try + { + shared_ptr<Object> object; + m_scanner = _scanner; + if (currentToken() == Token::LBrace) + { + // Special case: Code-only form. + object = make_shared<Object>(); + 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<Object> ObjectParser::parseObject(Object* _containingObject) +{ + RecursionGuard guard(*this); + + if (currentToken() != Token::Identifier || currentLiteral() != "object") + fatalParserError("Expected keyword \"object\"."); + advance(); + + shared_ptr<Object> ret = make_shared<Object>(); + 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<Block> ObjectParser::parseCode() +{ + if (currentToken() != Token::Identifier || currentLiteral() != "code") + fatalParserError("Expected keyword \"code\"."); + advance(); + + return parseBlock(); +} + +shared_ptr<Block> ObjectParser::parseBlock() +{ + Parser parser(m_errorReporter, m_flavour); + shared_ptr<Block> 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<Data>(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<ObjectNode> _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 <http://www.gnu.org/licenses/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#pragma once + +#include <libyul/YulString.h> +#include <libyul/Object.h> + +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/ParserBase.h> + +#include <libdevcore/Common.h> + +#include <memory> + +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<Object> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner); + +private: + std::shared_ptr<Object> parseObject(Object* _containingObject = nullptr); + std::shared_ptr<Block> parseCode(); + std::shared_ptr<Block> 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<ObjectNode> _subObject); + + yul::AsmFlavour m_flavour; +}; + +} |