diff options
author | chriseth <chris@ethereum.org> | 2018-11-28 18:33:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-28 18:33:29 +0800 |
commit | 7cbf04686442b44b41d7b24800edb8444db31092 (patch) | |
tree | 01df06aea5ea6845f8bf54f93e6efef335c06b72 | |
parent | bc7cb301e3d71756c8fbefe888aca53433302117 (diff) | |
parent | 69dcf1a5f78ce6caf1c78012e599d7a1f08a6877 (diff) | |
download | dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar.gz dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar.bz2 dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar.lz dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar.xz dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.tar.zst dexon-solidity-7cbf04686442b44b41d7b24800edb8444db31092.zip |
Merge pull request #5358 from ethereum/yulObjects
[Yul] Yul objects parser
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.cpp | 33 | ||||
-rw-r--r-- | libsolidity/interface/AssemblyStack.h | 16 | ||||
-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 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 9 | ||||
-rw-r--r-- | test/libyul/ObjectParser.cpp | 257 |
10 files changed, 635 insertions, 34 deletions
diff --git a/Changelog.md b/Changelog.md index bed3ab36..a90580bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Compiler Features: * SMTChecker: Support ``msg``, ``tx`` and ``block`` member variables. * SMTChecker: Support ``gasleft()`` and ``blockhash()`` functions. * SMTChecker: Support internal bound function calls. + * Yul: Support Yul objects in ``--assemble`` and ``--yul`` commandline options. Bugfixes: diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 31959d93..24e190b3 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -30,6 +30,7 @@ #include <libyul/AsmCodeGen.h> #include <libyul/backends/evm/EVMCodeTransform.h> #include <libyul/backends/evm/EVMAssembly.h> +#include <libyul/ObjectParser.h> #include <libevmasm/Assembly.h> @@ -69,30 +70,22 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName); - m_parserResult = yul::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); + m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); - - 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>(*_scanner); - m_parserResult = make_shared<yul::Block>(_block); + solAssert(m_parserResult->code, ""); return analyzeParsed(); } bool AssemblyStack::analyzeParsed() { - m_analysisInfo = make_shared<yul::AsmAnalysisInfo>(); - yul::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); - m_analysisSuccessful = analyzer.analyze(*m_parserResult); + solAssert(m_parserResult, ""); + solAssert(m_parserResult->code, ""); + m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language)); + m_analysisSuccessful = analyzer.analyze(*m_parserResult->code); return m_analysisSuccessful; } @@ -100,7 +93,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { solAssert(m_analysisSuccessful, ""); solAssert(m_parserResult, ""); - solAssert(m_analysisInfo, ""); + solAssert(m_parserResult->code, ""); + solAssert(m_parserResult->analysisInfo, ""); switch (_machine) { @@ -108,7 +102,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; eth::Assembly assembly; - yul::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly); + yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly); object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble()); object.assembly = assembly.assemblyString(); return object; @@ -117,7 +111,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const { MachineAssemblyObject object; yul::EVMAssembly assembly(true); - yul::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult); + yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); /// TODO: fill out text representation return object; @@ -132,5 +126,6 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const string AssemblyStack::print() const { solAssert(m_parserResult, ""); - return yul::AsmPrinter(m_language == Language::Yul)(*m_parserResult); + solAssert(m_parserResult->code, ""); + return m_parserResult->toString(m_language == Language::Yul) + "\n"; } diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 03b811db..7ae0592e 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -24,6 +24,9 @@ #include <liblangutil/ErrorReporter.h> #include <liblangutil/EVMVersion.h> +#include <libyul/Object.h> +#include <libyul/ObjectParser.h> + #include <libevmasm/LinkerObject.h> #include <string> @@ -34,12 +37,6 @@ namespace langutil class Scanner; } -namespace yul -{ -struct AsmAnalysisInfo; -struct Block; -} - namespace dev { namespace solidity @@ -72,10 +69,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 +87,7 @@ private: std::shared_ptr<langutil::Scanner> m_scanner; bool m_analysisSuccessful = false; - std::shared_ptr<yul::Block> m_parserResult; - std::shared_ptr<yul::AsmAnalysisInfo> m_analysisInfo; + std::shared_ptr<yul::Object> 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 <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; +}; + +} diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 31d21490..11d4c59f 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -124,7 +124,8 @@ void parsePrintCompare(string const& _source, bool _canWarn = false) BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); else BOOST_REQUIRE(stack.errors().empty()); - BOOST_CHECK_EQUAL(stack.print(), _source); + string expectation = "object \"object\" {\n code " + boost::replace_all_copy(_source, "\n", "\n ") + "\n}\n"; + BOOST_CHECK_EQUAL(stack.print(), expectation); } } @@ -567,12 +568,14 @@ BOOST_AUTO_TEST_CASE(print_string_literals) BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; - string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; + string parsed = "object \"object\" {\n code {\n let x := \"\\xe1\\xae\\xac\"\n }\n}\n"; AssemblyStack stack(dev::test::Options::get().evmVersion()); BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); BOOST_CHECK_EQUAL(stack.print(), parsed); - parsePrintCompare(parsed); + + string parsedInner = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; + parsePrintCompare(parsedInner); } BOOST_AUTO_TEST_CASE(print_if) diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp new file mode 100644 index 00000000..bb88e4da --- /dev/null +++ b/test/libyul/ObjectParser.cpp @@ -0,0 +1,257 @@ +/* + 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/>. +*/ +/** + * @date 2018 + * Unit tests for the Yul object parser. + */ + +#include <test/Options.h> + +#include <test/libsolidity/ErrorCheck.h> + +#include <libsolidity/interface/AssemblyStack.h> + +#include <boost/optional.hpp> +#include <boost/algorithm/string/replace.hpp> + +#include <string> +#include <memory> + +using namespace std; +using namespace langutil; + +namespace dev +{ +namespace yul +{ +namespace test +{ + +namespace +{ + +std::pair<bool, ErrorList> parse(string const& _source) +{ + try + { + solidity::AssemblyStack asmStack( + dev::test::Options::get().evmVersion(), + solidity::AssemblyStack::Language::StrictAssembly + ); + bool success = asmStack.parseAndAnalyze("source", _source); + return {success, asmStack.errors()}; + } + catch (FatalError const&) + { + BOOST_FAIL("Fatal error leaked."); + } + return {false, {}}; +} + +boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +{ + bool success; + ErrorList errors; + tie(success, errors) = parse(_source); + if (!success) + { + BOOST_REQUIRE_EQUAL(errors.size(), 1); + return *errors.front(); + } + else + { + // If success is true, there might still be an error in the assembly stage. + if (_allowWarnings && Error::containsOnlyWarnings(errors)) + return {}; + else if (!errors.empty()) + { + if (!_allowWarnings) + BOOST_CHECK_EQUAL(errors.size(), 1); + return *errors.front(); + } + } + return {}; +} + +bool successParse(std::string const& _source, bool _allowWarnings = true) +{ + return !parseAndReturnFirstError(_source, _allowWarnings); +} + +Error expectError(std::string const& _source, bool _allowWarnings = false) +{ + + auto error = parseAndReturnFirstError(_source, _allowWarnings); + BOOST_REQUIRE(error); + return *error; +} + +} + +#define CHECK_ERROR(text, typ, substring) \ +do \ +{ \ + Error err = expectError((text), false); \ + BOOST_CHECK(err.type() == (Error::Type::typ)); \ + BOOST_CHECK(solidity::searchErrorMessage(err, (substring))); \ +} while(0) + +BOOST_AUTO_TEST_SUITE(YulObjectParser) + +BOOST_AUTO_TEST_CASE(empty_code) +{ + BOOST_CHECK(successParse("{ }")); +} + +BOOST_AUTO_TEST_CASE(object_with_empty_code) +{ + BOOST_CHECK(successParse("object \"a\" { code { } }")); +} + +BOOST_AUTO_TEST_CASE(non_object) +{ + CHECK_ERROR("code {}", ParserError, "Expected keyword \"object\""); +} + +BOOST_AUTO_TEST_CASE(empty_name) +{ + CHECK_ERROR("object \"\" { code {} }", ParserError, "Object name cannot be empty"); +} + +BOOST_AUTO_TEST_CASE(recursion_depth) +{ + string input; + for (size_t i = 0; i < 20000; i++) + input += "object \"a" + to_string(i) + "\" { code {} "; + for (size_t i = 0; i < 20000; i++) + input += "}"; + + CHECK_ERROR(input, ParserError, "recursion"); +} + +BOOST_AUTO_TEST_CASE(object_with_code) +{ + BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } }")); +} + +BOOST_AUTO_TEST_CASE(object_with_code_and_data) +{ + BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } data \"b\" hex\"01010202\" }")); +} + +BOOST_AUTO_TEST_CASE(object_with_non_code_at_start) +{ + CHECK_ERROR("object \"a\" { data \"d\" hex\"0102\" code { } }", ParserError, "Expected keyword \"code\""); +} + +BOOST_AUTO_TEST_CASE(nested_object) +{ + string code = R"( + object "outer" { + code { let x := mload(0) } + data "x" "stringdata" + object "inner" { + code { mstore(0, 1) } + object "inner inner" { code {} } + data "innerx" "abc" + data "innery" "def" + } + } + )"; + BOOST_CHECK(successParse(code)); +} + +BOOST_AUTO_TEST_CASE(incomplete) +{ + CHECK_ERROR("object \"abc\" { code {} } object", ParserError, "Expected end of source"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_name) +{ + string code = R"( + object "outer" { + code { } + data "outer" "stringdata" + } + )"; + CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_in_subobject) +{ + string code = R"( + object "outer" { + code { } + object "outer" { code {} } + } + )"; + CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); +} + +BOOST_AUTO_TEST_CASE(reuse_object_of_sibling) +{ + string code = R"( + object "O" { + code { } + object "i" { code {} } + data "i" "abc" + } + )"; + CHECK_ERROR(code, ParserError, "already exists inside the"); +} + +BOOST_AUTO_TEST_CASE(to_string) +{ + string code = R"( + object "O" { + code { let x := mload(0) if x { sstore(0, 1) } } + object "i" { code {} data "j" "def" } + data "j" "abc" + data "k" hex"010203" + } + )"; + string expectation = R"(object "O" { + code { + let x := mload(0) + if x + { + sstore(0, 1) + } + } + object "i" { + code { + } + data "j" hex"646566" + } + data "j" hex"616263" + data "k" hex"010203" +} +)"; + expectation = boost::replace_all_copy(expectation, "\t", " "); + solidity::AssemblyStack asmStack( + dev::test::Options::get().evmVersion(), + solidity::AssemblyStack::Language::StrictAssembly + ); + BOOST_REQUIRE(asmStack.parseAndAnalyze("source", code)); + BOOST_CHECK_EQUAL(asmStack.print(), expectation); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces |