From 80368b870b55eac441e43a12b558361f04a5c1e6 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 17 Dec 2014 17:47:34 +0100 Subject: [PATCH 01/21] windows icon --- CMakeLists.txt | 4 ++++ alethzero/alethzero.ico | Bin 0 -> 370070 bytes 2 files changed, 4 insertions(+) create mode 100644 alethzero/alethzero.ico diff --git a/CMakeLists.txt b/CMakeLists.txt index 1293b4427..5763c9b85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,8 @@ if (WIN32) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_VERSION "0.7") set(CPACK_GENERATOR "NSIS") + # seems to be not working + # set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.bmp") # our stuff set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications") @@ -178,6 +180,8 @@ if (WIN32) set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum") set(CPACK_NSIS_CONTACT "ethereum.org") set(CPACK_NSIS_MODIFY_PATH ON) + set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.ico") + set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/alethzero/alethzero.ico") include(CPack) endif (WIN32) diff --git a/alethzero/alethzero.ico b/alethzero/alethzero.ico new file mode 100644 index 0000000000000000000000000000000000000000..acee27751646c7054bd617e0be34ed1c4e55a1f3 GIT binary patch literal 370070 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 Date: Thu, 18 Dec 2014 14:39:16 +0100 Subject: [PATCH 02/21] Possibility to replace source and obtain ContractDefinition by name. --- libsolidity/CompilerStack.cpp | 12 ++++++++---- libsolidity/CompilerStack.h | 9 +++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 9fdc88baa..79716fdec 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -36,13 +36,12 @@ namespace dev namespace solidity { -void CompilerStack::addSource(string const& _name, string const& _content) +bool CompilerStack::addSource(string const& _name, string const& _content) { - if (m_sources.count(_name)) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists.")); - + bool existed = m_sources.count(_name); reset(true); m_sources[_name].scanner = make_shared(CharStream(_content), _name); + return existed; } void CompilerStack::setSource(string const& _sourceCode) @@ -181,6 +180,11 @@ SourceUnit const& CompilerStack::getAST(string const& _sourceName) const return *getSource(_sourceName).ast; } +ContractDefinition const& CompilerStack::getContractDefinition(string const& _contractName) const +{ + return *getContract(_contractName).contract; +} + bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) { CompilerStack stack; diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 5ad6f0a60..358c8fb77 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -57,7 +57,8 @@ public: CompilerStack(): m_parseSuccessful(false) {} /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. - void addSource(std::string const& _name, std::string const& _content); + /// @returns true if a source object by the name already existed and was replaced. + bool addSource(std::string const& _name, std::string const& _content); void setSource(std::string const& _sourceCode); /// Parses all source units that were added void parse(); @@ -86,9 +87,13 @@ public: /// Can be one of 3 types defined at @c DocumentationType std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const; - /// Returns the previously used scanner, useful for counting lines during error reporting. + /// @returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner(std::string const& _sourceName = "") const; + /// @returns the parsed source unit with the supplied name. SourceUnit const& getAST(std::string const& _sourceName = "") const; + /// @returns the parsed contract with the supplied name. Throws an exception if the contract + /// does not exist. + ContractDefinition const& getContractDefinition(std::string const& _contractName) const; /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// scanning the source code - this is useful for printing exception information. From 6d48f3e98764fff270397fc00d37f9c6328294fd Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 17 Dec 2014 18:53:18 +0100 Subject: [PATCH 03/21] Work in progress for /** ... */ natspec comments - Work in progress on the scanner for recognizing the second type of doxygen comments for Natspec. --- libsolidity/Scanner.cpp | 58 +++++++++++++++++++++++++++++++++++++--- libsolidity/Scanner.h | 3 ++- test/solidityScanner.cpp | 7 +++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 1a21149a1..2e9b7b454 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -219,7 +219,7 @@ Token::Value Scanner::skipSingleLineComment() return Token::WHITESPACE; } -Token::Value Scanner::scanDocumentationComment() +Token::Value Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' @@ -250,7 +250,6 @@ Token::Value Scanner::scanDocumentationComment() Token::Value Scanner::skipMultiLineComment() { - solAssert(m_char == '*', ""); advance(); while (!isSourcePastEndOfInput()) { @@ -270,6 +269,43 @@ Token::Value Scanner::skipMultiLineComment() return Token::ILLEGAL; } +Token::Value Scanner::scanMultiLineDocComment() +{ + LiteralScope literal(this, LITERAL_TYPE_COMMENT); + bool endFound = false; + + advance(); //consume the last '*' + while (!isSourcePastEndOfInput()) + { + // skip starting '*' in multiine comments + if (isLineTerminator(m_char)) + { + skipWhitespace(); + if (!m_source.isPastEndOfInput(2) && m_source.get(1) == '*' && m_source.get(2) != '/') + { + addCommentLiteralChar('\n'); + m_char = m_source.advanceAndGet(3); + } + else + addCommentLiteralChar('\n'); + } + + if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') + { + m_source.advanceAndGet(2); + endFound = true; + break; + } + addCommentLiteralChar(m_char); + advance(); + } + literal.complete(); + if (!endFound) + return Token::ILLEGAL; + else + return Token::COMMENT_LITERAL; +} + void Scanner::scanToken() { m_nextToken.literal.clear(); @@ -381,7 +417,7 @@ void Scanner::scanToken() { Token::Value comment; m_nextSkippedComment.location.start = getSourcePos(); - comment = scanDocumentationComment(); + comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; token = Token::WHITESPACE; @@ -390,7 +426,21 @@ void Scanner::scanToken() token = skipSingleLineComment(); } else if (m_char == '*') - token = skipMultiLineComment(); + { + if (!advance()) /* slash star comment before EOS */ + token = Token::WHITESPACE; + else if (m_char == '*') + { + Token::Value comment; + m_nextSkippedComment.location.start = getSourcePos(); + comment = scanMultiLineDocComment(); + m_nextSkippedComment.location.end = getSourcePos(); + m_nextSkippedComment.token = comment; + token = Token::WHITESPACE; + } + else + token = skipMultiLineComment(); + } else if (m_char == '=') token = selectToken(Token::ASSIGN_DIV); else diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 18b1f5d3a..7f1b18352 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -190,7 +190,8 @@ private: Token::Value scanIdentifierOrKeyword(); Token::Value scanString(); - Token::Value scanDocumentationComment(); + Token::Value scanSingleLineDocComment(); + Token::Value scanMultiLineDocComment(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 573affe6d..2b8c7c458 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -160,6 +160,13 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); } +BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) +{ + Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); +} + BOOST_AUTO_TEST_CASE(documentation_comments_parsed) { Scanner scanner(CharStream("some other tokens /// Send $(value / 1000) chocolates to the user")); From 61a1f4436c5561a6c521b9c6009c57c76d275adb Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 13:27:25 +0100 Subject: [PATCH 04/21] Scanner properly scans multiline natspec comments - Single and multiline natspect comments get the initial whitespace skipped now - Some rules introduced for the multiline comments. If first line is empty then no newline is added to the literal. Same thing with the last line. Finally in all lines initial '*' are skipped --- libsolidity/Scanner.cpp | 36 ++++++++++++++++++++++++++++-------- libsolidity/Scanner.h | 2 ++ test/solidityScanner.cpp | 25 ++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 2e9b7b454..890d69494 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -209,6 +209,15 @@ bool Scanner::skipWhitespace() return getSourcePos() != startPosition; } +bool Scanner::skipWhitespaceExceptLF() +{ + int const startPosition = getSourcePos(); + while (m_char == ' ' || m_char == '\t') + advance(); + // Return whether or not we skipped any characters. + return getSourcePos() != startPosition; +} + Token::Value Scanner::skipSingleLineComment() { // The line terminator at the end of the line is not considered @@ -222,7 +231,8 @@ Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); - advance(); //consume the last '/' + advance(); //consume the last '/' at /// + skipWhitespaceExceptLF(); while (!isSourcePastEndOfInput()) { if (isLineTerminator(m_char)) @@ -273,18 +283,27 @@ Token::Value Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); bool endFound = false; + bool charsAdded = false; - advance(); //consume the last '*' + advance(); //consume the last '*' at /** + skipWhitespaceExceptLF(); while (!isSourcePastEndOfInput()) { - // skip starting '*' in multiine comments + //handle newlines in multline comments if (isLineTerminator(m_char)) { skipWhitespace(); - if (!m_source.isPastEndOfInput(2) && m_source.get(1) == '*' && m_source.get(2) != '/') - { - addCommentLiteralChar('\n'); - m_char = m_source.advanceAndGet(3); + if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/') + { // skip first '*' in subsequent lines + if (charsAdded) + addCommentLiteralChar('\n'); + m_char = m_source.advanceAndGet(2); + } + else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') + { // if after newline the comment ends, don't insert the newline + m_char = m_source.advanceAndGet(2); + endFound = true; + break; } else addCommentLiteralChar('\n'); @@ -292,11 +311,12 @@ Token::Value Scanner::scanMultiLineDocComment() if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') { - m_source.advanceAndGet(2); + m_char = m_source.advanceAndGet(2); endFound = true; break; } addCommentLiteralChar(m_char); + charsAdded = true; advance(); } literal.complete(); diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 7f1b18352..5e70db51d 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -182,6 +182,8 @@ private: /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); + /// Skips all whitespace except Line feeds and returns true if something was skipped + bool skipWhitespaceExceptLF(); Token::Value skipSingleLineComment(); Token::Value skipMultiLineComment(); diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 2b8c7c458..159e53055 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -157,14 +157,14 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) { Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); - BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) { Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); - BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(documentation_comments_parsed) @@ -174,7 +174,19 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed) BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); +} + +BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) +{ + Scanner scanner(CharStream("some other tokens /**\n" + "* Send $(value / 1000) chocolates to the user\n" + "*/")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(comment_before_eos) @@ -191,6 +203,13 @@ BOOST_AUTO_TEST_CASE(documentation_comment_before_eos) BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); } +BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos) +{ + Scanner scanner(CharStream("/***/")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); +} + BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) { Scanner scanner(CharStream("hello_world ///documentation comment \n" From 30beaca35955476ef07ee1f9c82735b0d63241bf Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 14:43:35 +0100 Subject: [PATCH 05/21] Changes in InterfaceHandler to deal with multiline natspec - Also now Solidity scanner considers Carriage Return as whitespace - Tests for Natspec generation with the new multiline comments --- libsolidity/InterfaceHandler.cpp | 46 +++++++++++++------------------- libsolidity/InterfaceHandler.h | 3 ++- libsolidity/Scanner.cpp | 2 +- test/solidityNatspecJSON.cpp | 29 ++++++++++++++++++++ 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index c734fa38c..9ae284b40 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -166,9 +166,12 @@ static inline std::string::const_iterator skipLineOrEOS(std::string::const_itera std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - DocTagType _tagType) + DocTagType _tagType, + bool _appending) { auto nlPos = std::find(_pos, _end, '\n'); + if (_appending && *_pos != ' ') + _tagString += " "; std::copy(_pos, nlPos, back_inserter(_tagString)); m_lastTag = _tagType; return skipLineOrEOS(nlPos, _end); @@ -201,7 +204,8 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); auto pair = m_params.back(); - pair.second += " "; + if (*_pos != ' ') + pair.second += " "; auto nlPos = std::find(_pos, _end, '\n'); std::copy(_pos, nlPos, back_inserter(pair.second)); @@ -221,17 +225,17 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite if (m_lastTag == DocTagType::NONE || _tag != "") { if (_tag == "dev") - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false); else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false); else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false); else if (_tag == "author") { if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false); else if (_owner == CommentOwner::FUNCTION) - return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false); else // LTODO: for now this else makes no sense but later comments will go to more language constructs BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); @@ -239,7 +243,7 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite else if (_tag == "title") { if (_owner == CommentOwner::CONTRACT) - return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); @@ -261,34 +265,22 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it switch (m_lastTag) { case DocTagType::DEV: - m_dev += " "; - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true); case DocTagType::NOTICE: - m_notice += " "; - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true); case DocTagType::RETURN: - m_return += " "; - return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true); case DocTagType::AUTHOR: if (_owner == CommentOwner::CONTRACT) - { - m_contractAuthor += " "; - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); - } + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true); else if (_owner == CommentOwner::FUNCTION) - { - m_author += " "; - return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); - } + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); case DocTagType::TITLE: if (_owner == CommentOwner::CONTRACT) - { - m_title += " "; - return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); - } + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true); else // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); @@ -329,7 +321,7 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _ currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner); } else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag - currPos = appendDocTag(currPos + 1, end, _owner); + currPos = appendDocTag(currPos, end, _owner); else if (currPos != end) // skip the line if a newline was found currPos = nlPos + 1; } diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index d271a6697..c8399d71f 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -92,7 +92,8 @@ private: std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - DocTagType _tagType); + DocTagType _tagType, + bool _appending); std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, std::string::const_iterator _end); std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 890d69494..69b61ce4b 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -80,7 +80,7 @@ bool isLineTerminator(char c) } bool isWhiteSpace(char c) { - return c == ' ' || c == '\n' || c == '\t'; + return c == ' ' || c == '\n' || c == '\t' || c == '\r'; } bool isIdentifierStart(char c) { diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 2c3ded08d..5b48a67ce 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -394,6 +394,35 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return) checkNatspec(sourceCode, natspec, false); } +BOOST_AUTO_TEST_CASE(dev_multiline_comment) +{ + char const* sourceCode = "contract test {\n" + " /**\n" + " * @dev Multiplies a number by 7 and adds second parameter\n" + " * @param a Documentation for the first parameter starts here.\n" + " * Since it's a really complicated parameter we need 2 lines\n" + " * @param second Documentation for the second parameter\n" + " * @return The result of the multiplication\n" + " * and cookies with nutella\n" + " */" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " },\n" + " \"return\": \"The result of the multiplication and cookies with nutella\"\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_CASE(dev_contract_no_doc) { char const* sourceCode = "contract test {\n" From 8edb5b8b54f1c023f68688f6ac52d9878975b8a7 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 15:21:03 +0100 Subject: [PATCH 06/21] Solidity Tests names are now more consistent - File names and Boost Test Suite have the same name now for every solidity Test, so that there is no need to guess or check when you want to run a specific suite from the command line --- test/{solidityJSONInterfaceTest.cpp => SolidityABIJSON.cpp} | 0 test/{solidityCompiler.cpp => SolidityCompiler.cpp} | 0 test/{solidityEndToEndTest.cpp => SolidityEndToEndTest.cpp} | 2 +- ...ityExpressionCompiler.cpp => SolidityExpressionCompiler.cpp} | 0 ...eAndTypeResolution.cpp => SolidityNameAndTypeResolution.cpp} | 0 test/{solidityNatspecJSON.cpp => SolidityNatspecJSON.cpp} | 0 test/{solidityOptimizerTest.cpp => SolidityOptimizer.cpp} | 2 +- test/{solidityParser.cpp => SolidityParser.cpp} | 0 test/{solidityScanner.cpp => SolidityScanner.cpp} | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename test/{solidityJSONInterfaceTest.cpp => SolidityABIJSON.cpp} (100%) rename test/{solidityCompiler.cpp => SolidityCompiler.cpp} (100%) rename test/{solidityEndToEndTest.cpp => SolidityEndToEndTest.cpp} (99%) rename test/{solidityExpressionCompiler.cpp => SolidityExpressionCompiler.cpp} (100%) rename test/{solidityNameAndTypeResolution.cpp => SolidityNameAndTypeResolution.cpp} (100%) rename test/{solidityNatspecJSON.cpp => SolidityNatspecJSON.cpp} (100%) rename test/{solidityOptimizerTest.cpp => SolidityOptimizer.cpp} (98%) rename test/{solidityParser.cpp => SolidityParser.cpp} (100%) rename test/{solidityScanner.cpp => SolidityScanner.cpp} (100%) diff --git a/test/solidityJSONInterfaceTest.cpp b/test/SolidityABIJSON.cpp similarity index 100% rename from test/solidityJSONInterfaceTest.cpp rename to test/SolidityABIJSON.cpp diff --git a/test/solidityCompiler.cpp b/test/SolidityCompiler.cpp similarity index 100% rename from test/solidityCompiler.cpp rename to test/SolidityCompiler.cpp diff --git a/test/solidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp similarity index 99% rename from test/solidityEndToEndTest.cpp rename to test/SolidityEndToEndTest.cpp index 26f5528ab..aa74f8186 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -36,7 +36,7 @@ namespace solidity namespace test { -BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework) +BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, ExecutionFramework) BOOST_AUTO_TEST_CASE(smoke_test) { diff --git a/test/solidityExpressionCompiler.cpp b/test/SolidityExpressionCompiler.cpp similarity index 100% rename from test/solidityExpressionCompiler.cpp rename to test/SolidityExpressionCompiler.cpp diff --git a/test/solidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp similarity index 100% rename from test/solidityNameAndTypeResolution.cpp rename to test/SolidityNameAndTypeResolution.cpp diff --git a/test/solidityNatspecJSON.cpp b/test/SolidityNatspecJSON.cpp similarity index 100% rename from test/solidityNatspecJSON.cpp rename to test/SolidityNatspecJSON.cpp diff --git a/test/solidityOptimizerTest.cpp b/test/SolidityOptimizer.cpp similarity index 98% rename from test/solidityOptimizerTest.cpp rename to test/SolidityOptimizer.cpp index 388e0579c..ef5c6f9b5 100644 --- a/test/solidityOptimizerTest.cpp +++ b/test/SolidityOptimizer.cpp @@ -71,7 +71,7 @@ protected: Address m_nonOptimizedContract; }; -BOOST_FIXTURE_TEST_SUITE(SolidityOptimizerTest, OptimizerTestFramework) +BOOST_FIXTURE_TEST_SUITE(SolidityOptimizer, OptimizerTestFramework) BOOST_AUTO_TEST_CASE(smoke_test) { diff --git a/test/solidityParser.cpp b/test/SolidityParser.cpp similarity index 100% rename from test/solidityParser.cpp rename to test/SolidityParser.cpp diff --git a/test/solidityScanner.cpp b/test/SolidityScanner.cpp similarity index 100% rename from test/solidityScanner.cpp rename to test/SolidityScanner.cpp From 2e3d45004681407393f0742bec6d9ed463211dcf Mon Sep 17 00:00:00 2001 From: ethdev Date: Thu, 18 Dec 2014 15:39:15 +0100 Subject: [PATCH 07/21] copy qml dependencies of mix to install directory --- cmake/EthExecutableHelper.cmake | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 0ec1e10d4..5f4ae3c09 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -60,7 +60,11 @@ macro(eth_install_executable EXECUTABLE) cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") if (ETH_INSTALL_EXECUTABLE_QMLDIR) - set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") + if (APPLE) + set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}") + elseif (WIN32) + set(eth_qml_dir --qmldir ${ETH_INSTALL_EXECUTABLE_QMLDIR}) + endif() message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}") endif() @@ -68,7 +72,8 @@ macro(eth_install_executable EXECUTABLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir} - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) # This tool and next will inspect linked libraries in order to determine which dependencies are required if (${CMAKE_CFG_INTDIR} STREQUAL ".") @@ -108,6 +113,13 @@ macro(eth_install_executable EXECUTABLE) $/platforms ) + # ugly way, improve that + add_custom_command(TARGET ${EXECUTABLE} POST_BUILD + COMMAND cmake -E copy_directory + "${ETH_DEPENDENCY_INSTALL_DIR}/qml" + $ + ) + install( FILES ${DLLS} DESTINATION bin COMPONENT ${EXECUTABLE} @@ -118,6 +130,14 @@ macro(eth_install_executable EXECUTABLE) COMPONENT ${EXECUTABLE} ) + file (GLOB QMLS ${ETH_DEPENDENCY_INSTALL_DIR}/qml/*) + foreach(QML ${QMLS}) + install( DIRECTORY ${QML} + DESTINATION bin + COMPONENT ${EXECUTABLE} + ) + endforeach() + install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin COMPONENT ${EXECUTABLE} From 23d4f98b744887bdc1f1952bf303651ac6573e3e Mon Sep 17 00:00:00 2001 From: ethdev Date: Thu, 18 Dec 2014 15:50:23 +0100 Subject: [PATCH 08/21] alethzero.exe icon --- alethzero/CMakeLists.txt | 1 + alethzero/alethzero.rc | 1 + cmake/EthExecutableHelper.cmake | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 alethzero/alethzero.rc diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 39c02d6be..8c8a37a42 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -27,6 +27,7 @@ endif () eth_add_executable(${EXECUTABLE} ICON alethzero UI_RESOURCES alethzero.icns Main.ui + WIN_RESOURCES alethzero.rc ) add_dependencies(${EXECUTABLE} BuildInfo.h) diff --git a/alethzero/alethzero.rc b/alethzero/alethzero.rc new file mode 100644 index 000000000..29c778bd4 --- /dev/null +++ b/alethzero/alethzero.rc @@ -0,0 +1 @@ +APP_ICON ICON DISCARDABLE "alethzero.ico" \ No newline at end of file diff --git a/cmake/EthExecutableHelper.cmake b/cmake/EthExecutableHelper.cmake index 5f4ae3c09..88e2a3e16 100644 --- a/cmake/EthExecutableHelper.cmake +++ b/cmake/EthExecutableHelper.cmake @@ -18,7 +18,7 @@ macro(eth_add_executable EXECUTABLE) set (extra_macro_args ${ARGN}) set (options) set (one_value_args ICON) - set (multi_value_args UI_RESOURCES) + set (multi_value_args UI_RESOURCES WIN_RESOURCES) cmake_parse_arguments (ETH_ADD_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}") if (APPLE) @@ -38,7 +38,7 @@ macro(eth_add_executable EXECUTABLE) set_source_files_properties(${MACOSX_BUNDLE_ICON_FILE}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) else () - add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${SRC_LIST} ${HEADERS}) + add_executable(${EXECUTABLE} ${ETH_ADD_EXECUTABLE_UI_RESOURCES} ${ETH_ADD_EXECUTABLE_WIN_RESOURCES} ${SRC_LIST} ${HEADERS}) endif() endmacro() From b657b009ef7a272d105f92fbc0d3d6e449664fd3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Dec 2014 16:03:59 +0100 Subject: [PATCH 09/21] Cleanups and YP formalism. --- libethereum/Transaction.cpp | 4 ++-- libevm/VM.h | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 0c1173134..24c56930e 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -43,9 +43,9 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender) m_receiveAddress = rlp[field = 3].toHash
(); m_value = rlp[field = 4].toInt(); m_data = rlp[field = 5].toBytes(); + byte v = rlp[field = 6].toInt() - 27; h256 r = rlp[field = 7].toInt(); h256 s = rlp[field = 8].toInt(); - byte v = rlp[field = 6].toInt() - 27; m_vrs = SignatureStruct{ r, s, v }; if (_checkSender) m_sender = sender(); @@ -74,7 +74,7 @@ Address Transaction::sender() const { if (!m_sender) { - auto p = recover(*(Signature const*)&m_vrs, sha3(WithoutSignature)); + auto p = recover(m_vrs, sha3(WithoutSignature)); if (!p) BOOST_THROW_EXCEPTION(InvalidSignature()); m_sender = right160(dev::sha3(bytesConstRef(p.data(), sizeof(p)))); diff --git a/libevm/VM.h b/libevm/VM.h index 3eb330fcd..1f3fc17d5 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -217,6 +217,8 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st require(7); runGas = (bigint)c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + if (_ext.depth == 1024) + BOOST_THROW_EXCEPTION(OutOfGas()); break; case Instruction::CREATE: @@ -226,6 +228,8 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st u256 inSize = m_stack[m_stack.size() - 3]; newTempSize = (bigint)inOff + inSize; runGas = c_createGas; + if (_ext.depth == 1024) + BOOST_THROW_EXCEPTION(OutOfGas()); break; } case Instruction::EXP: @@ -566,6 +570,7 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st break; default: // this is unreachable, but if someone introduces a bug in the future, he may get here. + assert(false); BOOST_THROW_EXCEPTION(InvalidOpcode() << errinfo_comment("CALLDATACOPY, CODECOPY or EXTCODECOPY instruction requested.")); break; } @@ -795,8 +800,6 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st if (_ext.balance(_ext.myAddress) >= endowment) { - if (_ext.depth == 1024) - BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(endowment); m_stack.push_back((u160)_ext.create(endowment, m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); } @@ -825,8 +828,6 @@ inline bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _st if (_ext.balance(_ext.myAddress) >= value) { - if (_ext.depth == 1024) - BOOST_THROW_EXCEPTION(OutOfGas()); _ext.subBalance(value); m_stack.push_back(_ext.call(inst == Instruction::CALL ? receiveAddress : _ext.myAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), gas, bytesRef(m_temp.data() + outOff, outSize), _onOp, {}, receiveAddress)); } From 71b267d01aaa32786dfbb3dccf520d86f2f77708 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Dec 2014 16:25:44 +0100 Subject: [PATCH 10/21] Update serpent. --- libserpent/bignum.h | 3 + libserpent/compiler.cpp | 279 +++------ libserpent/opcodes.h | 123 +--- libserpent/parser.cpp | 86 +-- libserpent/rewriter.cpp | 1206 +++++++++++++++------------------------ libserpent/tokenize.cpp | 4 +- libserpent/util.cpp | 57 +- libserpent/util.h | 36 +- sc/cmdline.cpp | 13 + 9 files changed, 709 insertions(+), 1098 deletions(-) diff --git a/libserpent/bignum.h b/libserpent/bignum.h index 599365b6c..99571acd2 100644 --- a/libserpent/bignum.h +++ b/libserpent/bignum.h @@ -35,4 +35,7 @@ bool decimalGt(std::string a, std::string b, bool eqAllowed=false); unsigned decimalToUnsigned(std::string a); +#define utd unsignedToDecimal +#define dtu decimalToUnsigned + #endif diff --git a/libserpent/compiler.cpp b/libserpent/compiler.cpp index 30628fbc9..a3e5b1c60 100644 --- a/libserpent/compiler.cpp +++ b/libserpent/compiler.cpp @@ -6,6 +6,7 @@ #include "bignum.h" #include "opcodes.h" +// Auxiliary data that is gathered while compiling struct programAux { std::map vars; int nextVarMem; @@ -13,15 +14,19 @@ struct programAux { bool calldataUsed; int step; int labelLength; - int functionCount; }; +// Auxiliary data that gets passed down vertically +// but not back up struct programVerticalAux { int height; + std::string innerScopeName; std::map dupvars; std::map funvars; + std::vector scopes; }; +// Compilation result struct programData { programAux aux; Node code; @@ -34,7 +39,6 @@ programAux Aux() { o.calldataUsed = false; o.step = 0; o.nextVarMem = 32; - o.functionCount = 0; return o; } @@ -43,6 +47,7 @@ programVerticalAux verticalAux() { o.height = 0; o.dupvars = std::map(); o.funvars = std::map(); + o.scopes = std::vector(); return o; } @@ -72,29 +77,58 @@ Node popwrap(Node node) { return multiToken(nodelist, 2, node.metadata); } +// Grabs variables +mss getVariables(Node node, mss cur=mss()) { + Metadata m = node.metadata; + // Tokens don't contain any variables + if (node.type == TOKEN) + return cur; + // Don't descend into call fragments + else if (node.val == "lll") + return getVariables(node.args[1], cur); + // At global scope get/set/ref also declare + else if (node.val == "get" || node.val == "set" || node.val == "ref") { + if (node.args[0].type != TOKEN) + err("Variable name must be simple token," + " not complex expression! " + printSimple(node.args[0]), m); + if (!cur.count(node.args[0].val)) { + cur[node.args[0].val] = utd(cur.size() * 32 + 32); + //std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n"; + } + } + // Recursively process children + for (unsigned i = 0; i < node.args.size(); i++) { + cur = getVariables(node.args[i], cur); + } + return cur; +} + // Turns LLL tree into tree of code fragments programData opcodeify(Node node, programAux aux=Aux(), programVerticalAux vaux=verticalAux()) { std::string symb = "_"+mkUniqueToken(); Metadata m = node.metadata; + // Get variables + if (!aux.vars.size()) { + aux.vars = getVariables(node); + aux.nextVarMem = aux.vars.size() * 32 + 32; + } // Numbers if (node.type == TOKEN) { return pd(aux, nodeToNumeric(node), 1); } - else if (node.val == "ref" || node.val == "get" || - node.val == "set" || node.val == "declare") { + else if (node.val == "ref" || node.val == "get" || node.val == "set") { std::string varname = node.args[0].val; - if (!aux.vars.count(varname)) { - aux.vars[varname] = unsignedToDecimal(aux.nextVarMem); - aux.nextVarMem += 32; - } - if (varname == "'msg.data") aux.calldataUsed = true; + // Determine reference to variable + Node varNode = tkn(aux.vars[varname], m); + //std::cerr << varname << " " << printSimple(varNode) << "\n"; // Set variable if (node.val == "set") { programData sub = opcodeify(node.args[1], aux, vaux); if (!sub.outs) err("Value to set variable must have nonzero arity!", m); + // What if we are setting a stack variable? if (vaux.dupvars.count(node.args[0].val)) { int h = vaux.height - vaux.dupvars[node.args[0].val]; if (h > 16) err("Too deep for stack variable (max 16)", m); @@ -105,149 +139,65 @@ programData opcodeify(Node node, }; return pd(sub.aux, multiToken(nodelist, 3, m), 0); } - Node nodelist[] = { - sub.code, - token(sub.aux.vars[varname], m), - token("MSTORE", m), - }; - return pd(sub.aux, multiToken(nodelist, 3, m), 0); + // Setting a memory variable + else { + Node nodelist[] = { + sub.code, + varNode, + token("MSTORE", m), + }; + return pd(sub.aux, multiToken(nodelist, 3, m), 0); + } } // Get variable else if (node.val == "get") { - if (vaux.dupvars.count(node.args[0].val)) { + // Getting a stack variable + if (vaux.dupvars.count(node.args[0].val)) { int h = vaux.height - vaux.dupvars[node.args[0].val]; if (h > 16) err("Too deep for stack variable (max 16)", m); return pd(aux, token("DUP"+unsignedToDecimal(h)), 1); } - Node nodelist[] = - { token(aux.vars[varname], m), token("MLOAD", m) }; - return pd(aux, multiToken(nodelist, 2, m), 1); + // Getting a memory variable + else { + Node nodelist[] = + { varNode, token("MLOAD", m) }; + return pd(aux, multiToken(nodelist, 2, m), 1); + } } // Refer variable else if (node.val == "ref") { if (vaux.dupvars.count(node.args[0].val)) err("Cannot ref stack variable!", m); - return pd(aux, token(aux.vars[varname], m), 1); - } - // Declare variable - else { - return pd(aux, multiToken(nullptr, 0, m), 0); + return pd(aux, varNode, 1); } } - // Define functions (TODO: eventually move to rewriter.cpp, keep - // compiler pure LLL) - if (node.val == "def") { - std::vector varNames; - std::vector varSizes; - bool useLt32 = false; - int totalSz = 0; - if (node.args.size() != 2) - err("Malformed def!", m); - // Collect the list of variable names and variable byte counts - for (unsigned i = 0; i < node.args[0].args.size(); i++) { - if (node.args[0].args[i].val == "kv") { - if (node.args[0].args[i].args.size() != 2) - err("Malformed def!", m); - varNames.push_back(node.args[0].args[i].args[0].val); - varSizes.push_back( - decimalToUnsigned(node.args[0].args[i].args[1].val)); - if (varSizes.back() > 32) - err("Max argument width: 32 bytes", m); - useLt32 = true; + // Comments do nothing + else if (node.val == "comment") { + Node nodelist[] = { }; + return pd(aux, multiToken(nodelist, 0, m), 0); + } + // Custom operation sequence + // eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc + if (node.val == "ops") { + std::vector subs2; + int depth = 0; + for (unsigned i = 0; i < node.args.size(); i++) { + std::string op = upperCase(node.args[i].val); + if (node.args[i].type == ASTNODE || opinputs(op) == -1) { + programVerticalAux vaux2 = vaux; + vaux2.height = vaux.height - i - 1 + node.args.size(); + programData sub = opcodeify(node.args[i], aux, vaux2); + aux = sub.aux; + depth += sub.outs; + subs2.push_back(sub.code); } else { - varNames.push_back(node.args[0].args[i].val); - varSizes.push_back(32); + subs2.push_back(token(op, m)); + depth += opoutputs(op) - opinputs(op); } - aux.vars[varNames.back()] = unsignedToDecimal(aux.nextVarMem + 32 * i); - totalSz += varSizes.back(); } - int functionCount = aux.functionCount; - int nextVarMem = aux.nextVarMem; - aux.nextVarMem += 32 * varNames.size(); - aux.functionCount += 1; - programData inner; - // If we're only using 32-byte variables, then great, just copy - // over the calldata! - if (!useLt32) { - programData sub = opcodeify(node.args[1], aux, vaux); - Node nodelist[] = { - token(unsignedToDecimal(totalSz), m), - token("1", m), - token(unsignedToDecimal(nextVarMem), m), - token("CALLDATACOPY", m), - sub.code - }; - inner = pd(sub.aux, multiToken(nodelist, 5, m), 0); - } - else { - std::vector innerList; - int cum = 1; - for (unsigned i = 0; i < varNames.size();) { - // If we get a series of 32-byte values, we calldatacopy them - if (varSizes[i] == 32) { - unsigned until = i+1; - while (until < varNames.size() && varSizes[until] == 32) - until += 1; - innerList.push_back(token(unsignedToDecimal((until - i) * 32), m)); - innerList.push_back(token(unsignedToDecimal(cum), m)); - innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m)); - innerList.push_back(token("CALLDATACOPY", m)); - cum += (until - i) * 32; - i = until; - } - // Otherwise, we do a clever trick to extract the value - else { - innerList.push_back(token(unsignedToDecimal(32 - varSizes[i]), m)); - innerList.push_back(token("256", m)); - innerList.push_back(token("EXP", m)); - innerList.push_back(token(unsignedToDecimal(cum), m)); - innerList.push_back(token("CALLDATALOAD", m)); - innerList.push_back(token("DIV", m)); - innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m)); - innerList.push_back(token("MSTORE", m)); - cum += varSizes[i]; - i += 1; - } - } - // If caller == origin, then it's from a tx, so unpack, otherwise - // plain copy - programData sub = opcodeify(node.args[1], aux, vaux); - Node ilnode = astnode("", innerList, m); - Node nodelist[] = { - token(unsignedToDecimal(32 * varNames.size()), m), - token("1", m), - token(unsignedToDecimal(nextVarMem), m), - token("CALLDATACOPY", m), - token("CALLER", m), - token("ORIGIN", m), - token("EQ", m), - token("ISZERO", m), - token("$maincode"+symb, m), - token("JUMPI", m), - ilnode, - token("~maincode"+symb, m), - token("JUMPDEST", m), - sub.code - }; - inner = pd(sub.aux, multiToken(nodelist, 14, m), 0); - } - // Check if the function call byte is the same - Node nodelist2[] = { - token("0", m), - token("CALLDATALOAD", m), - token("0", m), - token("BYTE", m), - token(unsignedToDecimal(functionCount), m), - token("EQ", m), - token("ISZERO", m), - token("$endcode"+symb, m), - token("JUMPI", m), - inner.code, - token("~endcode"+symb, m), - token("JUMPDEST", m), - }; - return pd(inner.aux, multiToken(nodelist2, 12, m), 0); + if (depth < 0 || depth > 1) err("Stack depth mismatch", m); + return pd(aux, astnode("_", subs2, m), 0); } // Code blocks if (node.val == "lll" && node.args.size() == 2) { @@ -372,49 +322,14 @@ programData opcodeify(Node node, }; return pd(aux, multiToken(nodelist, 8, m), 1); } - // Array literals - else if (node.val == "array_lit") { - aux.allocUsed = true; - std::vector nodes; - if (!node.args.size()) { - nodes.push_back(token("MSIZE", m)); - return pd(aux, astnode("_", nodes, m)); - } - nodes.push_back(token("MSIZE", m)); - nodes.push_back(token("0", m)); - nodes.push_back(token("MSIZE", m)); - nodes.push_back(token(unsignedToDecimal(node.args.size() * 32 - 1), m)); - nodes.push_back(token("ADD", m)); - nodes.push_back(token("MSTORE8", m)); - for (unsigned i = 0; i < node.args.size(); i++) { - Metadata m2 = node.args[i].metadata; - nodes.push_back(token("DUP1", m2)); - programVerticalAux vaux2 = vaux; - vaux2.height += 2; - programData sub = opcodeify(node.args[i], aux, vaux2); - if (!sub.outs) - err("Array_lit item " + unsignedToDecimal(i) + " has zero arity", m2); - aux = sub.aux; - nodes.push_back(sub.code); - nodes.push_back(token("SWAP1", m2)); - if (i > 0) { - nodes.push_back(token(unsignedToDecimal(i * 32), m2)); - nodes.push_back(token("ADD", m2)); - } - nodes.push_back(token("MSTORE", m2)); - } - return pd(aux, astnode("_", nodes, m), 1); - } // All other functions/operators else { std::vector subs2; int depth = opinputs(upperCase(node.val)); - if (node.val != "debug") { - if (depth == -1) - err("Not a function or opcode: "+node.val, m); - if ((int)node.args.size() != depth) - err("Invalid arity for "+node.val, m); - } + if (depth == -1) + err("Not a function or opcode: "+node.val, m); + if ((int)node.args.size() != depth) + err("Invalid arity for "+node.val, m); for (int i = node.args.size() - 1; i >= 0; i--) { programVerticalAux vaux2 = vaux; vaux2.height = vaux.height - i - 1 + node.args.size(); @@ -424,13 +339,8 @@ programData opcodeify(Node node, err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata); subs2.push_back(sub.code); } - if (node.val == "debug") { - subs2.push_back(token("DUP"+unsignedToDecimal(node.args.size()), m)); - for (int i = 0; i <= (int)node.args.size(); i++) - subs2.push_back(token("POP", m)); - } - else subs2.push_back(token(upperCase(node.val), m)); - int outdepth = node.val == "debug" ? 0 : opoutputs(upperCase(node.val)); + subs2.push_back(token(upperCase(node.val), m)); + int outdepth = opoutputs(upperCase(node.val)); return pd(aux, astnode("_", subs2, m), outdepth); } } @@ -449,15 +359,6 @@ Node finalize(programData c) { }; bottom.push_back(multiToken(nodelist, 3, m)); } - // If msg.data is being used as an array, then we need to copy it - if (c.aux.calldataUsed) { - Node nodelist[] = { - token("MSIZE", m), token("CALLDATASIZE", m), token("0", m), - token("MSIZE", m), token("CALLDATACOPY", m), - token(c.aux.vars["'msg.data"], m), token("MSTORE", m) - }; - bottom.push_back(multiToken(nodelist, 7, m)); - } // The actual code bottom.push_back(c.code); return astnode("_", bottom, m); diff --git a/libserpent/opcodes.h b/libserpent/opcodes.h index a7bcc1af9..41423c169 100644 --- a/libserpent/opcodes.h +++ b/libserpent/opcodes.h @@ -5,6 +5,7 @@ #include #include #include +#include "util.h" class Mapping { public: @@ -20,119 +21,25 @@ class Mapping { int out; }; -Mapping mapping[] = { - Mapping("STOP", 0x00, 0, 0), - Mapping("ADD", 0x01, 2, 1), - Mapping("MUL", 0x02, 2, 1), - Mapping("SUB", 0x03, 2, 1), - Mapping("DIV", 0x04, 2, 1), - Mapping("SDIV", 0x05, 2, 1), - Mapping("MOD", 0x06, 2, 1), - Mapping("SMOD", 0x07, 2, 1), - Mapping("ADDMOD", 0x08, 3, 1), - Mapping("MULMOD", 0x09, 3, 1), - Mapping("EXP", 0x0a, 2, 1), - Mapping("SIGNEXTEND", 0x0b, 2, 1), - Mapping("LT", 0x10, 2, 1), - Mapping("GT", 0x11, 2, 1), - Mapping("SLT", 0x12, 2, 1), - Mapping("SGT", 0x13, 2, 1), - Mapping("EQ", 0x14, 2, 1), - Mapping("ISZERO", 0x15, 1, 1), - Mapping("AND", 0x16, 2, 1), - Mapping("OR", 0x17, 2, 1), - Mapping("XOR", 0x18, 2, 1), - Mapping("NOT", 0x19, 1, 1), - Mapping("BYTE", 0x1a, 2, 1), - Mapping("ADDMOD", 0x14, 3, 1), - Mapping("MULMOD", 0x15, 3, 1), - Mapping("SIGNEXTEND", 0x16, 2, 1), - Mapping("SHA3", 0x20, 2, 1), - Mapping("ADDRESS", 0x30, 0, 1), - Mapping("BALANCE", 0x31, 1, 1), - Mapping("ORIGIN", 0x32, 0, 1), - Mapping("CALLER", 0x33, 0, 1), - Mapping("CALLVALUE", 0x34, 0, 1), - Mapping("CALLDATALOAD", 0x35, 1, 1), - Mapping("CALLDATASIZE", 0x36, 0, 1), - Mapping("CALLDATACOPY", 0x37, 3, 1), - Mapping("CODESIZE", 0x38, 0, 1), - Mapping("CODECOPY", 0x39, 3, 1), - Mapping("GASPRICE", 0x3a, 0, 1), - Mapping("PREVHASH", 0x40, 0, 1), - Mapping("COINBASE", 0x41, 0, 1), - Mapping("TIMESTAMP", 0x42, 0, 1), - Mapping("NUMBER", 0x43, 0, 1), - Mapping("DIFFICULTY", 0x44, 0, 1), - Mapping("GASLIMIT", 0x45, 0, 1), - Mapping("POP", 0x50, 1, 0), - Mapping("MLOAD", 0x51, 1, 1), - Mapping("MSTORE", 0x52, 2, 0), - Mapping("MSTORE8", 0x53, 2, 0), - Mapping("SLOAD", 0x54, 1, 1), - Mapping("SSTORE", 0x55, 2, 0), - Mapping("JUMP", 0x56, 1, 0), - Mapping("JUMPI", 0x57, 2, 0), - Mapping("PC", 0x58, 0, 1), - Mapping("MSIZE", 0x59, 0, 1), - Mapping("GAS", 0x5a, 0, 1), - Mapping("JUMPDEST", 0x5b, 0, 0), - Mapping("LOG0", 0xa0, 2, 0), - Mapping("LOG1", 0xa1, 3, 0), - Mapping("LOG2", 0xa2, 4, 0), - Mapping("LOG3", 0xa3, 5, 0), - Mapping("LOG4", 0xa4, 6, 0), - Mapping("CREATE", 0xf0, 3, 1), - Mapping("CALL", 0xf1, 7, 1), - Mapping("RETURN", 0xf2, 2, 0), - Mapping("CALL_CODE", 0xf3, 7, 1), - Mapping("SUICIDE", 0xff, 1, 0), - Mapping("---END---", 0x00, 0, 0), -}; +extern Mapping mapping[]; -std::map > opcodes; -std::map reverseOpcodes; +extern std::map > opcodes; +extern std::map reverseOpcodes; -// Fetches everything EXCEPT PUSH1..32 -std::pair > _opdata(std::string ops, int opi) { - if (!opcodes.size()) { - int i = 0; - while (mapping[i].op != "---END---") { - Mapping mi = mapping[i]; - opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); - i++; - } - for (i = 1; i <= 16; i++) { - opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); - opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); - } - for (std::map >::iterator it=opcodes.begin(); - it != opcodes.end(); - it++) { - reverseOpcodes[(*it).second[0]] = (*it).first; - } - } - std::string op; - std::vector opdata; - op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; - opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); - return std::pair >(op, opdata); -} +std::pair > _opdata(std::string ops, int opi); + +int opcode(std::string op); + +int opinputs(std::string op); + +int opoutputs(std::string op); -int opcode(std::string op) { - return _opdata(op, -1).second[0]; -} +std::string op(int opcode); -int opinputs(std::string op) { - return _opdata(op, -1).second[1]; -} +extern std::string lllSpecials[][3]; -int opoutputs(std::string op) { - return _opdata(op, -1).second[2]; -} +extern std::map > lllMap; -std::string op(int opcode) { - return _opdata("", opcode).first; -} +bool isValidLLLFunc(std::string f, int argc); #endif diff --git a/libserpent/parser.cpp b/libserpent/parser.cpp index 4ceb1d12d..2b9d73702 100644 --- a/libserpent/parser.cpp +++ b/libserpent/parser.cpp @@ -12,17 +12,15 @@ int precedence(Node tok) { if (v == ".") return -1; else if (v == "!" || v == "not") return 1; else if (v=="^" || v == "**") return 2; - else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 3; + else if (v=="*" || v=="/" || v=="%") return 3; else if (v=="+" || v=="-") return 4; else if (v=="<" || v==">" || v=="<=" || v==">=") return 5; - else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 5; else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6; else if (v=="&&" || v=="and") return 7; else if (v=="||" || v=="or") return 8; - else if (v==":") return 9; else if (v=="=") return 10; else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10; - else if (v=="@/=" || v=="@%=") return 10; + else if (v==":" || v == "::") return 11; else return 0; } @@ -223,8 +221,15 @@ Node treefy(std::vector stream) { filename = filename.substr(1, filename.length() - 2); if (!exists(root + filename)) err("File does not exist: "+root + filename, tok.metadata); - oq.back().args.pop_back(); - oq.back().args.push_back(parseSerpent(root + filename)); + if (v == "inset") { + oq.pop_back(); + oq.push_back(parseSerpent(root + filename)); + } + else { + oq.back().args.pop_back(); + oq.back().args.push_back( + asn("outer", parseSerpent(root + filename), tok.metadata)); + } } //Useful for debugging //for (int i = 0; i < oq.size(); i++) { @@ -237,7 +242,7 @@ Node treefy(std::vector stream) { err("Output blank", Metadata()); } else if (oq.size() > 1) { - err("Multiple expressions or unclosed bracket", oq[1].metadata); + return asn("multi", oq, oq[0].metadata); } return oq[0]; @@ -262,15 +267,9 @@ int spaceCount(std::string s) { bool bodied(std::string tok) { return tok == "if" || tok == "elif" || tok == "while" || tok == "with" || tok == "def" || tok == "extern" - || tok == "data"; -} - -// Is this a command that takes an argument as a child block? -bool childBlocked(std::string tok) { - return tok == "if" || tok == "elif" || tok == "else" - || tok == "code" || tok == "shared" || tok == "init" - || tok == "while" || tok == "repeat" || tok == "for" - || tok == "with" || tok == "def"; + || tok == "data" || tok == "assert" || tok == "return" + || tok == "fun" || tok == "scope" || tok == "macro" + || tok == "type"; } // Are the two commands meant to continue each other? @@ -278,10 +277,7 @@ bool bodiedContinued(std::string prev, std::string tok) { return (prev == "if" && tok == "elif") || (prev == "elif" && tok == "else") || (prev == "elif" && tok == "elif") - || (prev == "if" && tok == "else") - || (prev == "init" && tok == "code") - || (prev == "shared" && tok == "code") - || (prev == "shared" && tok == "init"); + || (prev == "if" && tok == "else"); } // Is a line of code empty? @@ -310,16 +306,17 @@ Node parseLines(std::vector lines, Metadata metadata, int sp) { } // Tokenize current line std::vector tokens = tokenize(main.substr(sp), metadata); - // Remove extraneous tokens, including if / elif + // Remove comments std::vector tokens2; for (unsigned j = 0; j < tokens.size(); j++) { if (tokens[j].val == "#" || tokens[j].val == "//") break; - if (j >= 1 || !bodied(tokens[j].val)) { - tokens2.push_back(tokens[j]); - } + tokens2.push_back(tokens[j]); } - if (tokens2.size() > 0 && tokens2.back().val == ":") + bool expectingChildBlock = false; + if (tokens2.size() > 0 && tokens2.back().val == ":") { tokens2.pop_back(); + expectingChildBlock = true; + } // Parse current line Node out = parseSerpentTokenStream(tokens2); // Parse child block @@ -343,14 +340,8 @@ Node parseLines(std::vector lines, Metadata metadata, int sp) { for (unsigned i = 0; i < childBlock.size(); i++) { if (childBlock[i].length() > 0) { cbe = false; break; } } - // Bring back if / elif into AST - if (bodied(tokens[0].val)) { - std::vector args; - args.push_back(out); - out = astnode(tokens[0].val, args, out.metadata); - } // Add child block to AST - if (childBlocked(tokens[0].val)) { + if (expectingChildBlock) { if (cbe) err("Expected indented child block!", out.metadata); out.type = ASTNODE; @@ -360,6 +351,37 @@ Node parseLines(std::vector lines, Metadata metadata, int sp) { } else if (!cbe) err("Did not expect indented child block!", out.metadata); + else if (out.args.size() && out.args[out.args.size() - 1].val == ":") { + Node n = out.args[out.args.size() - 1]; + out.args.pop_back(); + out.args.push_back(n.args[0]); + out.args.push_back(n.args[1]); + } + // Bring back if / elif into AST + if (bodied(tokens[0].val)) { + if (out.val != "multi") { + // token not being used in bodied form + } + else if (out.args[0].val == "id") + out = astnode(tokens[0].val, out.args[1].args, out.metadata); + else if (out.args[0].type == TOKEN) { + std::vector out2; + for (unsigned i = 1; i < out.args.size(); i++) + out2.push_back(out.args[i]); + out = astnode(tokens[0].val, out2, out.metadata); + } + else + out = astnode("fun", out.args, out.metadata); + } + // Multi not supported + if (out.val == "multi") + err("Multiple expressions or unclosed bracket", out.metadata); + // Convert top-level colon expressions into non-colon expressions; + // makes if statements and the like equivalent indented or not + //if (out.val == ":" && out.args[0].type == TOKEN) + // out = asn(out.args[0].val, out.args[1], out.metadata); + //if (bodied(tokens[0].val) && out.args[0].val == ":") + // out = asn(tokens[0].val, out.args[0].args); if (o.size() == 0 || o.back().type == TOKEN) { o.push_back(out); continue; diff --git a/libserpent/rewriter.cpp b/libserpent/rewriter.cpp index 443457acf..294c9a0b3 100644 --- a/libserpent/rewriter.cpp +++ b/libserpent/rewriter.cpp @@ -2,37 +2,25 @@ #include #include #include -#include #include "util.h" #include "lllparser.h" #include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" +#include "functions.h" +#include "opcodes.h" -std::string valid[][3] = { - { "if", "2", "3" }, - { "unless", "2", "2" }, - { "while", "2", "2" }, - { "until", "2", "2" }, - { "alloc", "1", "1" }, - { "array", "1", "1" }, - { "call", "2", tt256 }, - { "call_code", "2", tt256 }, - { "create", "1", "4" }, - { "getch", "2", "2" }, - { "setch", "3", "3" }, - { "sha3", "1", "2" }, - { "return", "1", "2" }, - { "inset", "1", "1" }, - { "min", "2", "2" }, - { "max", "2", "2" }, - { "array_lit", "0", tt256 }, - { "seq", "0", tt256 }, - { "log", "1", "6" }, - { "outer", "1", "1" }, - { "set", "2", "2" }, - { "---END---", "", "" } //Keep this line at the end of the list -}; - +// Rewrite rules std::string macros[][2] = { + { + "(seq $x)", + "$x" + }, + { + "(seq (seq) $x)", + "$x" + }, { "(+= $a $b)", "(set $a (+ $a $b))" @@ -58,24 +46,28 @@ std::string macros[][2] = { "(set $a (^ $a $b))" }, { - "(@/= $a $b)", - "(set $a (@/ $a $b))" + "(!= $a $b)", + "(iszero (eq $a $b))" }, { - "(@%= $a $b)", - "(set $a (@% $a $b))" + "(assert $x)", + "(unless $x (stop))" }, { - "(!= $a $b)", - "(iszero (eq $a $b))" + "(min $a $b)", + "(with $1 $a (with $2 $b (if (lt $1 $2) $1 $2)))" + }, + { + "(max $a $b)", + "(with $1 $a (with $2 $b (if (lt $1 $2) $2 $1)))" }, { - "(min a b)", - "(with $1 a (with $2 b (if (lt $1 $2) $1 $2)))" + "(smin $a $b)", + "(with $1 $a (with $2 $b (if (slt $1 $2) $1 $2)))" }, { - "(max a b)", - "(with $1 a (with $2 b (if (lt $1 $2) $2 $1)))" + "(smax $a $b)", + "(with $1 $a (with $2 $b (if (slt $1 $2) $2 $1)))" }, { "(if $cond $do (else $else))", @@ -85,10 +77,6 @@ std::string macros[][2] = { "(code $code)", "$code" }, - { - "(access (. msg data) $ind)", - "(calldataload (mul 32 $ind))" - }, { "(slice $arr $pos)", "(add $arr (mul 32 $pos))", @@ -125,13 +113,17 @@ std::string macros[][2] = { "(set (access (. self storage) $ind) $val)", "(sstore $ind $val)" }, + { + "(set (sload $ind) $val)", + "(sstore $ind $val)" + }, { "(set (access $var $ind) $val)", "(mstore (add $var (mul 32 $ind)) $val)" }, { "(getch $var $ind)", - "(mod (mload (add $var $ind)) 256)" + "(mod (mload (sub (add $var $ind) 31)) 256)" }, { "(setch $var $ind $val)", @@ -149,6 +141,10 @@ std::string macros[][2] = { "(sha3 $x)", "(seq (set $1 $x) (~sha3 (ref $1) 32))" }, + { + "(sha3 $mstart (= chars $msize))", + "(~sha3 $mstart $msize)" + }, { "(sha3 $mstart $msize)", "(~sha3 $mstart (mul 32 $msize))" @@ -161,6 +157,10 @@ std::string macros[][2] = { "(return $x)", "(seq (set $1 $x) (~return (ref $1) 32))" }, + { + "(return $mstart (= chars $msize))", + "(~return $mstart $msize)" + }, { "(return $start $len)", "(~return $start (mul 32 $len))" @@ -171,7 +171,7 @@ std::string macros[][2] = { }, { "(|| $x $y)", - "(with $1 $x (if (get $1) (get $1) $y))" + "(with $1 $x (if $1 $1 $y))" }, { "(>= $x $y)", @@ -181,45 +181,41 @@ std::string macros[][2] = { "(<= $x $y)", "(iszero (sgt $x $y))" }, - { - "(@>= $x $y)", - "(iszero (lt $x $y))" - }, - { - "(@<= $x $y)", - "(iszero (gt $x $y))" - }, { "(create $code)", "(create 0 $code)" }, { "(create $endowment $code)", - "(with $1 (msize) (create $endowment (get $1) (lll (outer $code) (msize))))" + "(with $1 (msize) (create $endowment (get $1) (lll $code (msize))))" }, { "(sha256 $x)", - "(seq (set $1 $x) (pop (~call 101 2 0 (ref $1) 32 (ref $2) 32)) (get $2))" + "(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 2 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" + }, + { + "(sha256 $arr (= chars $sz))", + "(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr $sz (get $1) 32)) (mload (get $1))))" }, { "(sha256 $arr $sz)", - "(seq (pop (~call 101 2 0 $arr (mul 32 $sz) (ref $2) 32)) (get $2))" + "(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" }, { "(ripemd160 $x)", - "(seq (set $1 $x) (pop (~call 101 3 0 (ref $1) 32 (ref $2) 32)) (get $2))" + "(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 3 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" }, { - "(ripemd160 $arr $sz)", - "(seq (pop (~call 101 3 0 $arr (mul 32 $sz) (ref $2) 32)) (get $2))" + "(ripemd160 $arr (= chars $sz))", + "(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr $sz (mload $1) 32)) (mload (get $1))))" }, { - "(ecrecover $h $v $r $s)", - "(seq (declare $1) (declare $2) (declare $3) (declare $4) (set $1 $h) (set $2 $v) (set $3 $r) (set $4 $s) (pop (~call 101 1 0 (ref $1) 128 (ref $5) 32)) (get $5))" + "(ripemd160 $arr $sz)", + "(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" }, { - "(seq (seq) $x)", - "$x" + "(ecrecover $h $v $r $s)", + "(with $1 (alloc 160) (seq (mstore (get $1) $h) (mstore (add (get $1) 32) $v) (mstore (add (get $1) 64) $r) (mstore (add (get $1) 96) $s) (pop (~call 101 1 0 (get $1) 128 (add (get $1 128)) 32)) (mload (add (get $1) 128))))" }, { "(inset $x)", @@ -235,21 +231,64 @@ std::string macros[][2] = { }, { "(log $t1)", - "(~log1 $t1 0 0)" + "(~log1 0 0 $t1)" }, { "(log $t1 $t2)", - "(~log2 $t1 $t2 0 0)" + "(~log2 0 0 $t1 $t2)" }, { "(log $t1 $t2 $t3)", - "(~log3 $t1 $t2 $t3 0 0)" + "(~log3 0 0 $t1 $t2 $t3)" }, { "(log $t1 $t2 $t3 $t4)", - "(~log4 $t1 $t2 $t3 $t4 0 0)" + "(~log4 0 0 $t1 $t2 $t3 $t4)" + }, + { + "(logarr $a $sz)", + "(~log0 $a (mul 32 $sz))" + }, + { + "(logarr $a $sz $t1)", + "(~log1 $a (mul 32 $sz) $t1)" + }, + { + "(logarr $a $sz $t1 $t2)", + "(~log2 $a (mul 32 $sz) $t1 $t2)" + }, + { + "(logarr $a $sz $t1 $t2 $t3)", + "(~log3 $a (mul 32 $sz) $t1 $t2 $t3)" + }, + { + "(logarr $a $sz $t1 $t2 $t3 $t4)", + "(~log4 $a (mul 32 $sz) $t1 $t2 $t3 $t4)" + }, + { + "(save $loc $array (= chars $count))", + "(with $location (ref $loc) (with $c $count (with $end (div $c 32) (with $i 0 (seq (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))) (sstore (add $i $location) (~and (access $array $i) (sub 0 (exp 256 (sub 32 (mod $c 32)))))))))))" + }, + { + "(save $loc $array $count)", + "(with $location (ref $loc) (with $end $count (with $i 0 (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))))))" + }, + { + "(load $loc (= chars $count))", + "(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i (div $c 32)) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) (set (access $a $i) (~and (sload (add $location $i)) (sub 0 (exp 256 (sub 32 (mod $c 32)))))) $a)))))" + }, + { + "(load $loc $count)", + "(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i $c) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) $a)))))" + }, + { + "(unsafe_mcopy $to $from $sz)", + "(with _sz $sz (with _from $from (with _to $to (seq (comment STARTING UNSAFE MCOPY) (with _i 0 (while (lt _i _sz) (seq (mstore (add $to _i) (mload (add _from _i))) (set _i (add _i 32)))))))))" + }, + { + "(mcopy $to $from $_sz)", + "(with _to $to (with _from $from (with _sz $sz (seq (comment STARTING MCOPY (with _i 0 (seq (while (lt (add _i 31) _sz) (seq (mstore (add _to _i) (mload (add _from _i))) (set _i (add _i 32)))) (with _mask (exp 256 (sub 32 (mod _sz 32))) (mstore (add $to _i) (add (mod (mload (add $to _i)) _mask) (and (mload (add $from _i)) (sub 0 _mask))))))))))))" }, - { "(. msg datasize)", "(div (calldatasize) 32)" }, { "(. msg sender)", "(caller)" }, { "(. msg value)", "(callvalue)" }, { "(. tx gasprice)", "(gasprice)" }, @@ -267,8 +306,7 @@ std::string macros[][2] = { { "---END---", "" } //Keep this line at the end of the list }; -std::vector > nodeMacros; - +// Token synonyms std::string synonyms[][2] = { { "or", "||" }, { "and", "&&" }, @@ -286,10 +324,6 @@ std::string synonyms[][2] = { { "^", "exp" }, { "**", "exp" }, { "%", "smod" }, - { "@/", "div" }, - { "@%", "mod" }, - { "@<", "lt" }, - { "@>", "gt" }, { "<", "slt" }, { ">", "sgt" }, { "=", "set" }, @@ -298,6 +332,10 @@ std::string synonyms[][2] = { { "---END---", "" } //Keep this line at the end of the list }; +std::map synonymMap; + +// Custom setters (need to be registered separately +// for use with managed storage) std::string setters[][2] = { { "+=", "+" }, { "-=", "-" }, @@ -305,550 +343,136 @@ std::string setters[][2] = { { "/=", "/" }, { "%=", "%" }, { "^=", "^" }, - { "!=", "!" }, { "---END---", "" } //Keep this line at the end of the list }; -// Match result storing object -struct matchResult { - bool success; - std::map map; -}; - -// Storage variable index storing object -struct svObj { - std::map offsets; - std::map indices; - std::map > coefficients; - std::map nonfinal; - std::string globalOffset; -}; - -// Preprocessing result storing object -class preprocessAux { - public: - preprocessAux() { - globalExterns = std::map(); - localExterns = std::map >(); - localExterns["self"] = std::map(); - } - std::map globalExterns; - std::map > localExterns; - svObj storageVars; -}; - -#define preprocessResult std::pair - -// Main pattern matching routine, for those patterns that can be expressed -// using our standard mini-language above -// -// Returns two values. First, a boolean to determine whether the node matches -// the pattern, second, if the node does match then a map mapping variables -// in the pattern to nodes -matchResult match(Node p, Node n) { - matchResult o; - o.success = false; - if (p.type == TOKEN) { - if (p.val == n.val && n.type == TOKEN) o.success = true; - else if (p.val[0] == '$') { - o.success = true; - o.map[p.val.substr(1)] = n; - } - } - else if (n.type==TOKEN || p.val!=n.val || p.args.size()!=n.args.size()) { - // do nothing - } - else { - for (unsigned i = 0; i < p.args.size(); i++) { - matchResult oPrime = match(p.args[i], n.args[i]); - if (!oPrime.success) { - o.success = false; - return o; - } - for (std::map::iterator it = oPrime.map.begin(); - it != oPrime.map.end(); - it++) { - o.map[(*it).first] = (*it).second; - } - } - o.success = true; - } - return o; -} - -// Fills in the pattern with a dictionary mapping variable names to -// nodes (these dicts are generated by match). Match and subst together -// create a full pattern-matching engine. -Node subst(Node pattern, - std::map dict, - std::string varflag, - Metadata metadata) { - if (pattern.type == TOKEN && pattern.val[0] == '$') { - if (dict.count(pattern.val.substr(1))) { - return dict[pattern.val.substr(1)]; - } - else { - return token(varflag + pattern.val.substr(1), metadata); - } - } - else if (pattern.type == TOKEN) { - return pattern; - } - else { - std::vector args; - for (unsigned i = 0; i < pattern.args.size(); i++) { - args.push_back(subst(pattern.args[i], dict, varflag, metadata)); - } - return astnode(pattern.val, args, metadata); - } -} +std::map setterMap; // Processes mutable array literals - Node array_lit_transform(Node node) { + std::string prefix = "_temp"+mkUniqueToken() + "_"; Metadata m = node.metadata; - std::vector o1; - o1.push_back(token(unsignedToDecimal(node.args.size() * 32), m)); - std::vector o2; - std::string symb = "_temp"+mkUniqueToken()+"_0"; - o2.push_back(token(symb, m)); - o2.push_back(astnode("alloc", o1, m)); - std::vector o3; - o3.push_back(astnode("set", o2, m)); + std::map d; + std::string o = "(seq (set $arr (alloc "+utd(node.args.size()*32)+"))"; for (unsigned i = 0; i < node.args.size(); i++) { - std::vector o5; - o5.push_back(token(symb, m)); - std::vector o6; - o6.push_back(astnode("get", o5, m)); - o6.push_back(token(unsignedToDecimal(i * 32), m)); - std::vector o7; - o7.push_back(astnode("add", o6)); - o7.push_back(node.args[i]); - o3.push_back(astnode("mstore", o7, m)); - } - std::vector o8; - o8.push_back(token(symb, m)); - o3.push_back(astnode("get", o8)); - return astnode("seq", o3, m); -} - -// Is the given node something of the form -// self.cow -// self.horse[0] -// self.a[6][7][self.storage[3]].chicken[9] -bool isNodeStorageVariable(Node node) { - std::vector nodez; - nodez.push_back(node); - while (1) { - if (nodez.back().type == TOKEN) return false; - if (nodez.back().args.size() == 0) return false; - if (nodez.back().val != "." && nodez.back().val != "access") - return false; - if (nodez.back().args[0].val == "self") return true; - nodez.push_back(nodez.back().args[0]); + o += " (mstore (add (get $arr) "+utd(i * 32)+") $"+utd(i)+")"; + d[utd(i)] = node.args[i]; } + o += " (get $arr))"; + return subst(parseLLL(o), d, prefix, m); } -Node optimize(Node inp); - -Node apply_rules(preprocessResult pr); - -// Convert: -// self.cow -> ["cow"] -// self.horse[0] -> ["horse", "0"] -// self.a[6][7][self.storage[3]].chicken[9] -> -// ["6", "7", (sload 3), "chicken", "9"] -std::vector listfyStorageAccess(Node node) { - std::vector out; - std::vector nodez; - nodez.push_back(node); - while (1) { - if (nodez.back().type == TOKEN) { - out.push_back(token("--" + nodez.back().val, node.metadata)); - std::vector outrev; - for (int i = (signed)out.size() - 1; i >= 0; i--) { - outrev.push_back(out[i]); +// Processes long text literals +Node string_transform(Node node) { + Metadata m = node.metadata; + if (!node.args.size()) + err("Empty text!", m); + if (node.args[0].val.size() < 2 + || node.args[0].val[0] != '"' + || node.args[0].val[node.args[0].val.size() - 1] != '"') + err("Text contents don't look like a string!", m); + std::string bin = node.args[0].val.substr(1, node.args[0].val.size() - 2); + unsigned sz = bin.size(); + std::vector o; + for (unsigned i = 0; i < sz; i += 32) { + std::string t = binToNumeric(bin.substr(i, 32)); + if ((sz - i) < 32 && (sz - i) > 0) { + while ((sz - i) < 32) { + t = decimalMul(t, "256"); + i--; } - return outrev; + i = sz; } - if (nodez.back().val == ".") - nodez.back().args[1].val = "--" + nodez.back().args[1].val; - if (nodez.back().args.size() == 0) - err("Error parsing storage variable statement", node.metadata); - if (nodez.back().args.size() == 1) - out.push_back(token(tt256m1, node.metadata)); - else - out.push_back(nodez.back().args[1]); - nodez.push_back(nodez.back().args[0]); + o.push_back(token(t, node.metadata)); } + node = astnode("array_lit", o, node.metadata); + return array_lit_transform(node); } -// Cool function for debug purposes (named cerrStringList to make -// all prints searchable via 'cerr') -void cerrStringList(std::vector s, std::string suffix="") { - for (unsigned i = 0; i < s.size(); i++) std::cerr << s[i] << " "; - std::cerr << suffix << "\n"; -} -// Populate an svObj with the arguments needed to determine -// the storage position of a node -svObj getStorageVars(svObj pre, Node node, std::string prefix="", int index=0) { - Metadata m = node.metadata; - if (!pre.globalOffset.size()) pre.globalOffset = "0"; - std::vector h; - std::vector coefficients; - // Array accesses or atoms - if (node.val == "access" || node.type == TOKEN) { - std::string tot = "1"; - h = listfyStorageAccess(node); - coefficients.push_back("1"); - for (unsigned i = h.size() - 1; i >= 1; i--) { - // Array sizes must be constant or at least arithmetically - // evaluable at compile time - h[i] = optimize(apply_rules(preprocessResult( - h[i], preprocessAux()))); - if (!isNumberLike(h[i])) - err("Array size must be fixed value", m); - // Create a list of the coefficient associated with each - // array index - coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); - } - } - // Tuples - else { - int startc; - // Handle the (fun args...) case - if (node.val == "fun") { - startc = 1; - h = listfyStorageAccess(node.args[0]); - } - // Handle the ( args...) case, which - // the serpent parser produces when the function - // is a simple name and not a complex astnode - else { - startc = 0; - h = listfyStorageAccess(token(node.val, m)); - } - svObj sub = pre; - sub.globalOffset = "0"; - // Evaluate tuple elements recursively - for (unsigned i = startc; i < node.args.size(); i++) { - sub = getStorageVars(sub, - node.args[i], - prefix+h[0].val.substr(2)+".", - i-1); - } - coefficients.push_back(sub.globalOffset); - for (unsigned i = h.size() - 1; i >= 1; i--) { - // Array sizes must be constant or at least arithmetically - // evaluable at compile time - h[i] = optimize(apply_rules(preprocessResult( - h[i], preprocessAux()))); - if (!isNumberLike(h[i])) - err("Array size must be fixed value", m); - // Create a list of the coefficient associated with each - // array index - coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); - } - pre.offsets = sub.offsets; - pre.coefficients = sub.coefficients; - pre.nonfinal = sub.nonfinal; - pre.nonfinal[prefix+h[0].val.substr(2)] = true; - } - pre.coefficients[prefix+h[0].val.substr(2)] = coefficients; - pre.offsets[prefix+h[0].val.substr(2)] = pre.globalOffset; - pre.indices[prefix+h[0].val.substr(2)] = index; - if (decimalGt(tt176, coefficients.back())) - pre.globalOffset = decimalAdd(pre.globalOffset, coefficients.back()); - return pre; -} +Node apply_rules(preprocessResult pr); -// Transform a node of the form (call to funid vars...) into +// Transform ".(args...)" into // a call - -#define psn std::pair - -Node call_transform(Node node, std::string op) { +Node dotTransform(Node node, preprocessAux aux) { Metadata m = node.metadata; // We're gonna make lots of temporary variables, // so set up a unique flag for them std::string prefix = "_temp"+mkUniqueToken()+"_"; + // Check that the function name is a token + if (node.args[0].args[1].type == ASTNODE) + err("Function name must be static", m); + + Node dotOwner = node.args[0].args[0]; + std::string dotMember = node.args[0].args[1].val; // kwargs = map of special arguments std::map kwargs; kwargs["value"] = token("0", m); - kwargs["gas"] = parseLLL("(- (gas) 25)"); - std::vector args; - for (unsigned i = 0; i < node.args.size(); i++) { - if (node.args[i].val == "=" || node.args[i].val == "set") { - if (node.args[i].args.size() != 2) - err("Malformed set", m); - kwargs[node.args[i].args[0].val] = node.args[i].args[1]; - } - else args.push_back(node.args[i]); - } - if (args.size() < 2) err("Too few arguments for call!", m); - kwargs["to"] = args[0]; - kwargs["funid"] = args[1]; - std::vector inputs; - for (unsigned i = 2; i < args.size(); i++) { - inputs.push_back(args[i]); - } - std::vector with; - std::vector precompute; - std::vector post; - if (kwargs.count("data")) { - if (!kwargs.count("datasz")) err("Required param datasz", m); - // The strategy here is, we store the function ID byte at the index - // before the start of the byte, but then we store the value that was - // there before and reinstate it once the process is over - // store data: data array start - with.push_back(psn(prefix+"data", kwargs["data"])); - // store data: prior: data array - 32 - Node prior = astnode("sub", token(prefix+"data", m), token("32", m), m); - with.push_back(psn(prefix+"prior", prior)); - // store data: priormem: data array - 32 prior memory value - Node priormem = astnode("mload", token(prefix+"prior", m), m); - with.push_back(psn(prefix+"priormem", priormem)); - // post: reinstate prior mem at data array - 32 - post.push_back(astnode("mstore", - token(prefix+"prior", m), - token(prefix+"priormem", m), - m)); - // store data: datastart: data array - 1 - Node datastart = astnode("sub", - token(prefix+"data", m), - token("1", m), - m); - with.push_back(psn(prefix+"datastart", datastart)); - // push funid byte to datastart - precompute.push_back(astnode("mstore8", - token(prefix+"datastart", m), - kwargs["funid"], - m)); - // set data array start loc - kwargs["datain"] = token(prefix+"datastart", m); - kwargs["datainsz"] = astnode("add", - token("1", m), - astnode("mul", - token("32", m), - kwargs["datasz"], - m), - m); - } - else { - // Here, there is no data array, instead there are function arguments. - // This actually lets us be much more efficient with how we set things - // up. - // Pre-declare variables; relies on declared variables being sequential - precompute.push_back(astnode("declare", - token(prefix+"prebyte", m), - m)); - for (unsigned i = 0; i < inputs.size(); i++) { - precompute.push_back(astnode("declare", - token(prefix+unsignedToDecimal(i), m), - m)); - } - // Set up variables to store the function arguments, and store the - // function ID at the byte before the start - Node datastart = astnode("add", - token("31", m), - astnode("ref", - token(prefix+"prebyte", m), - m), - m); - precompute.push_back(astnode("mstore8", - datastart, - kwargs["funid"], - m)); - for (unsigned i = 0; i < inputs.size(); i++) { - precompute.push_back(astnode("set", - token(prefix+unsignedToDecimal(i), m), - inputs[i], - m)); - - } - kwargs["datain"] = datastart; - kwargs["datainsz"] = token(unsignedToDecimal(inputs.size()*32+1), m); - } - if (!kwargs.count("outsz")) { - kwargs["dataout"] = astnode("ref", token(prefix+"dataout", m), m); - kwargs["dataoutsz"] = token("32", node.metadata); - post.push_back(astnode("get", token(prefix+"dataout", m), m)); - } - else { - kwargs["dataout"] = kwargs["out"]; - kwargs["dataoutsz"] = kwargs["outsz"]; - post.push_back(astnode("ref", token(prefix+"dataout", m), m)); - } - // Set up main call - std::vector main; - for (unsigned i = 0; i < precompute.size(); i++) { - main.push_back(precompute[i]); - } - std::vector call; - call.push_back(kwargs["gas"]); - call.push_back(kwargs["to"]); - call.push_back(kwargs["value"]); - call.push_back(kwargs["datain"]); - call.push_back(kwargs["datainsz"]); - call.push_back(kwargs["dataout"]); - call.push_back(kwargs["dataoutsz"]); - main.push_back(astnode("pop", astnode("~"+op, call, m), m)); - for (unsigned i = 0; i < post.size(); i++) { - main.push_back(post[i]); - } - Node mainNode = astnode("seq", main, node.metadata); - // Add with variables - for (int i = with.size() - 1; i >= 0; i--) { - mainNode = astnode("with", - token(with[i].first, m), - with[i].second, - mainNode, - m); - } - return mainNode; -} - -// Preprocess input containing functions -// -// localExterns is a map of the form, eg, -// -// { x: { foo: 0, bar: 1, baz: 2 }, y: { qux: 0, foo: 1 } ... } -// -// Signifying that x.foo = 0, x.baz = 2, y.foo = 1, etc -// -// globalExterns is a one-level map, eg from above -// -// { foo: 1, bar: 1, baz: 2, qux: 0 } -// -// Note that globalExterns may be ambiguous -preprocessResult preprocess(Node inp) { - inp = inp.args[0]; - Metadata m = inp.metadata; - if (inp.val != "seq") { - std::vector args; - args.push_back(inp); - inp = astnode("seq", args, m); - } - std::vector empty; - Node init = astnode("seq", empty, m); - Node shared = astnode("seq", empty, m); - std::vector any; - std::vector functions; - preprocessAux out = preprocessAux(); - out.localExterns["self"] = std::map(); - int functionCount = 0; - int storageDataCount = 0; - for (unsigned i = 0; i < inp.args.size(); i++) { - Node obj = inp.args[i]; - // Functions - if (obj.val == "def") { - if (obj.args.size() == 0) - err("Empty def", m); - std::string funName = obj.args[0].val; - // Init, shared and any are special functions - if (funName == "init" || funName == "shared" || funName == "any") { - if (obj.args[0].args.size()) - err(funName+" cannot have arguments", m); - } - if (funName == "init") init = obj.args[1]; - else if (funName == "shared") shared = obj.args[1]; - else if (funName == "any") any.push_back(obj.args[1]); - else { - // Other functions - functions.push_back(obj); - out.localExterns["self"][obj.args[0].val] = functionCount; - functionCount++; - } - } - // Extern declarations - else if (obj.val == "extern") { - std::string externName = obj.args[0].args[0].val; - Node al = obj.args[0].args[1]; - if (!out.localExterns.count(externName)) - out.localExterns[externName] = std::map(); - for (unsigned i = 0; i < al.args.size(); i++) { - out.globalExterns[al.args[i].val] = i; - out.localExterns[externName][al.args[i].val] = i; - } - } - // Storage variables/structures - else if (obj.val == "data") { - out.storageVars = getStorageVars(out.storageVars, - obj.args[0], - "", - storageDataCount); - storageDataCount += 1; - } - else any.push_back(obj); - } - std::vector main; - if (shared.args.size()) main.push_back(shared); - if (init.args.size()) main.push_back(init); - - std::vector code; - if (shared.args.size()) code.push_back(shared); - for (unsigned i = 0; i < any.size(); i++) - code.push_back(any[i]); - for (unsigned i = 0; i < functions.size(); i++) - code.push_back(functions[i]); - main.push_back(astnode("~return", - token("0", m), - astnode("lll", - astnode("seq", code, m), - token("0", m), - m), - m)); - - - - return preprocessResult(astnode("seq", main, inp.metadata), out); -} - -// Transform ".(args...)" into -// (call args...) -Node dotTransform(Node node, preprocessAux aux) { - Metadata m = node.metadata; - Node pre = node.args[0].args[0]; - std::string post = node.args[0].args[1].val; - if (node.args[0].args[1].type == ASTNODE) - err("Function name must be static", m); - // Search for as=? and call=code keywords + kwargs["gas"] = subst(parseLLL("(- (gas) 25)"), msn(), prefix, m); + // Search for as=? and call=code keywords, and isolate the actual + // function arguments + std::vector fnargs; std::string as = ""; - bool call_code = false; + std::string op = "call"; for (unsigned i = 1; i < node.args.size(); i++) { - Node arg = node.args[i]; + fnargs.push_back(node.args[i]); + Node arg = fnargs.back(); if (arg.val == "=" || arg.val == "set") { if (arg.args[0].val == "as") as = arg.args[1].val; if (arg.args[0].val == "call" && arg.args[1].val == "code") - call_code = true; + op = "callcode"; + if (arg.args[0].val == "gas") + kwargs["gas"] = arg.args[1]; + if (arg.args[0].val == "value") + kwargs["value"] = arg.args[1]; + if (arg.args[0].val == "outsz") + kwargs["outsz"] = arg.args[1]; } } - if (pre.val == "self") { + if (dotOwner.val == "self") { if (as.size()) err("Cannot use \"as\" when calling self!", m); - as = pre.val; + as = dotOwner.val; } - std::vector args; - args.push_back(pre); - // Determine the funId assuming the "as" keyword was used + // Determine the funId and sig assuming the "as" keyword was used + int funId = 0; + std::string sig; if (as.size() > 0 && aux.localExterns.count(as)) { - if (!aux.localExterns[as].count(post)) - err("Invalid call: "+printSimple(pre)+"."+post, m); - std::string funid = unsignedToDecimal(aux.localExterns[as][post]); - args.push_back(token(funid, m)); + if (!aux.localExterns[as].count(dotMember)) + err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + funId = aux.localExterns[as][dotMember]; + sig = aux.localExternSigs[as][dotMember]; } - // Determine the funId otherwise + // Determine the funId and sig otherwise else if (!as.size()) { - if (!aux.globalExterns.count(post)) - err("Invalid call: "+printSimple(pre)+"."+post, m); - std::string key = unsignedToDecimal(aux.globalExterns[post]); - args.push_back(token(key, m)); + if (!aux.globalExterns.count(dotMember)) + err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + std::string key = unsignedToDecimal(aux.globalExterns[dotMember]); + funId = aux.globalExterns[dotMember]; + sig = aux.globalExternSigs[dotMember]; + } + else err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); + // Pack arguments + kwargs["data"] = packArguments(fnargs, sig, funId, m); + kwargs["to"] = dotOwner; + Node main; + // Pack output + if (!kwargs.count("outsz")) { + main = parseLLL( + "(with _data $data (seq " + "(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) (ref $dataout) 32))" + "(get $dataout)))"); + } + else { + main = parseLLL( + "(with _data $data (with _outsz (mul 32 $outsz) (with _out (alloc _outsz) (seq " + "(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) _out _outsz))" + "(get _out)))))"); } - else err("Invalid call: "+printSimple(pre)+"."+post, m); - for (unsigned i = 1; i < node.args.size(); i++) - args.push_back(node.args[i]); - return astnode(call_code ? "call_code" : "call", args, m); + // Set up main call + + Node o = subst(main, kwargs, prefix, m); + return o; } // Transform an access of the form self.bob, self.users[5], etc into @@ -877,7 +501,8 @@ Node dotTransform(Node node, preprocessAux aux) { // obj2[0].a -> sha3([1, 0, 0]) // obj2[5].b[1][3] -> sha3([1, 5, 1, 1, 3]) // obj2[45].c -> sha3([1, 45, 2]) -Node storageTransform(Node node, preprocessAux aux, bool mapstyle=false) { +Node storageTransform(Node node, preprocessAux aux, + bool mapstyle=false, bool ref=false) { Metadata m = node.metadata; // Get a list of all of the "access parameters" used in order // eg. self.users[5].cow[4][m[2]][woof] -> @@ -909,7 +534,7 @@ Node storageTransform(Node node, preprocessAux aux, bool mapstyle=false) { // If the size of an object exceeds 2^176, we make it an infinite // array if (decimalGt(coefficients.back(), tt176) && !mapstyle) - return storageTransform(node, aux, true); + return storageTransform(node, aux, true, ref); offset = decimalAdd(offset, aux.storageVars.offsets[tempPrefix]); c = 0; if (mapstyle) @@ -940,28 +565,29 @@ Node storageTransform(Node node, preprocessAux aux, bool mapstyle=false) { if (c > (signed)coefficients.size() - 1) { err("Too many array index lookups", m); } + Node o; if (mapstyle) { - // We pre-declare variables, relying on the idea that sequentially - // declared variables are doing to appear beside each other in - // memory - std::vector main; + std::string t = "_temp_"+mkUniqueToken(); + std::vector sub; for (unsigned i = 0; i < terms.size(); i++) - main.push_back(astnode("declare", - token(varPrefix+unsignedToDecimal(i), m), - m)); - for (unsigned i = 0; i < terms.size(); i++) - main.push_back(astnode("set", - token(varPrefix+unsignedToDecimal(i), m), - terms[i], - m)); - main.push_back(astnode("ref", token(varPrefix+"0", m), m)); - Node sz = token(unsignedToDecimal(terms.size()), m); - return astnode("sload", - astnode("sha3", - astnode("seq", main, m), - sz, - m), - m); + sub.push_back(asn("mstore", + asn("add", + tkn(utd(i * 32), m), + asn("get", tkn(t+"pos", m), m), + m), + terms[i], + m)); + sub.push_back(tkn(t+"pos", m)); + Node main = asn("with", + tkn(t+"pos", m), + asn("alloc", tkn(utd(terms.size() * 32), m), m), + asn("seq", sub, m), + m); + Node sz = token(utd(terms.size() * 32), m); + o = astnode("~sha3", + main, + sz, + m); } else { // We add up all the index*coefficients @@ -972,42 +598,92 @@ Node storageTransform(Node node, preprocessAux aux, bool mapstyle=false) { temp.push_back(terms[i]); out = astnode("add", temp, node.metadata); } - std::vector temp2; - temp2.push_back(out); - return astnode("sload", temp2, node.metadata); + o = out; + } + if (ref) return o; + else return astnode("sload", o, node.metadata); +} + +// Basic rewrite rule execution +std::pair rulesTransform(Node node, rewriteRuleSet macros) { + std::string prefix = "_temp_"+mkUniqueToken(); + bool changed = false; + if (!macros.ruleLists.count(node.val)) + return std::pair(node, false); + std::vector rules = macros.ruleLists[node.val]; + for (unsigned pos = 0; pos < rules.size(); pos++) { + rewriteRule macro = rules[pos]; + matchResult mr = match(macro.pattern, node); + if (mr.success) { + node = subst(macro.substitution, mr.map, prefix, node.metadata); + std::pair o = rulesTransform(node, macros); + o.second = true; + return o; + } + } + return std::pair(node, changed); +} + +std::pair synonymTransform(Node node) { + bool changed = false; + if (node.type == ASTNODE && synonymMap.count(node.val)) { + node.val = synonymMap[node.val]; + changed = true; } + return std::pair(node, changed); } +rewriteRuleSet nodeMacros; +rewriteRuleSet setterMacros; -// Recursively applies rewrite rules -Node apply_rules(preprocessResult pr) { +bool dontDescend(std::string s) { + return s == "macro" || s == "comment" || s == "outer"; +} + +// Recursively applies any set of rewrite rules +std::pair apply_rules_iter(preprocessResult pr, rewriteRuleSet rules) { + bool changed = false; Node node = pr.first; - // If the rewrite rules have not yet been parsed, parse them - if (!nodeMacros.size()) { - for (int i = 0; i < 9999; i++) { - std::vector o; - if (macros[i][0] == "---END---") break; - o.push_back(parseLLL(macros[i][0])); - o.push_back(parseLLL(macros[i][1])); - nodeMacros.push_back(o); + if (dontDescend(node.val)) + return std::pair(node, false); + std::pair o = rulesTransform(node, rules); + node = o.first; + changed = changed || o.second; + if (node.type == ASTNODE) { + for (unsigned i = 0; i < node.args.size(); i++) { + std::pair r = + apply_rules_iter(preprocessResult(node.args[i], pr.second), rules); + node.args[i] = r.first; + changed = changed || r.second; } } - // Assignment transformations - for (int i = 0; i < 9999; i++) { - if (setters[i][0] == "---END---") break; - if (node.val == setters[i][0]) { - node = astnode("=", - node.args[0], - astnode(setters[i][1], - node.args[0], - node.args[1], - node.metadata), - node.metadata); - } + return std::pair(node, changed); +} + +// Recursively applies rewrite rules and other primary transformations +std::pair mainTransform(preprocessResult pr) { + bool changed = false; + Node node = pr.first; + + // Anything inside "outer" should be treated as a separate program + // and thus recursively compiled in its entirety + if (node.val == "outer") { + node = apply_rules(preprocess(node.args[0])); + changed = true; } + + // Don't descend into comments, macros and inner scopes + if (dontDescend(node.val)) + return std::pair(node, changed); + // Special storage transformation if (isNodeStorageVariable(node)) { node = storageTransform(node, pr.second); + changed = true; + } + if (node.val == "ref" && isNodeStorageVariable(node.args[0])) { + node = storageTransform(node.args[0], pr.second, false, true); + changed = true; } if (node.val == "=" && isNodeStorageVariable(node.args[0])) { Node t = storageTransform(node.args[0], pr.second); @@ -1017,195 +693,213 @@ Node apply_rules(preprocessResult pr) { o.push_back(node.args[1]); node = astnode("sstore", o, node.metadata); } + changed = true; } // Main code - unsigned pos = 0; - std::string prefix = "_temp"+mkUniqueToken()+"_"; - while(1) { - if (synonyms[pos][0] == "---END---") { - break; - } - else if (node.type == ASTNODE && node.val == synonyms[pos][0]) { - node.val = synonyms[pos][1]; - } - pos++; - } - for (pos = 0; pos < nodeMacros.size(); pos++) { - Node pattern = nodeMacros[pos][0]; - matchResult mr = match(pattern, node); - if (mr.success) { - Node pattern2 = nodeMacros[pos][1]; - node = subst(pattern2, mr.map, prefix, node.metadata); - pos = 0; - } - } + std::pair pnb = synonymTransform(node); + node = pnb.first; + changed = changed || pnb.second; + // std::cerr << priority << " " << macros.size() << "\n"; + std::pair pnc = rulesTransform(node, nodeMacros); + node = pnc.first; + changed = changed || pnc.second; + + // Special transformations - if (node.val == "outer") { - pr = preprocess(node); - node = pr.first; - } - if (node.val == "array_lit") + if (node.val == "array_lit") { node = array_lit_transform(node); + changed = true; + } if (node.val == "fun" && node.args[0].val == ".") { node = dotTransform(node, pr.second); + changed = true; + } + if (node.val == "text") { + node = string_transform(node); + changed = true; } - if (node.val == "call") - node = call_transform(node, "call"); - if (node.val == "call_code") - node = call_transform(node, "call_code"); if (node.type == ASTNODE) { unsigned i = 0; + // Arg 0 of all of these is a variable, so should not be changed if (node.val == "set" || node.val == "ref" - || node.val == "get" || node.val == "with" - || node.val == "def" || node.val == "declare") { - node.args[0].val = "'" + node.args[0].val; + || node.val == "get" || node.val == "with") { + if (node.args[0].type == TOKEN && + node.args[0].val.size() > 0 && node.args[0].val[0] != '\'') { + node.args[0].val = "'" + node.args[0].val; + changed = true; + } i = 1; } - if (node.val == "def") { - for (unsigned j = 0; j < node.args[0].args.size(); j++) { - if (node.args[0].args[j].val == ":") { - node.args[0].args[j].val = "kv"; - node.args[0].args[j].args[0].val = - "'" + node.args[0].args[j].args[0].val; - } - else { - node.args[0].args[j].val = "'" + node.args[0].args[j].val; - } - } + // Convert arglen(x) to '_len_x + else if (node.val == "arglen") { + node.val = "get"; + node.args[0].val = "'_len_" + node.args[0].val; + i = 1; + changed = true; } + // Recursively process children for (; i < node.args.size(); i++) { - node.args[i] = - apply_rules(preprocessResult(node.args[i], pr.second)); + std::pair r = + mainTransform(preprocessResult(node.args[i], pr.second)); + node.args[i] = r.first; + changed = changed || r.second; } } + // Add leading ' to variable names, and wrap them inside get else if (node.type == TOKEN && !isNumberLike(node)) { - node.val = "'" + node.val; - std::vector args; - args.push_back(node); - node = astnode("get", args, node.metadata); + if (node.val.size() && node.val[0] != '\'' && node.val[0] != '$') { + Node n = astnode("get", tkn("'"+node.val), node.metadata); + node = n; + changed = true; + } } - // This allows people to use ~x as a way of having functions with the same - // name and arity as macros; the idea is that ~x is a "final" form, and - // should not be remacroed, but it is converted back at the end - if (node.type == ASTNODE && node.val[0] == '~') - node.val = node.val.substr(1); - return node; + // Convert all numbers to normalized form + else if (node.type == TOKEN && isNumberLike(node) && !isDecimal(node.val)) { + node.val = strToNumeric(node.val); + changed = true; + } + return std::pair(node, changed); } -// Compile-time arithmetic calculations -Node optimize(Node inp) { - if (inp.type == TOKEN) { - Node o = tryNumberize(inp); - if (decimalGt(o.val, tt256, true)) - err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata); - return o; +// Do some preprocessing to convert all of our macro lists into compiled +// forms that can then be reused +void parseMacros() { + for (int i = 0; i < 9999; i++) { + std::vector o; + if (macros[i][0] == "---END---") break; + nodeMacros.addRule(rewriteRule( + parseLLL(macros[i][0]), + parseLLL(macros[i][1]) + )); } - for (unsigned i = 0; i < inp.args.size(); i++) { - inp.args[i] = optimize(inp.args[i]); + for (int i = 0; i < 9999; i++) { + std::vector o; + if (setters[i][0] == "---END---") break; + setterMacros.addRule(rewriteRule( + asn(setters[i][0], tkn("$x"), tkn("$y")), + asn("=", tkn("$x"), asn(setters[i][1], tkn("$x"), tkn("$y"))) + )); } - // Degenerate cases for add and mul - if (inp.args.size() == 2) { - if (inp.val == "add" && inp.args[0].type == TOKEN && - inp.args[0].val == "0") { - inp = inp.args[1]; - } - if (inp.val == "add" && inp.args[1].type == TOKEN && - inp.args[1].val == "0") { - inp = inp.args[0]; - } - if (inp.val == "mul" && inp.args[0].type == TOKEN && - inp.args[0].val == "1") { - inp = inp.args[1]; - } - if (inp.val == "mul" && inp.args[1].type == TOKEN && - inp.args[1].val == "1") { - inp = inp.args[0]; - } + for (int i = 0; i < 9999; i++) { + if (synonyms[i][0] == "---END---") break; + synonymMap[synonyms[i][0]] = synonyms[i][1]; + } +} + +Node apply_rules(preprocessResult pr) { + // If the rewrite rules have not yet been parsed, parse them + if (!nodeMacros.ruleLists.size()) parseMacros(); + // Iterate over macros by priority list + std::map::iterator it; + std::pair r; + for(it=pr.second.customMacros.begin(); + it != pr.second.customMacros.end(); it++) { + while (1) { + // std::cerr << "STARTING ARI CYCLE: " << (*it).first <<"\n"; + // std::cerr << printAST(pr.first) << "\n"; + r = apply_rules_iter(pr, (*it).second); + pr.first = r.first; + if (!r.second) break; + } + } + // Apply setter macros + while (1) { + r = apply_rules_iter(pr, setterMacros); + pr.first = r.first; + if (!r.second) break; } - // Arithmetic computation - if (inp.args.size() == 2 - && inp.args[0].type == TOKEN - && inp.args[1].type == TOKEN) { - std::string o; - if (inp.val == "add") { - o = decimalMod(decimalAdd(inp.args[0].val, inp.args[1].val), tt256); - } - else if (inp.val == "sub") { - if (decimalGt(inp.args[0].val, inp.args[1].val, true)) - o = decimalSub(inp.args[0].val, inp.args[1].val); - } - else if (inp.val == "mul") { - o = decimalMod(decimalMul(inp.args[0].val, inp.args[1].val), tt256); - } - else if (inp.val == "div" && inp.args[1].val != "0") { - o = decimalDiv(inp.args[0].val, inp.args[1].val); - } - else if (inp.val == "sdiv" && inp.args[1].val != "0" - && decimalGt(tt255, inp.args[0].val) - && decimalGt(tt255, inp.args[1].val)) { - o = decimalDiv(inp.args[0].val, inp.args[1].val); - } - else if (inp.val == "mod" && inp.args[1].val != "0") { - o = decimalMod(inp.args[0].val, inp.args[1].val); - } - else if (inp.val == "smod" && inp.args[1].val != "0" - && decimalGt(tt255, inp.args[0].val) - && decimalGt(tt255, inp.args[1].val)) { - o = decimalMod(inp.args[0].val, inp.args[1].val); - } - else if (inp.val == "exp") { - o = decimalModExp(inp.args[0].val, inp.args[1].val, tt256); - } - if (o.length()) return token(o, inp.metadata); + // Apply all other mactos + while (1) { + r = mainTransform(pr); + pr.first = r.first; + if (!r.second) break; } - return inp; + return r.first; } +// Pre-validation Node validate(Node inp) { + Metadata m = inp.metadata; if (inp.type == ASTNODE) { int i = 0; - while(valid[i][0] != "---END---") { - if (inp.val == valid[i][0]) { + while(validFunctions[i][0] != "---END---") { + if (inp.val == validFunctions[i][0]) { std::string sz = unsignedToDecimal(inp.args.size()); - if (decimalGt(valid[i][1], sz)) { + if (decimalGt(validFunctions[i][1], sz)) { err("Too few arguments for "+inp.val, inp.metadata); } - if (decimalGt(sz, valid[i][2])) { + if (decimalGt(sz, validFunctions[i][2])) { err("Too many arguments for "+inp.val, inp.metadata); } } i++; } + } + else if (inp.type == TOKEN) { + if (!inp.val.size()) err("??? empty token", m); + if (inp.val[0] == '_') err("Variables cannot start with _", m); } for (unsigned i = 0; i < inp.args.size(); i++) validate(inp.args[i]); return inp; } Node postValidate(Node inp) { + // This allows people to use ~x as a way of having functions with the same + // name and arity as macros; the idea is that ~x is a "final" form, and + // should not be remacroed, but it is converted back at the end + if (inp.val.size() > 0 && inp.val[0] == '~') { + inp.val = inp.val.substr(1); + } if (inp.type == ASTNODE) { if (inp.val == ".") err("Invalid object member (ie. a foo.bar not mapped to anything)", inp.metadata); - for (unsigned i = 0; i < inp.args.size(); i++) - postValidate(inp.args[i]); + else if (opcode(inp.val) >= 0) { + if ((signed)inp.args.size() < opinputs(inp.val)) + err("Too few arguments for "+inp.val, inp.metadata); + if ((signed)inp.args.size() > opinputs(inp.val)) + err("Too many arguments for "+inp.val, inp.metadata); + } + else if (isValidLLLFunc(inp.val, inp.args.size())) { + // do nothing + } + else err ("Invalid argument count or LLL function: "+printSimple(inp), inp.metadata); + for (unsigned i = 0; i < inp.args.size(); i++) { + inp.args[i] = postValidate(inp.args[i]); + } } return inp; } -Node outerWrap(Node inp) { - std::vector args; - args.push_back(inp); - return astnode("outer", args, inp.metadata); + +Node rewriteChunk(Node inp) { + return postValidate(optimize(apply_rules( + preprocessResult( + validate(inp), preprocessAux())))); } -Node rewrite(Node inp) { - return postValidate(optimize(apply_rules(preprocessResult( - validate(outerWrap(inp)), preprocessAux())))); +// Flatten nested sequence into flat sequence +Node flattenSeq(Node inp) { + std::vector o; + if (inp.val == "seq" && inp.type == ASTNODE) { + for (unsigned i = 0; i < inp.args.size(); i++) { + if (inp.args[i].val == "seq" && inp.args[i].type == ASTNODE) + o = extend(o, flattenSeq(inp.args[i]).args); + else + o.push_back(flattenSeq(inp.args[i])); + } + } + else if (inp.type == ASTNODE) { + for (unsigned i = 0; i < inp.args.size(); i++) { + o.push_back(flattenSeq(inp.args[i])); + } + } + else return inp; + return asn(inp.val, o, inp.metadata); } -Node rewriteChunk(Node inp) { - return postValidate(optimize(apply_rules(preprocessResult( - validate(inp), preprocessAux())))); +Node rewrite(Node inp) { + return postValidate(optimize(apply_rules(preprocess(flattenSeq(inp))))); } using namespace std; diff --git a/libserpent/tokenize.cpp b/libserpent/tokenize.cpp index c6a211593..b60cc8a44 100644 --- a/libserpent/tokenize.cpp +++ b/libserpent/tokenize.cpp @@ -13,8 +13,8 @@ int chartype(char c) { if (c >= '0' && c <= '9') return ALPHANUM; else if (c >= 'a' && c <= 'z') return ALPHANUM; else if (c >= 'A' && c <= 'Z') return ALPHANUM; - else if (std::string("~_$").find(c) != std::string::npos) return ALPHANUM; - else if (c == '\t' || c == ' ' || c == '\n') return SPACE; + else if (std::string("~_$@").find(c) != std::string::npos) return ALPHANUM; + else if (c == '\t' || c == ' ' || c == '\n' || c == '\r') return SPACE; else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK; else if (c == '"') return DQUOTE; else if (c == '\'') return SQUOTE; diff --git a/libserpent/util.cpp b/libserpent/util.cpp index fbce5e8b5..5e83c0e41 100644 --- a/libserpent/util.cpp +++ b/libserpent/util.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include "util.h" #include "bignum.h" #include @@ -28,6 +27,11 @@ Node astnode(std::string val, std::vector args, Metadata met) { } //AST node constructors for a specific number of children +Node astnode(std::string val, Metadata met) { + std::vector args; + return astnode(val, args, met); +} + Node astnode(std::string val, Node a, Metadata met) { std::vector args; args.push_back(a); @@ -49,6 +53,16 @@ Node astnode(std::string val, Node a, Node b, Node c, Metadata met) { return astnode(val, args, met); } +Node astnode(std::string val, Node a, Node b, Node c, Node d, Metadata met) { + std::vector args; + args.push_back(a); + args.push_back(b); + args.push_back(c); + args.push_back(d); + return astnode(val, args, met); +} + + // Print token list std::string printTokens(std::vector tokens) { std::string s = ""; @@ -146,6 +160,15 @@ std::string indentLines(std::string inp) { return joinLines(lines); } +// Binary to hexadecimal +std::string binToNumeric(std::string inp) { + std::string o = "0"; + for (unsigned i = 0; i < inp.length(); i++) { + o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); + } + return o; +} + // Converts string to simple numeric format std::string strToNumeric(std::string inp) { std::string o = "0"; @@ -154,7 +177,7 @@ std::string strToNumeric(std::string inp) { } else if ((inp[0] == '"' && inp[inp.length()-1] == '"') || (inp[0] == '\'' && inp[inp.length()-1] == '\'')) { - for (unsigned i = 1; i < inp.length() - 1; i++) { + for (unsigned i = 1; i < inp.length() - 1; i++) { o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); } } @@ -181,6 +204,14 @@ bool isNumberLike(Node node) { return strToNumeric(node.val) != ""; } +// Is the number decimal? +bool isDecimal(std::string inp) { + for (unsigned i = 0; i < inp.length(); i++) { + if (inp[i] < '0' || inp[i] > '9') return false; + } + return true; +} + //Normalizes number representations Node nodeToNumeric(Node node) { std::string o = strToNumeric(node.val); @@ -246,6 +277,14 @@ void err(std::string errtext, Metadata met) { throw(err); } +//Report warning +void warn(std::string errtext, Metadata met) { + std::string err = "Warning (file \"" + met.file + "\", line " + + unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + + "): " + errtext; + std::cerr << err << "\n"; +} + //Bin to hex std::string binToHex(std::string inp) { std::string o = ""; @@ -280,7 +319,15 @@ std::string upperCase(std::string inp) { //Three-int vector std::vector triple(int a, int b, int c) { - std::vector o; - o.push_back(a); o.push_back(b); o.push_back(c); - return o; + std::vector v; + v.push_back(a); + v.push_back(b); + v.push_back(c); + return v; +} + +//Extend node vector +std::vector extend(std::vector a, std::vector b) { + for (unsigned i = 0; i < b.size(); i++) a.push_back(b[i]); + return a; } diff --git a/libserpent/util.h b/libserpent/util.h index c0a2e9324..e25712d0f 100644 --- a/libserpent/util.h +++ b/libserpent/util.h @@ -28,30 +28,36 @@ const int TOKEN = 0, // Stores metadata about each token class Metadata { public: - Metadata(std::string File="main", int Ln=0, int Ch=0) { + Metadata(std::string File="main", int Ln=-1, int Ch=-1) { file = File; ln = Ln; ch = Ch; + fixed = false; } std::string file; int ln; int ch; + bool fixed; }; std::string mkUniqueToken(); // type can be TOKEN or ASTNODE -struct Node { - int type; - std::string val; - std::vector args; - Metadata metadata; +class Node { + public: + int type; + std::string val; + std::vector args; + Metadata metadata; }; Node token(std::string val, Metadata met=Metadata()); Node astnode(std::string val, std::vector args, Metadata met=Metadata()); +Node astnode(std::string val, Metadata met=Metadata()); Node astnode(std::string val, Node a, Metadata met=Metadata()); Node astnode(std::string val, Node a, Node b, Metadata met=Metadata()); Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata()); +Node astnode(std::string val, Node a, Node b, + Node c, Node d, Metadata met=Metadata()); // Number of tokens in a tree int treeSize(Node prog); @@ -74,6 +80,9 @@ std::string joinLines(std::vector lines); // Indent all lines by 4 spaces std::string indentLines(std::string inp); +// Converts binary to simple numeric format +std::string binToNumeric(std::string inp); + // Converts string to simple numeric format std::string strToNumeric(std::string inp); @@ -98,6 +107,9 @@ bool exists(std::string fileName); //Report error void err(std::string errtext, Metadata met); +//Report warning +void warn(std::string errtext, Metadata met); + //Bin to hex std::string binToHex(std::string inp); @@ -110,4 +122,16 @@ std::string upperCase(std::string inp); //Three-int vector std::vector triple(int a, int b, int c); +//Extend node vector +std::vector extend(std::vector a, std::vector b); + +// Is the number decimal? +bool isDecimal(std::string inp); + +#define asn astnode +#define tkn token +#define msi std::map +#define msn std::map +#define mss std::map + #endif diff --git a/sc/cmdline.cpp b/sc/cmdline.cpp index b44d2538c..a5fed37d6 100644 --- a/sc/cmdline.cpp +++ b/sc/cmdline.cpp @@ -10,6 +10,19 @@ int main(int argv, char** argc) { std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n"; return 0; } + if (argv == 2 && (std::string(argc[1]) == "--help" || std::string(argc[1]) == "-h" )) { + std::cout << argc[1] << "\n"; + + std::cout << "serpent command input\n"; + std::cout << "where input -s for from stdin, a file, or interpreted as serpent code if does not exist as file."; + std::cout << "where command: \n"; + std::cout << " parse: Just parses and returns s-expression code.\n"; + std::cout << " rewrite: Parse, use rewrite rules print s-expressions of result.\n"; + std::cout << " compile: Return resulting compiled EVM code in hex.\n"; + std::cout << " assemble: Return result from step before compilation.\n"; + return 0; + } + std::string flag = ""; std::string command = argc[1]; std::string input; From c4ed74cc8ba923a2c15ac4f4e04962df5eec3727 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Dec 2014 16:27:07 +0100 Subject: [PATCH 11/21] Additional serpent files. --- libserpent/functions.cpp | 203 ++++++++++++++++++++++ libserpent/functions.h | 39 +++++ libserpent/opcodes.cpp | 154 +++++++++++++++++ libserpent/optimize.cpp | 98 +++++++++++ libserpent/optimize.h | 19 +++ libserpent/preprocess.cpp | 327 ++++++++++++++++++++++++++++++++++++ libserpent/preprocess.h | 50 ++++++ libserpent/rewriteutils.cpp | 211 +++++++++++++++++++++++ libserpent/rewriteutils.h | 76 +++++++++ 9 files changed, 1177 insertions(+) create mode 100644 libserpent/functions.cpp create mode 100644 libserpent/functions.h create mode 100644 libserpent/opcodes.cpp create mode 100644 libserpent/optimize.cpp create mode 100644 libserpent/optimize.h create mode 100644 libserpent/preprocess.cpp create mode 100644 libserpent/preprocess.h create mode 100644 libserpent/rewriteutils.cpp create mode 100644 libserpent/rewriteutils.h diff --git a/libserpent/functions.cpp b/libserpent/functions.cpp new file mode 100644 index 000000000..78e12e84a --- /dev/null +++ b/libserpent/functions.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" +#include "functions.h" + +std::string getSignature(std::vector args) { + std::string o; + for (unsigned i = 0; i < args.size(); i++) { + if (args[i].val == ":" && args[i].args[1].val == "s") + o += "s"; + else if (args[i].val == ":" && args[i].args[1].val == "a") + o += "a"; + else + o += "i"; + } + return o; +} + +// Convert a list of arguments into a node containing a +// < datastart, datasz > pair + +Node packArguments(std::vector args, std::string sig, + int funId, Metadata m) { + // Plain old 32 byte arguments + std::vector nargs; + // Variable-sized arguments + std::vector vargs; + // Variable sizes + std::vector sizes; + // Is a variable an array? + std::vector isArray; + // Fill up above three argument lists + int argCount = 0; + for (unsigned i = 0; i < args.size(); i++) { + Metadata m = args[i].metadata; + if (args[i].val == "=") { + // do nothing + } + else { + // Determine the correct argument type + char argType; + if (sig.size() > 0) { + if (argCount >= (signed)sig.size()) + err("Too many args", m); + argType = sig[argCount]; + } + else argType = 'i'; + // Integer (also usable for short strings) + if (argType == 'i') { + if (args[i].val == ":") + err("Function asks for int, provided string or array", m); + nargs.push_back(args[i]); + } + // Long string + else if (argType == 's') { + if (args[i].val != ":") + err("Must specify string length", m); + vargs.push_back(args[i].args[0]); + sizes.push_back(args[i].args[1]); + isArray.push_back(false); + } + // Array + else if (argType == 'a') { + if (args[i].val != ":") + err("Must specify array length", m); + vargs.push_back(args[i].args[0]); + sizes.push_back(args[i].args[1]); + isArray.push_back(true); + } + else err("Invalid arg type in signature", m); + argCount++; + } + } + int static_arg_size = 1 + (vargs.size() + nargs.size()) * 32; + // Start off by saving the size variables and calculating the total + msn kwargs; + kwargs["funid"] = tkn(utd(funId), m); + std::string pattern = + "(with _sztot "+utd(static_arg_size)+" " + " (with _sizes (alloc "+utd(sizes.size() * 32)+") " + " (seq "; + for (unsigned i = 0; i < sizes.size(); i++) { + std::string sizeIncrement = + isArray[i] ? "(mul 32 _x)" : "_x"; + pattern += + "(with _x $sz"+utd(i)+"(seq " + " (mstore (add _sizes "+utd(i * 32)+") _x) " + " (set _sztot (add _sztot "+sizeIncrement+" )))) "; + kwargs["sz"+utd(i)] = sizes[i]; + } + // Allocate memory, and set first data byte + pattern += + "(with _datastart (alloc (add _sztot 32)) (seq " + " (mstore8 _datastart $funid) "; + // Copy over size variables + for (unsigned i = 0; i < sizes.size(); i++) { + int v = 1 + i * 32; + pattern += + " (mstore " + " (add _datastart "+utd(v)+") " + " (mload (add _sizes "+utd(v-1)+"))) "; + } + // Store normal arguments + for (unsigned i = 0; i < nargs.size(); i++) { + int v = 1 + (i + sizes.size()) * 32; + pattern += + " (mstore (add _datastart "+utd(v)+") $"+utd(i)+") "; + kwargs[utd(i)] = nargs[i]; + } + // Loop through variable-sized arguments, store them + pattern += + " (with _pos (add _datastart "+utd(static_arg_size)+") (seq"; + for (unsigned i = 0; i < vargs.size(); i++) { + std::string copySize = + isArray[i] ? "(mul 32 (mload (add _sizes "+utd(i * 32)+")))" + : "(mload (add _sizes "+utd(i * 32)+"))"; + pattern += + " (unsafe_mcopy _pos $vl"+utd(i)+" "+copySize+") " + " (set _pos (add _pos "+copySize+")) "; + kwargs["vl"+utd(i)] = vargs[i]; + } + // Return a 2-item array containing the start and size + pattern += " (array_lit _datastart _sztot))))))))"; + std::string prefix = "_temp_"+mkUniqueToken(); + // Fill in pattern, return triple + return subst(parseLLL(pattern), kwargs, prefix, m); +} + +// Create a node for argument unpacking +Node unpackArguments(std::vector vars, Metadata m) { + std::vector varNames; + std::vector longVarNames; + std::vector longVarIsArray; + // Fill in variable and long variable names, as well as which + // long variables are arrays and which are strings + for (unsigned i = 0; i < vars.size(); i++) { + if (vars[i].val == ":") { + if (vars[i].args.size() != 2) + err("Malformed def!", m); + longVarNames.push_back(vars[i].args[0].val); + std::string tag = vars[i].args[1].val; + if (tag == "s") + longVarIsArray.push_back(false); + else if (tag == "a") + longVarIsArray.push_back(true); + else + err("Function value can only be string or array", m); + } + else { + varNames.push_back(vars[i].val); + } + } + std::vector sub; + if (!varNames.size() && !longVarNames.size()) { + // do nothing if we have no arguments + } + else { + std::vector varNodes; + for (unsigned i = 0; i < longVarNames.size(); i++) + varNodes.push_back(token(longVarNames[i], m)); + for (unsigned i = 0; i < varNames.size(); i++) + varNodes.push_back(token(varNames[i], m)); + // Copy over variable lengths and short variables + for (unsigned i = 0; i < varNodes.size(); i++) { + int pos = 1 + i * 32; + std::string prefix = (i < longVarNames.size()) ? "_len_" : ""; + sub.push_back(asn("untyped", asn("set", + token(prefix+varNodes[i].val, m), + asn("calldataload", tkn(utd(pos), m), m), + m))); + } + // Copy over long variables + if (longVarNames.size() > 0) { + std::vector sub2; + int pos = varNodes.size() * 32 + 1; + Node tot = tkn("_tot", m); + for (unsigned i = 0; i < longVarNames.size(); i++) { + Node var = tkn(longVarNames[i], m); + Node varlen = longVarIsArray[i] + ? asn("mul", tkn("32", m), tkn("_len_"+longVarNames[i], m)) + : tkn("_len_"+longVarNames[i], m); + sub2.push_back(asn("untyped", + asn("set", var, asn("alloc", varlen)))); + sub2.push_back(asn("calldatacopy", var, tot, varlen)); + sub2.push_back(asn("set", tot, asn("add", tot, varlen))); + } + std::string prefix = "_temp_"+mkUniqueToken(); + sub.push_back(subst( + astnode("with", tot, tkn(utd(pos), m), asn("seq", sub2)), + msn(), + prefix, + m)); + } + } + return asn("seq", sub, m); +} diff --git a/libserpent/functions.h b/libserpent/functions.h new file mode 100644 index 000000000..68a1c69ce --- /dev/null +++ b/libserpent/functions.h @@ -0,0 +1,39 @@ +#ifndef ETHSERP_FUNCTIONS +#define ETHSERP_FUNCTIONS + +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "optimize.h" +#include "rewriteutils.h" +#include "preprocess.h" + + +class argPack { + public: + argPack(Node a, Node b, Node c) { + pre = a; + datastart = b; + datasz = c; + } + Node pre; + Node datastart; + Node datasz; +}; + +// Get a signature from a function +std::string getSignature(std::vector args); + +// Convert a list of arguments into a node +// triple, given the signature of a function +Node packArguments(std::vector args, std::string sig, + int funId, Metadata m); + +// Create a node for argument unpacking +Node unpackArguments(std::vector vars, Metadata m); + +#endif diff --git a/libserpent/opcodes.cpp b/libserpent/opcodes.cpp new file mode 100644 index 000000000..b24144e46 --- /dev/null +++ b/libserpent/opcodes.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include "opcodes.h" +#include "util.h" +#include "bignum.h" + +Mapping mapping[] = { + Mapping("STOP", 0x00, 0, 0), + Mapping("ADD", 0x01, 2, 1), + Mapping("MUL", 0x02, 2, 1), + Mapping("SUB", 0x03, 2, 1), + Mapping("DIV", 0x04, 2, 1), + Mapping("SDIV", 0x05, 2, 1), + Mapping("MOD", 0x06, 2, 1), + Mapping("SMOD", 0x07, 2, 1), + Mapping("ADDMOD", 0x08, 3, 1), + Mapping("MULMOD", 0x09, 3, 1), + Mapping("EXP", 0x0a, 2, 1), + Mapping("SIGNEXTEND", 0x0b, 2, 1), + Mapping("LT", 0x10, 2, 1), + Mapping("GT", 0x11, 2, 1), + Mapping("SLT", 0x12, 2, 1), + Mapping("SGT", 0x13, 2, 1), + Mapping("EQ", 0x14, 2, 1), + Mapping("ISZERO", 0x15, 1, 1), + Mapping("AND", 0x16, 2, 1), + Mapping("OR", 0x17, 2, 1), + Mapping("XOR", 0x18, 2, 1), + Mapping("NOT", 0x19, 1, 1), + Mapping("BYTE", 0x1a, 2, 1), + Mapping("SHA3", 0x20, 2, 1), + Mapping("ADDRESS", 0x30, 0, 1), + Mapping("BALANCE", 0x31, 1, 1), + Mapping("ORIGIN", 0x32, 0, 1), + Mapping("CALLER", 0x33, 0, 1), + Mapping("CALLVALUE", 0x34, 0, 1), + Mapping("CALLDATALOAD", 0x35, 1, 1), + Mapping("CALLDATASIZE", 0x36, 0, 1), + Mapping("CALLDATACOPY", 0x37, 3, 0), + Mapping("CODESIZE", 0x38, 0, 1), + Mapping("CODECOPY", 0x39, 3, 0), + Mapping("GASPRICE", 0x3a, 0, 1), + Mapping("EXTCODESIZE", 0x3b, 1, 1), + Mapping("EXTCODECOPY", 0x3c, 4, 0), + Mapping("PREVHASH", 0x40, 0, 1), + Mapping("COINBASE", 0x41, 0, 1), + Mapping("TIMESTAMP", 0x42, 0, 1), + Mapping("NUMBER", 0x43, 0, 1), + Mapping("DIFFICULTY", 0x44, 0, 1), + Mapping("GASLIMIT", 0x45, 0, 1), + Mapping("POP", 0x50, 1, 0), + Mapping("MLOAD", 0x51, 1, 1), + Mapping("MSTORE", 0x52, 2, 0), + Mapping("MSTORE8", 0x53, 2, 0), + Mapping("SLOAD", 0x54, 1, 1), + Mapping("SSTORE", 0x55, 2, 0), + Mapping("JUMP", 0x56, 1, 0), + Mapping("JUMPI", 0x57, 2, 0), + Mapping("PC", 0x58, 0, 1), + Mapping("MSIZE", 0x59, 0, 1), + Mapping("GAS", 0x5a, 0, 1), + Mapping("JUMPDEST", 0x5b, 0, 0), + Mapping("LOG0", 0xa0, 2, 0), + Mapping("LOG1", 0xa1, 3, 0), + Mapping("LOG2", 0xa2, 4, 0), + Mapping("LOG3", 0xa3, 5, 0), + Mapping("LOG4", 0xa4, 6, 0), + Mapping("CREATE", 0xf0, 3, 1), + Mapping("CALL", 0xf1, 7, 1), + Mapping("CALLCODE", 0xf2, 7, 1), + Mapping("RETURN", 0xf3, 2, 0), + Mapping("SUICIDE", 0xff, 1, 0), + Mapping("---END---", 0x00, 0, 0), +}; + +std::map > opcodes; +std::map reverseOpcodes; + +// Fetches everything EXCEPT PUSH1..32 +std::pair > _opdata(std::string ops, int opi) { + if (!opcodes.size()) { + int i = 0; + while (mapping[i].op != "---END---") { + Mapping mi = mapping[i]; + opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); + i++; + } + for (i = 1; i <= 16; i++) { + opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); + opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); + } + for (std::map >::iterator it=opcodes.begin(); + it != opcodes.end(); + it++) { + reverseOpcodes[(*it).second[0]] = (*it).first; + } + } + ops = upperCase(ops); + std::string op; + std::vector opdata; + op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; + opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); + return std::pair >(op, opdata); +} + +int opcode(std::string op) { + return _opdata(op, -1).second[0]; +} + +int opinputs(std::string op) { + return _opdata(op, -1).second[1]; +} + +int opoutputs(std::string op) { + return _opdata(op, -1).second[2]; +} + +std::string op(int opcode) { + return _opdata("", opcode).first; +} + +std::string lllSpecials[][3] = { + { "ref", "1", "1" }, + { "get", "1", "1" }, + { "set", "2", "2" }, + { "with", "3", "3" }, + { "comment", "0", "2147483647" }, + { "ops", "0", "2147483647" }, + { "lll", "2", "2" }, + { "seq", "0", "2147483647" }, + { "if", "3", "3" }, + { "unless", "2", "2" }, + { "until", "2", "2" }, + { "alloc", "1", "1" }, + { "---END---", "0", "0" }, +}; + +std::map > lllMap; + +// Is a function name one of the valid functions above? +bool isValidLLLFunc(std::string f, int argc) { + if (lllMap.size() == 0) { + for (int i = 0; ; i++) { + if (lllSpecials[i][0] == "---END---") break; + lllMap[lllSpecials[i][0]] = std::pair( + dtu(lllSpecials[i][1]), dtu(lllSpecials[i][2])); + } + } + return lllMap.count(f) + && argc >= lllMap[f].first + && argc <= lllMap[f].second; +} diff --git a/libserpent/optimize.cpp b/libserpent/optimize.cpp new file mode 100644 index 000000000..e689fcb69 --- /dev/null +++ b/libserpent/optimize.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" + +// Compile-time arithmetic calculations +Node optimize(Node inp) { + if (inp.type == TOKEN) { + Node o = tryNumberize(inp); + if (decimalGt(o.val, tt256, true)) + err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata); + return o; + } + for (unsigned i = 0; i < inp.args.size(); i++) { + inp.args[i] = optimize(inp.args[i]); + } + // Arithmetic-specific transform + if (inp.val == "+") inp.val = "add"; + if (inp.val == "*") inp.val = "mul"; + if (inp.val == "-") inp.val = "sub"; + if (inp.val == "/") inp.val = "sdiv"; + if (inp.val == "^") inp.val = "exp"; + if (inp.val == "**") inp.val = "exp"; + if (inp.val == "%") inp.val = "smod"; + // Degenerate cases for add and mul + if (inp.args.size() == 2) { + if (inp.val == "add" && inp.args[0].type == TOKEN && + inp.args[0].val == "0") { + Node x = inp.args[1]; + inp = x; + } + if (inp.val == "add" && inp.args[1].type == TOKEN && + inp.args[1].val == "0") { + Node x = inp.args[0]; + inp = x; + } + if (inp.val == "mul" && inp.args[0].type == TOKEN && + inp.args[0].val == "1") { + Node x = inp.args[1]; + inp = x; + } + if (inp.val == "mul" && inp.args[1].type == TOKEN && + inp.args[1].val == "1") { + Node x = inp.args[0]; + inp = x; + } + } + // Arithmetic computation + if (inp.args.size() == 2 + && inp.args[0].type == TOKEN + && inp.args[1].type == TOKEN) { + std::string o; + if (inp.val == "add") { + o = decimalMod(decimalAdd(inp.args[0].val, inp.args[1].val), tt256); + } + else if (inp.val == "sub") { + if (decimalGt(inp.args[0].val, inp.args[1].val, true)) + o = decimalSub(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "mul") { + o = decimalMod(decimalMul(inp.args[0].val, inp.args[1].val), tt256); + } + else if (inp.val == "div" && inp.args[1].val != "0") { + o = decimalDiv(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "sdiv" && inp.args[1].val != "0" + && decimalGt(tt255, inp.args[0].val) + && decimalGt(tt255, inp.args[1].val)) { + o = decimalDiv(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "mod" && inp.args[1].val != "0") { + o = decimalMod(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "smod" && inp.args[1].val != "0" + && decimalGt(tt255, inp.args[0].val) + && decimalGt(tt255, inp.args[1].val)) { + o = decimalMod(inp.args[0].val, inp.args[1].val); + } + else if (inp.val == "exp") { + o = decimalModExp(inp.args[0].val, inp.args[1].val, tt256); + } + if (o.length()) return token(o, inp.metadata); + } + return inp; +} + +// Is a node degenerate (ie. trivial to calculate) ? +bool isDegenerate(Node n) { + return optimize(n).type == TOKEN; +} + +// Is a node purely arithmetic? +bool isPureArithmetic(Node n) { + return isNumberLike(optimize(n)); +} diff --git a/libserpent/optimize.h b/libserpent/optimize.h new file mode 100644 index 000000000..06ea3bba1 --- /dev/null +++ b/libserpent/optimize.h @@ -0,0 +1,19 @@ +#ifndef ETHSERP_OPTIMIZER +#define ETHSERP_OPTIMIZER + +#include +#include +#include +#include +#include "util.h" + +// Compile-time arithmetic calculations +Node optimize(Node inp); + +// Is a node degenerate (ie. trivial to calculate) ? +bool isDegenerate(Node n); + +// Is a node purely arithmetic? +bool isPureArithmetic(Node n); + +#endif diff --git a/libserpent/preprocess.cpp b/libserpent/preprocess.cpp new file mode 100644 index 000000000..2df149945 --- /dev/null +++ b/libserpent/preprocess.cpp @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "rewriteutils.h" +#include "optimize.h" +#include "preprocess.h" +#include "functions.h" +#include "opcodes.h" + +// Convert a function of the form (def (f x y z) (do stuff)) into +// (if (first byte of ABI is correct) (seq (setup x y z) (do stuff))) +Node convFunction(Node node, int functionCount) { + std::string prefix = "_temp"+mkUniqueToken()+"_"; + Metadata m = node.metadata; + + if (node.args.size() != 2) + err("Malformed def!", m); + // Collect the list of variable names and variable byte counts + Node unpack = unpackArguments(node.args[0].args, m); + // And the actual code + Node body = node.args[1]; + // Main LLL-based function body + return astnode("if", + astnode("eq", + astnode("get", token("__funid", m), m), + token(unsignedToDecimal(functionCount), m), + m), + astnode("seq", unpack, body, m)); +} + +// Populate an svObj with the arguments needed to determine +// the storage position of a node +svObj getStorageVars(svObj pre, Node node, std::string prefix, + int index) { + Metadata m = node.metadata; + if (!pre.globalOffset.size()) pre.globalOffset = "0"; + std::vector h; + std::vector coefficients; + // Array accesses or atoms + if (node.val == "access" || node.type == TOKEN) { + std::string tot = "1"; + h = listfyStorageAccess(node); + coefficients.push_back("1"); + for (unsigned i = h.size() - 1; i >= 1; i--) { + // Array sizes must be constant or at least arithmetically + // evaluable at compile time + if (!isPureArithmetic(h[i])) + err("Array size must be fixed value", m); + // Create a list of the coefficient associated with each + // array index + coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); + } + } + // Tuples + else { + int startc; + // Handle the (fun args...) case + if (node.val == "fun") { + startc = 1; + h = listfyStorageAccess(node.args[0]); + } + // Handle the ( args...) case, which + // the serpent parser produces when the function + // is a simple name and not a complex astnode + else { + startc = 0; + h = listfyStorageAccess(token(node.val, m)); + } + svObj sub = pre; + sub.globalOffset = "0"; + // Evaluate tuple elements recursively + for (unsigned i = startc; i < node.args.size(); i++) { + sub = getStorageVars(sub, + node.args[i], + prefix+h[0].val.substr(2)+".", + i-startc); + } + coefficients.push_back(sub.globalOffset); + for (unsigned i = h.size() - 1; i >= 1; i--) { + // Array sizes must be constant or at least arithmetically + // evaluable at compile time + if (!isPureArithmetic(h[i])) + err("Array size must be fixed value", m); + // Create a list of the coefficient associated with each + // array index + coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); + } + pre.offsets = sub.offsets; + pre.coefficients = sub.coefficients; + pre.nonfinal = sub.nonfinal; + pre.nonfinal[prefix+h[0].val.substr(2)] = true; + } + pre.coefficients[prefix+h[0].val.substr(2)] = coefficients; + pre.offsets[prefix+h[0].val.substr(2)] = pre.globalOffset; + pre.indices[prefix+h[0].val.substr(2)] = index; + if (decimalGt(tt176, coefficients.back())) + pre.globalOffset = decimalAdd(pre.globalOffset, coefficients.back()); + return pre; +} + +// Preprocess input containing functions +// +// localExterns is a map of the form, eg, +// +// { x: { foo: 0, bar: 1, baz: 2 }, y: { qux: 0, foo: 1 } ... } +// +// localExternSigs is a map of the form, eg, +// +// { x : { foo: iii, bar: iis, baz: ia }, y: { qux: i, foo: as } ... } +// +// Signifying that x.foo = 0, x.baz = 2, y.foo = 1, etc +// and that x.foo has three integers as arguments, x.bar has two +// integers and a variable-length string, and baz has an integer +// and an array +// +// globalExterns is a one-level map, eg from above +// +// { foo: 1, bar: 1, baz: 2, qux: 0 } +// +// globalExternSigs is a one-level map, eg from above +// +// { foo: as, bar: iis, baz: ia, qux: i} +// +// Note that globalExterns and globalExternSigs may be ambiguous +// Also, a null signature implies an infinite tail of integers +preprocessResult preprocessInit(Node inp) { + Metadata m = inp.metadata; + if (inp.val != "seq") + inp = astnode("seq", inp, m); + std::vector empty = std::vector(); + Node init = astnode("seq", empty, m); + Node shared = astnode("seq", empty, m); + std::vector any; + std::vector functions; + preprocessAux out = preprocessAux(); + out.localExterns["self"] = std::map(); + int functionCount = 0; + int storageDataCount = 0; + for (unsigned i = 0; i < inp.args.size(); i++) { + Node obj = inp.args[i]; + // Functions + if (obj.val == "def") { + if (obj.args.size() == 0) + err("Empty def", m); + std::string funName = obj.args[0].val; + // Init, shared and any are special functions + if (funName == "init" || funName == "shared" || funName == "any") { + if (obj.args[0].args.size()) + err(funName+" cannot have arguments", m); + } + if (funName == "init") init = obj.args[1]; + else if (funName == "shared") shared = obj.args[1]; + else if (funName == "any") any.push_back(obj.args[1]); + else { + // Other functions + functions.push_back(convFunction(obj, functionCount)); + out.localExterns["self"][obj.args[0].val] = functionCount; + out.localExternSigs["self"][obj.args[0].val] + = getSignature(obj.args[0].args); + functionCount++; + } + } + // Extern declarations + else if (obj.val == "extern") { + std::string externName = obj.args[0].val; + Node al = obj.args[1]; + if (!out.localExterns.count(externName)) + out.localExterns[externName] = std::map(); + for (unsigned i = 0; i < al.args.size(); i++) { + if (al.args[i].val == ":") { + std::string v = al.args[i].args[0].val; + std::string sig = al.args[i].args[1].val; + out.globalExterns[v] = i; + out.globalExternSigs[v] = sig; + out.localExterns[externName][v] = i; + out.localExternSigs[externName][v] = sig; + } + else { + std::string v = al.args[i].val; + out.globalExterns[v] = i; + out.globalExternSigs[v] = ""; + out.localExterns[externName][v] = i; + out.localExternSigs[externName][v] = ""; + } + } + } + // Custom macros + else if (obj.val == "macro" || (obj.val == "fun" && obj.args[0].val == "macro")) { + // Rules for valid macros: + // + // There are only four categories of valid macros: + // + // 1. a macro where the outer function is something + // which is NOT an existing valid function/extern/datum + // 2. a macro of the form set(c(x), d) where c must NOT + // be an existing valid function/extern/datum + // 3. something of the form access(c(x)), where c must NOT + // be an existing valid function/extern/datum + // 4. something of the form set(access(c(x)), d) where c must + // NOT be an existing valid function/extern/datum + // 5. something of the form with(c(x), d, e) where c must + // NOT be an existing valid function/extern/datum + bool valid = false; + Node pattern; + Node substitution; + int priority; + // Priority not set: default zero + if (obj.val == "macro") { + pattern = obj.args[0]; + substitution = obj.args[1]; + priority = 0; + } + // Specified priority + else { + pattern = obj.args[1]; + substitution = obj.args[2]; + if (obj.args[0].args.size()) + priority = dtu(obj.args[0].args[0].val); + else + priority = 0; + } + if (opcode(pattern.val) < 0 && !isValidFunctionName(pattern.val)) + valid = true; + if (pattern.val == "set" && + opcode(pattern.args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].val)) + valid = true; + if (pattern.val == "access" && + opcode(pattern.args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].val)) + if (pattern.val == "set" && + pattern.args[0].val == "access" && + opcode(pattern.args[0].args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].args[0].val)) + valid = true; + if (pattern.val == "with" && + opcode(pattern.args[0].val) < 0 && + !isValidFunctionName(pattern.args[0].val)) + valid = true; + if (valid) { + if (!out.customMacros.count(priority)) + out.customMacros[priority] = rewriteRuleSet(); + out.customMacros[priority].addRule + (rewriteRule(pattern, substitution)); + } + else warn("Macro does not fit valid template: "+printSimple(pattern), m); + } + // Variable types + else if (obj.val == "type") { + std::string typeName = obj.args[0].val; + std::vector vars = obj.args[1].args; + for (unsigned i = 0; i < vars.size(); i++) + out.types[vars[i].val] = typeName; + } + // Storage variables/structures + else if (obj.val == "data") { + out.storageVars = getStorageVars(out.storageVars, + obj.args[0], + "", + storageDataCount); + storageDataCount += 1; + } + else any.push_back(obj); + } + // Set up top-level AST structure + std::vector main; + if (shared.args.size()) main.push_back(shared); + if (init.args.size()) main.push_back(init); + + std::vector code; + if (shared.args.size()) code.push_back(shared); + for (unsigned i = 0; i < any.size(); i++) + code.push_back(any[i]); + for (unsigned i = 0; i < functions.size(); i++) + code.push_back(functions[i]); + Node codeNode; + if (functions.size() > 0) { + codeNode = astnode("with", + token("__funid", m), + astnode("byte", + token("0", m), + astnode("calldataload", token("0", m), m), + m), + astnode("seq", code, m), + m); + } + else codeNode = astnode("seq", code, m); + main.push_back(astnode("~return", + token("0", m), + astnode("lll", + codeNode, + token("0", m), + m), + m)); + + + Node result; + if (main.size() == 1) result = main[0]; + else result = astnode("seq", main, inp.metadata); + return preprocessResult(result, out); +} + +preprocessResult processTypes (preprocessResult pr) { + preprocessAux aux = pr.second; + Node node = pr.first; + if (node.type == TOKEN && aux.types.count(node.val)) + node = asn(aux.types[node.val], node, node.metadata); + else if (node.val == "untyped") + return preprocessResult(node.args[0], aux); + else if (node.val == "outer") + return preprocessResult(node, aux); + else { + for (unsigned i = 0; i < node.args.size(); i++) { + node.args[i] = + processTypes(preprocessResult(node.args[i], aux)).first; + } + } + return preprocessResult(node, aux); +} + +preprocessResult preprocess(Node n) { + return processTypes(preprocessInit(n)); +} diff --git a/libserpent/preprocess.h b/libserpent/preprocess.h new file mode 100644 index 000000000..321fb8527 --- /dev/null +++ b/libserpent/preprocess.h @@ -0,0 +1,50 @@ +#ifndef ETHSERP_PREPROCESSOR +#define ETHSERP_PREPROCESSOR + +#include +#include +#include +#include +#include "util.h" +#include "rewriteutils.h" + +// Storage variable index storing object +struct svObj { + std::map offsets; + std::map indices; + std::map > coefficients; + std::map nonfinal; + std::string globalOffset; +}; + + + +// Preprocessing result storing object +class preprocessAux { + public: + preprocessAux() { + globalExterns = std::map(); + localExterns = std::map >(); + localExterns["self"] = std::map(); + } + std::map globalExterns; + std::map globalExternSigs; + std::map > localExterns; + std::map > localExternSigs; + std::map customMacros; + std::map types; + svObj storageVars; +}; + +#define preprocessResult std::pair + +// Populate an svObj with the arguments needed to determine +// the storage position of a node +svObj getStorageVars(svObj pre, Node node, std::string prefix="", + int index=0); + +// Preprocess a function (see cpp for details) +preprocessResult preprocess(Node inp); + + +#endif diff --git a/libserpent/rewriteutils.cpp b/libserpent/rewriteutils.cpp new file mode 100644 index 000000000..0d810bdbc --- /dev/null +++ b/libserpent/rewriteutils.cpp @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include "util.h" +#include "lllparser.h" +#include "bignum.h" +#include "rewriteutils.h" +#include "optimize.h" + +// Valid functions and their min and max argument counts +std::string validFunctions[][3] = { + { "if", "2", "3" }, + { "unless", "2", "2" }, + { "while", "2", "2" }, + { "until", "2", "2" }, + { "alloc", "1", "1" }, + { "array", "1", "1" }, + { "call", "2", tt256 }, + { "callcode", "2", tt256 }, + { "create", "1", "4" }, + { "getch", "2", "2" }, + { "setch", "3", "3" }, + { "sha3", "1", "2" }, + { "return", "1", "2" }, + { "inset", "1", "1" }, + { "min", "2", "2" }, + { "max", "2", "2" }, + { "array_lit", "0", tt256 }, + { "seq", "0", tt256 }, + { "log", "1", "6" }, + { "outer", "1", "1" }, + { "set", "2", "2" }, + { "get", "1", "1" }, + { "ref", "1", "1" }, + { "declare", "1", tt256 }, + { "with", "3", "3" }, + { "outer", "1", "1" }, + { "mcopy", "3", "3" }, + { "unsafe_mcopy", "3", "3" }, + { "save", "3", "3" }, + { "load", "2", "2" }, + { "---END---", "", "" } //Keep this line at the end of the list +}; + +std::map vfMap; + +// Is a function name one of the valid functions above? +bool isValidFunctionName(std::string f) { + if (vfMap.size() == 0) { + for (int i = 0; ; i++) { + if (validFunctions[i][0] == "---END---") break; + vfMap[validFunctions[i][0]] = true; + } + } + return vfMap.count(f); +} + +// Cool function for debug purposes (named cerrStringList to make +// all prints searchable via 'cerr') +void cerrStringList(std::vector s, std::string suffix) { + for (unsigned i = 0; i < s.size(); i++) std::cerr << s[i] << " "; + std::cerr << suffix << "\n"; +} + +// Convert: +// self.cow -> ["cow"] +// self.horse[0] -> ["horse", "0"] +// self.a[6][7][self.storage[3]].chicken[9] -> +// ["6", "7", (sload 3), "chicken", "9"] +std::vector listfyStorageAccess(Node node) { + std::vector out; + std::vector nodez; + nodez.push_back(node); + while (1) { + if (nodez.back().type == TOKEN) { + out.push_back(token("--" + nodez.back().val, node.metadata)); + std::vector outrev; + for (int i = (signed)out.size() - 1; i >= 0; i--) { + outrev.push_back(out[i]); + } + return outrev; + } + if (nodez.back().val == ".") + nodez.back().args[1].val = "--" + nodez.back().args[1].val; + if (nodez.back().args.size() == 0) + err("Error parsing storage variable statement", node.metadata); + if (nodez.back().args.size() == 1) + out.push_back(token(tt256m1, node.metadata)); + else + out.push_back(nodez.back().args[1]); + nodez.push_back(nodez.back().args[0]); + } +} + +// Is the given node something of the form +// self.cow +// self.horse[0] +// self.a[6][7][self.storage[3]].chicken[9] +bool isNodeStorageVariable(Node node) { + std::vector nodez; + nodez.push_back(node); + while (1) { + if (nodez.back().type == TOKEN) return false; + if (nodez.back().args.size() == 0) return false; + if (nodez.back().val != "." && nodez.back().val != "access") + return false; + if (nodez.back().args[0].val == "self") return true; + nodez.push_back(nodez.back().args[0]); + } +} + +// Main pattern matching routine, for those patterns that can be expressed +// using our standard mini-language above +// +// Returns two values. First, a boolean to determine whether the node matches +// the pattern, second, if the node does match then a map mapping variables +// in the pattern to nodes +matchResult match(Node p, Node n) { + matchResult o; + o.success = false; + if (p.type == TOKEN) { + if (p.val == n.val && n.type == TOKEN) o.success = true; + else if (p.val[0] == '$' || p.val[0] == '@') { + o.success = true; + o.map[p.val.substr(1)] = n; + } + } + else if (n.type==TOKEN || p.val!=n.val || p.args.size()!=n.args.size()) { + // do nothing + } + else { + for (unsigned i = 0; i < p.args.size(); i++) { + matchResult oPrime = match(p.args[i], n.args[i]); + if (!oPrime.success) { + o.success = false; + return o; + } + for (std::map::iterator it = oPrime.map.begin(); + it != oPrime.map.end(); + it++) { + o.map[(*it).first] = (*it).second; + } + } + o.success = true; + } + return o; +} + + +// Fills in the pattern with a dictionary mapping variable names to +// nodes (these dicts are generated by match). Match and subst together +// create a full pattern-matching engine. +Node subst(Node pattern, + std::map dict, + std::string varflag, + Metadata m) { + // Swap out patterns at the token level + if (pattern.metadata.ln == -1) + pattern.metadata = m; + if (pattern.type == TOKEN && + pattern.val[0] == '$') { + if (dict.count(pattern.val.substr(1))) { + return dict[pattern.val.substr(1)]; + } + else { + return token(varflag + pattern.val.substr(1), m); + } + } + // Other tokens are untouched + else if (pattern.type == TOKEN) { + return pattern; + } + // Substitute recursively for ASTs + else { + std::vector args; + for (unsigned i = 0; i < pattern.args.size(); i++) { + args.push_back(subst(pattern.args[i], dict, varflag, m)); + } + return asn(pattern.val, args, m); + } +} + +// Transforms a sequence containing two-argument with statements +// into a statement containing those statements in nested form +Node withTransform (Node source) { + Node o = token("--"); + Metadata m = source.metadata; + std::vector args; + for (int i = source.args.size() - 1; i >= 0; i--) { + Node a = source.args[i]; + if (a.val == "with" && a.args.size() == 2) { + std::vector flipargs; + for (int j = args.size() - 1; j >= 0; j--) + flipargs.push_back(args[i]); + if (o.val != "--") + flipargs.push_back(o); + o = asn("with", a.args[0], a.args[1], asn("seq", flipargs, m), m); + args = std::vector(); + } + else { + args.push_back(a); + } + } + std::vector flipargs; + for (int j = args.size() - 1; j >= 0; j--) + flipargs.push_back(args[j]); + if (o.val != "--") + flipargs.push_back(o); + return asn("seq", flipargs, m); +} diff --git a/libserpent/rewriteutils.h b/libserpent/rewriteutils.h new file mode 100644 index 000000000..3a9a837ad --- /dev/null +++ b/libserpent/rewriteutils.h @@ -0,0 +1,76 @@ +#ifndef ETHSERP_REWRITEUTILS +#define ETHSERP_REWRITEUTILS + +#include +#include +#include +#include +#include "util.h" + +// Valid functions and their min and max argument counts +extern std::string validFunctions[][3]; + +extern std::map vfMap; + +bool isValidFunctionName(std::string f); + +// Converts deep array access into ordered list of the arguments +// along the descent +std::vector listfyStorageAccess(Node node); + +// Cool function for debug purposes (named cerrStringList to make +// all prints searchable via 'cerr') +void cerrStringList(std::vector s, std::string suffix=""); + +// Is the given node something of the form +// self.cow +// self.horse[0] +// self.a[6][7][self.storage[3]].chicken[9] +bool isNodeStorageVariable(Node node); + +// Applies rewrite rules adding without wrapper +Node rewriteChunk(Node inp); + +// Match result storing object +struct matchResult { + bool success; + std::map map; +}; + +// Match node to pattern +matchResult match(Node p, Node n); + +// Substitute node using pattern +Node subst(Node pattern, + std::map dict, + std::string varflag, + Metadata m); + +Node withTransform(Node source); + +class rewriteRule { + public: + rewriteRule(Node p, Node s) { + pattern = p; + substitution = s; + } + Node pattern; + Node substitution; +}; + +class rewriteRuleSet { + public: + rewriteRuleSet() { + ruleLists = std::map >(); + } + void addRule(rewriteRule r) { + if (!ruleLists.count(r.pattern.val)) + ruleLists[r.pattern.val] = std::vector(); + ruleLists[r.pattern.val].push_back(r); + } + std::map > ruleLists; +}; + + + +#endif From 0b5b6c7cd40adc20944067bca42a9434c70a51d1 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 16:27:17 +0100 Subject: [PATCH 12/21] Adressing some natspec issues --- libsolidity/InterfaceHandler.cpp | 4 ++-- libsolidity/Scanner.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 9ae284b40..5b5682ff1 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -170,7 +170,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const bool _appending) { auto nlPos = std::find(_pos, _end, '\n'); - if (_appending && *_pos != ' ') + if (_appending && _pos < _end && *_pos != ' ') _tagString += " "; std::copy(_pos, nlPos, back_inserter(_tagString)); m_lastTag = _tagType; @@ -204,7 +204,7 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); auto pair = m_params.back(); - if (*_pos != ' ') + if (_pos < _end && *_pos != ' ') pair.second += " "; auto nlPos = std::find(_pos, _end, '\n'); std::copy(_pos, nlPos, back_inserter(pair.second)); diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 69b61ce4b..90a3b4545 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -328,6 +328,7 @@ Token::Value Scanner::scanMultiLineDocComment() void Scanner::scanToken() { + int savedPosition; m_nextToken.literal.clear(); m_nextSkippedComment.literal.clear(); Token::Value token; @@ -428,6 +429,7 @@ void Scanner::scanToken() break; case '/': // / // /* /= + savedPosition = getSourcePos(); advance(); if (m_char == '/') { @@ -436,7 +438,7 @@ void Scanner::scanToken() else if (m_char == '/') { Token::Value comment; - m_nextSkippedComment.location.start = getSourcePos(); + m_nextSkippedComment.location.start = savedPosition; comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; @@ -447,12 +449,13 @@ void Scanner::scanToken() } else if (m_char == '*') { + // /** doxygent style natspec comment if (!advance()) /* slash star comment before EOS */ token = Token::WHITESPACE; else if (m_char == '*') { Token::Value comment; - m_nextSkippedComment.location.start = getSourcePos(); + m_nextSkippedComment.location.start = savedPosition; comment = scanMultiLineDocComment(); m_nextSkippedComment.location.end = getSourcePos(); m_nextSkippedComment.token = comment; From 73593674419a4c2e2aea7a03a554b5577aa31405 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 16:48:25 +0100 Subject: [PATCH 13/21] More multiline natspec tests and small issue fix --- libsolidity/Scanner.cpp | 4 ++-- test/solidityScanner.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 90a3b4545..124c88d92 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -212,7 +212,7 @@ bool Scanner::skipWhitespace() bool Scanner::skipWhitespaceExceptLF() { int const startPosition = getSourcePos(); - while (m_char == ' ' || m_char == '\t') + while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) advance(); // Return whether or not we skipped any characters. return getSourcePos() != startPosition; @@ -305,7 +305,7 @@ Token::Value Scanner::scanMultiLineDocComment() endFound = true; break; } - else + else if (charsAdded) addCommentLiteralChar('\n'); } diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 159e53055..355ea9e22 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -189,6 +189,30 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); } +BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) +{ + Scanner scanner(CharStream("some other tokens /**\n" + " Send $(value / 1000) chocolates to the user\n" + "*/")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); +} + +BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) +{ + Scanner scanner(CharStream("some other tokens /** \t \r \n" + "\t \r * Send $(value / 1000) chocolates to the user\n" + "*/")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); +} + BOOST_AUTO_TEST_CASE(comment_before_eos) { Scanner scanner(CharStream("//")); From b162e267acee2654e000a71af05b5d93db0d613f Mon Sep 17 00:00:00 2001 From: ethdev Date: Thu, 18 Dec 2014 17:10:00 +0100 Subject: [PATCH 14/21] windows fixes --- libserpent/compiler.cpp | 2 +- libserpent/rewriteutils.cpp | 1 + libserpent/util.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libserpent/compiler.cpp b/libserpent/compiler.cpp index a3e5b1c60..886eee6df 100644 --- a/libserpent/compiler.cpp +++ b/libserpent/compiler.cpp @@ -173,7 +173,7 @@ programData opcodeify(Node node, } // Comments do nothing else if (node.val == "comment") { - Node nodelist[] = { }; + Node* nodelist = nullptr; return pd(aux, multiToken(nodelist, 0, m), 0); } // Custom operation sequence diff --git a/libserpent/rewriteutils.cpp b/libserpent/rewriteutils.cpp index 0d810bdbc..e6429434a 100644 --- a/libserpent/rewriteutils.cpp +++ b/libserpent/rewriteutils.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "util.h" #include "lllparser.h" #include "bignum.h" diff --git a/libserpent/util.cpp b/libserpent/util.cpp index 5e83c0e41..4b99a5ea5 100644 --- a/libserpent/util.cpp +++ b/libserpent/util.cpp @@ -5,6 +5,7 @@ #include "util.h" #include "bignum.h" #include +#include #include //Token or value node constructor From d14ed2d4dc29afcce8c16a0173d7a4e87b2c5974 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Dec 2014 17:04:20 +0100 Subject: [PATCH 15/21] Bit operators should bind more strongly than comparison operators. --- libsolidity/Token.h | 20 ++++++++++---------- test/SolidityNameAndTypeResolution.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 32656096a..897d6eaaa 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -107,9 +107,9 @@ namespace solidity T(COMMA, ",", 1) \ T(OR, "||", 4) \ T(AND, "&&", 5) \ - T(BIT_OR, "|", 6) \ - T(BIT_XOR, "^", 7) \ - T(BIT_AND, "&", 8) \ + T(BIT_OR, "|", 8) \ + T(BIT_XOR, "^", 9) \ + T(BIT_AND, "&", 10) \ T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ @@ -122,13 +122,13 @@ namespace solidity /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ - T(EQ, "==", 9) \ - T(NE, "!=", 9) \ - T(LT, "<", 10) \ - T(GT, ">", 10) \ - T(LTE, "<=", 10) \ - T(GTE, ">=", 10) \ - K(IN, "in", 10) \ + T(EQ, "==", 6) \ + T(NE, "!=", 6) \ + T(LT, "<", 7) \ + T(GT, ">", 7) \ + T(LTE, "<=", 7) \ + T(GTE, ">=", 7) \ + K(IN, "in", 7) \ \ /* Unary operators. */ \ /* IsUnaryOp() relies on this block of enum values */ \ diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index 0bda0a1fd..c7f0b3ab8 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -311,6 +311,16 @@ BOOST_AUTO_TEST_CASE(forward_function_reference) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) +{ + char const* text = "contract First {\n" + " function fun() returns (bool ret) {\n" + " return 1 & 2 == 8 & 9;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() } From 49f60a1d6d0b93fad28cea57e03beb261d9f09b1 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 17:30:10 +0100 Subject: [PATCH 16/21] Factoring forward slash scanning out to its own function --- libsolidity/Scanner.cpp | 85 ++++++++++++++++++++++------------------- libsolidity/Scanner.h | 2 + 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 124c88d92..f22e69bc8 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -326,9 +326,52 @@ Token::Value Scanner::scanMultiLineDocComment() return Token::COMMENT_LITERAL; } +Token::Value Scanner::scanSlash() +{ + int firstSlashPosition = getSourcePos(); + advance(); + if (m_char == '/') + { + if (!advance()) /* double slash comment directly before EOS */ + return Token::WHITESPACE; + else if (m_char == '/') + { + // doxygen style /// comment + Token::Value comment; + m_nextSkippedComment.location.start = firstSlashPosition; + comment = scanSingleLineDocComment(); + m_nextSkippedComment.location.end = getSourcePos(); + m_nextSkippedComment.token = comment; + return Token::WHITESPACE; + } + else + return skipSingleLineComment(); + } + else if (m_char == '*') + { + // doxygen style /** natspec comment + if (!advance()) /* slash star comment before EOS */ + return Token::WHITESPACE; + else if (m_char == '*') + { + Token::Value comment; + m_nextSkippedComment.location.start = firstSlashPosition; + comment = scanMultiLineDocComment(); + m_nextSkippedComment.location.end = getSourcePos(); + m_nextSkippedComment.token = comment; + return Token::WHITESPACE; + } + else + return skipMultiLineComment(); + } + else if (m_char == '=') + return selectToken(Token::ASSIGN_DIV); + else + return Token::DIV; +} + void Scanner::scanToken() { - int savedPosition; m_nextToken.literal.clear(); m_nextSkippedComment.literal.clear(); Token::Value token; @@ -429,45 +472,7 @@ void Scanner::scanToken() break; case '/': // / // /* /= - savedPosition = getSourcePos(); - advance(); - if (m_char == '/') - { - if (!advance()) /* double slash comment directly before EOS */ - token = Token::WHITESPACE; - else if (m_char == '/') - { - Token::Value comment; - m_nextSkippedComment.location.start = savedPosition; - comment = scanSingleLineDocComment(); - m_nextSkippedComment.location.end = getSourcePos(); - m_nextSkippedComment.token = comment; - token = Token::WHITESPACE; - } - else - token = skipSingleLineComment(); - } - else if (m_char == '*') - { - // /** doxygent style natspec comment - if (!advance()) /* slash star comment before EOS */ - token = Token::WHITESPACE; - else if (m_char == '*') - { - Token::Value comment; - m_nextSkippedComment.location.start = savedPosition; - comment = scanMultiLineDocComment(); - m_nextSkippedComment.location.end = getSourcePos(); - m_nextSkippedComment.token = comment; - token = Token::WHITESPACE; - } - else - token = skipMultiLineComment(); - } - else if (m_char == '=') - token = selectToken(Token::ASSIGN_DIV); - else - token = Token::DIV; + token = scanSlash(); break; case '&': // & && &= diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 5e70db51d..5b90a94eb 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -194,6 +194,8 @@ private: Token::Value scanString(); Token::Value scanSingleLineDocComment(); Token::Value scanMultiLineDocComment(); + /// Scans a slash '/' and depending on the characters returns the appropriate token + Token::Value scanSlash(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern From ae994f28043065ef9ff8c3a2835a69389a495029 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Dec 2014 17:49:11 +0100 Subject: [PATCH 17/21] Also test non-equality comparison operator. --- test/SolidityNameAndTypeResolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index c7f0b3ab8..25bff71f7 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) { char const* text = "contract First {\n" " function fun() returns (bool ret) {\n" - " return 1 & 2 == 8 & 9;\n" + " return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6;\n" " }\n" "}\n"; BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); From 4165e45464eaec9ed8f62d2571f2a10111599376 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 18 Dec 2014 18:16:43 +0100 Subject: [PATCH 18/21] updating solidity parser natspec tests to comply with recent changes --- test/SolidityParser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index f978cdd9b..c14c05122 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(function_natspec_documentation) BOOST_REQUIRE_NO_THROW(contract = parseText(text)); auto functions = contract->getDefinedFunctions(); BOOST_REQUIRE_NO_THROW(function = functions.at(0)); - BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is a test function"); + BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is a test function"); } BOOST_AUTO_TEST_CASE(function_normal_comments) @@ -166,17 +166,17 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation) auto functions = contract->getDefinedFunctions(); BOOST_REQUIRE_NO_THROW(function = functions.at(0)); - BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 1"); + BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 1"); BOOST_REQUIRE_NO_THROW(function = functions.at(1)); - BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 2"); + BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 2"); BOOST_REQUIRE_NO_THROW(function = functions.at(2)); BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr, "Should not have gotten natspec comment for functionName3()"); BOOST_REQUIRE_NO_THROW(function = functions.at(3)); - BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 4"); + BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 4"); } BOOST_AUTO_TEST_CASE(multiline_function_documentation) @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(multiline_function_documentation) BOOST_REQUIRE_NO_THROW(function = functions.at(0)); BOOST_CHECK_EQUAL(*function->getDocumentation(), - " This is a test function\n" + "This is a test function\n" " and it has 2 lines"); } @@ -220,11 +220,11 @@ BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body) auto functions = contract->getDefinedFunctions(); BOOST_REQUIRE_NO_THROW(function = functions.at(0)); - BOOST_CHECK_EQUAL(*function->getDocumentation(), " fun1 description"); + BOOST_CHECK_EQUAL(*function->getDocumentation(), "fun1 description"); BOOST_REQUIRE_NO_THROW(function = functions.at(1)); BOOST_CHECK_EQUAL(*function->getDocumentation(), - " This is a test function\n" + "This is a test function\n" " and it has 2 lines"); } From 672c1ca15b5d749609ef8c68267fe58e9a69e6e8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Dec 2014 18:44:55 +0100 Subject: [PATCH 19/21] Version bump. --- libdevcore/Common.cpp | 2 +- libethcore/CommonEth.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index 55250b418..1de7ef957 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.7.12"; +char const* Version = "0.7.13"; } diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 5e510572e..ff5e6aed0 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -32,7 +32,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 49; +const unsigned c_protocolVersion = 50; const unsigned c_databaseVersion = 5; static const vector> g_units = From e427a0a2cafa5ecd4ea3872aadd2b5f19eb705cb Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 18 Dec 2014 22:15:11 +0100 Subject: [PATCH 20/21] Bugfix: Additional swap for compound assignment. --- libsolidity/ExpressionCompiler.cpp | 2 ++ test/SolidityEndToEndTest.cpp | 35 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index f58c157d9..cf641935a 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -62,6 +62,8 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_currentLValue.retrieveValue(_assignment, true); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); + if (m_currentLValue.storesReferenceOnStack()) + m_context << eth::Instruction::SWAP1; } m_currentLValue.storeValue(_assignment); m_currentLValue.reset(); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index aa74f8186..9559e3702 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -504,6 +504,41 @@ BOOST_AUTO_TEST_CASE(state_smoke_test) BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x3))); } +BOOST_AUTO_TEST_CASE(compound_assign) +{ + char const* sourceCode = "contract test {\n" + " uint value1;\n" + " uint value2;\n" + " function f(uint x, uint y) returns (uint w) {\n" + " uint value3 = y;" + " value1 += x;\n" + " value3 *= x;" + " value2 *= value3 + value1;\n" + " return value2 += 7;" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + u256 value1; + u256 value2; + auto f = [&](u256 const& _x, u256 const& _y) -> u256 + { + u256 value3 = _y; + value1 += _x; + value3 *= _x; + value2 *= value3 + value1; + return value2 += 7; + }; + testSolidityAgainstCpp(0, f, u256(0), u256(6)); + testSolidityAgainstCpp(0, f, u256(1), u256(3)); + testSolidityAgainstCpp(0, f, u256(2), u256(25)); + testSolidityAgainstCpp(0, f, u256(3), u256(69)); + testSolidityAgainstCpp(0, f, u256(4), u256(84)); + testSolidityAgainstCpp(0, f, u256(5), u256(2)); + testSolidityAgainstCpp(0, f, u256(6), u256(51)); + testSolidityAgainstCpp(0, f, u256(7), u256(48)); +} + BOOST_AUTO_TEST_CASE(simple_mapping) { char const* sourceCode = "contract test {\n" From 29261206b9293c2e07b2b879aff059bc57e5d1cb Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 19 Dec 2014 10:48:59 +0100 Subject: [PATCH 21/21] Adding const attribute to ABI output --- libsolidity/InterfaceHandler.cpp | 1 + test/SolidityABIJSON.cpp | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 5b5682ff1..60971fbb9 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -56,6 +56,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio }; method["name"] = f->getName(); + method["const"] = f->isDeclaredConst(); method["inputs"] = populateParameters(f->getParameters()); method["outputs"] = populateParameters(f->getReturnParameters()); methods.append(method); diff --git a/test/SolidityABIJSON.cpp b/test/SolidityABIJSON.cpp index c734009c3..62ab0458c 100644 --- a/test/SolidityABIJSON.cpp +++ b/test/SolidityABIJSON.cpp @@ -76,6 +76,7 @@ BOOST_AUTO_TEST_CASE(basic_test) char const* interface = R"([ { "name": "f", + "const": false, "inputs": [ { "name": "a", @@ -114,6 +115,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) char const* interface = R"([ { "name": "f", + "const": false, "inputs": [ { "name": "a", @@ -129,6 +131,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) }, { "name": "g", + "const": false, "inputs": [ { "name": "b", @@ -156,6 +159,7 @@ BOOST_AUTO_TEST_CASE(multiple_params) char const* interface = R"([ { "name": "f", + "const": false, "inputs": [ { "name": "a", @@ -189,6 +193,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) char const* interface = R"([ { "name": "c", + "const": false, "inputs": [ { "name": "b", @@ -204,6 +209,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) }, { "name": "f", + "const": false, "inputs": [ { "name": "a", @@ -222,6 +228,53 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) checkInterface(sourceCode, interface); } +BOOST_AUTO_TEST_CASE(const_function) +{ + char const* sourceCode = "contract test {\n" + " function foo(uint a, uint b) returns(uint d) { return a + b; }\n" + " function boo(uint32 a) const returns(uint b) { return a * 4; }\n" + "}\n"; + + char const* interface = R"([ + { + "name": "boo", + "const": true, + "inputs": [{ + "name": "a", + "type": "uint32" + }], + "outputs": [ + { + "name": "b", + "type": "uint256" + } + ] + }, + { + "name": "foo", + "const": false, + "inputs": [ + { + "name": "a", + "type": "uint256" + }, + { + "name": "b", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "d", + "type": "uint256" + } + ] + } + ])"; + + checkInterface(sourceCode, interface); +} + BOOST_AUTO_TEST_SUITE_END() }