diff options
author | chriseth <c@ethdev.com> | 2016-02-22 09:13:41 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-03-30 08:37:00 +0800 |
commit | 949b00ed591303c531ed8fa73087b710b7a554de (patch) | |
tree | 182664f2545e6211d7994ef90a1e7746d5482981 | |
parent | 8236732e9a5d2535afd3a3573a70d5aab3da3efe (diff) | |
download | dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar.gz dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar.bz2 dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar.lz dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar.xz dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.tar.zst dexon-solidity-949b00ed591303c531ed8fa73087b710b7a554de.zip |
Parsing for inline assembly.
-rw-r--r-- | libsolidity/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 25 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 10 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTVisitor.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 12 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmData.h | 70 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 212 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.h | 55 | ||||
-rw-r--r-- | libsolidity/interface/SourceReferenceFormatter.cpp | 9 | ||||
-rw-r--r-- | libsolidity/interface/SourceReferenceFormatter.h | 3 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 20 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 48 | ||||
-rw-r--r-- | solc/CommandLineInterface.h | 4 | ||||
-rw-r--r-- | solc/jsonCompiler.cpp | 18 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 138 |
19 files changed, 630 insertions, 16 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index ab9ee1e2..5f4a1a06 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -6,6 +6,7 @@ aux_source_directory(codegen SRC_LIST) aux_source_directory(formal SRC_LIST) aux_source_directory(interface SRC_LIST) aux_source_directory(parsing SRC_LIST) +aux_source_directory(inlineasm SRC_LIST) set(EXECUTABLE solidity) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d32d76a4..f53c78f2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -28,6 +28,7 @@ #include <memory> #include <boost/noncopyable.hpp> #include <libevmasm/SourceLocation.h> +#include <libevmcore/Instruction.h> #include <libsolidity/interface/Utils.h> #include <libsolidity/ast/ASTForward.h> #include <libsolidity/parsing/Token.h> @@ -854,6 +855,30 @@ public: virtual StatementAnnotation& annotation() const override; }; +// Forward-declaration to InlineAssembly.h +class AsmData; + +/** + * Inline assembly. + */ +class InlineAssembly: public Statement +{ +public: + InlineAssembly( + SourceLocation const& _location, + ASTPointer<ASTString> const& _docString, + std::shared_ptr<AsmData> const& _operations + ): + Statement(_location, _docString), m_operations(_operations) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + AsmData const& operations() const { return *m_operations; } + +private: + std::shared_ptr<AsmData> m_operations; +}; + /** * Brace-enclosed block containing zero or more statements. */ diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 163e22f4..89d0bf35 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -157,6 +157,12 @@ bool ASTJsonConverter::visit(Mapping const&) return true; } +bool ASTJsonConverter::visit(InlineAssembly const&) +{ + addJsonNode("InlineAssembly", {}, true); + return true; +} + bool ASTJsonConverter::visit(Block const&) { addJsonNode("Block", {}, true); @@ -355,6 +361,10 @@ void ASTJsonConverter::endVisit(Mapping const&) { } +void ASTJsonConverter::endVisit(InlineAssembly const&) +{ +} + void ASTJsonConverter::endVisit(Block const&) { goUp(); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index b7fc84e9..91ee72e1 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -57,6 +57,7 @@ public: bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(Mapping const& _node) override; + bool visit(InlineAssembly const& _node) override; bool visit(Block const& _node) override; bool visit(IfStatement const& _node) override; bool visit(WhileStatement const& _node) override; @@ -90,6 +91,7 @@ public: void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; void endVisit(Mapping const&) override; + void endVisit(InlineAssembly const&) override; void endVisit(Block const&) override; void endVisit(IfStatement const&) override; void endVisit(WhileStatement const&) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 283bc8f9..9ed9c6d5 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -171,6 +171,13 @@ bool ASTPrinter::visit(ArrayTypeName const& _node) return goDeeper(); } +bool ASTPrinter::visit(InlineAssembly const& _node) +{ + writeLine("InlineAssembly"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(Block const& _node) { writeLine("Block"); @@ -433,6 +440,11 @@ void ASTPrinter::endVisit(ArrayTypeName const&) m_indentation--; } +void ASTPrinter::endVisit(InlineAssembly const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(Block const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 334fefc7..a2546935 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -64,6 +64,7 @@ public: bool visit(UserDefinedTypeName const& _node) override; bool visit(Mapping const& _node) override; bool visit(ArrayTypeName const& _node) override; + bool visit(InlineAssembly const& _node) override; bool visit(Block const& _node) override; bool visit(PlaceholderStatement const& _node) override; bool visit(IfStatement const& _node) override; @@ -105,6 +106,7 @@ public: void endVisit(UserDefinedTypeName const&) override; void endVisit(Mapping const&) override; void endVisit(ArrayTypeName const&) override; + void endVisit(InlineAssembly const&) override; void endVisit(Block const&) override; void endVisit(PlaceholderStatement const&) override; void endVisit(IfStatement const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 625f395d..5aac2066 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -62,6 +62,7 @@ public: virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } virtual bool visit(Mapping& _node) { return visitNode(_node); } virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); } + virtual bool visit(InlineAssembly& _node) { return visitNode(_node); } virtual bool visit(Block& _node) { return visitNode(_node); } virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); } virtual bool visit(IfStatement& _node) { return visitNode(_node); } @@ -105,6 +106,7 @@ public: virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); } virtual void endVisit(Mapping& _node) { endVisitNode(_node); } virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); } + virtual void endVisit(InlineAssembly& _node) { endVisitNode(_node); } virtual void endVisit(Block& _node) { endVisitNode(_node); } virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); } virtual void endVisit(IfStatement& _node) { endVisitNode(_node); } @@ -166,6 +168,7 @@ public: virtual bool visit(WhileStatement const& _node) { return visitNode(_node); } virtual bool visit(ForStatement const& _node) { return visitNode(_node); } virtual bool visit(Continue const& _node) { return visitNode(_node); } + virtual bool visit(InlineAssembly const& _node) { return visitNode(_node); } virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } virtual bool visit(Throw const& _node) { return visitNode(_node); } @@ -209,6 +212,7 @@ public: virtual void endVisit(WhileStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Continue const& _node) { endVisitNode(_node); } + virtual void endVisit(InlineAssembly const& _node) { endVisitNode(_node); } virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } virtual void endVisit(Throw const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index dee9d5b1..dd2a7d60 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -357,6 +357,18 @@ void ArrayTypeName::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void InlineAssembly::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void InlineAssembly::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void Block::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h new file mode 100644 index 00000000..a38a9d36 --- /dev/null +++ b/libsolidity/inlineasm/AsmData.h @@ -0,0 +1,70 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Parsed inline assembly to be used by the AST + */ + +#pragma once + +#include <boost/variant.hpp> +#include <libevmcore/Instruction.h> + +namespace dev +{ +namespace solidity +{ + +class AsmData +{ +public: + /// Direct EVM instruction (except PUSHi and JUMPDEST) + struct Instruction { eth::Instruction instruction; }; + /// Literal number or string (up to 32 bytes) + struct Literal { bool isNumber; std::string value; }; + /// External / internal identifier or label reference + struct Identifier { std::string name; }; + struct FunctionalInstruction; + /// Jump label ("name:") + struct Label { std::string name; }; + /// Assignemnt (":= x", moves stack top into x, potentially multiple slots) + struct Assignment { Identifier variableName; }; + struct FunctionalAssignment; + struct VariableDeclaration; + struct Block; + using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>; + /// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand + /// side and requires x to occupy exactly one stack slot. + struct FunctionalAssignment { Identifier variableName; std::shared_ptr<Statement> value; }; + /// Functional instruction, e.g. "mul(mload(20), add(2, x))" + struct FunctionalInstruction { Instruction instruction; std::vector<Statement> arguments; }; + /// Block-scope variable declaration ("let x := mload(20)"), non-hoisted + struct VariableDeclaration { std::string name; std::shared_ptr<Statement> value; }; + /// Block that creates a scope (frees declared stack variables) + struct Block { std::vector<Statement> statements; }; + + AsmData(Block&& _statements): m_statements(_statements) {} + + Block const& statements() const { return m_statements; } + +private: + Block m_statements; +}; + +} +} diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp new file mode 100644 index 00000000..28fd5354 --- /dev/null +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -0,0 +1,212 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Solidity inline assembly parser. + */ + +#include <libsolidity/inlineasm/AsmParser.h> +#include <ctype.h> +#include <algorithm> +#include <libevmcore/Instruction.h> +#include <libsolidity/parsing/Scanner.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +shared_ptr<AsmData> InlineAssemblyParser::parse(std::shared_ptr<Scanner> const& _scanner) +{ + try + { + m_scanner = _scanner; + return make_shared<AsmData>(parseBlock()); + } + catch (FatalError const&) + { + if (m_errors.empty()) + throw; // Something is weird here, rather throw again. + } + return nullptr; +} + +AsmData::Block InlineAssemblyParser::parseBlock() +{ + expectToken(Token::LBrace); + AsmData::Block block; + while (m_scanner->currentToken() != Token::RBrace) + block.statements.emplace_back(parseStatement()); + m_scanner->next(); + return block; +} + +AsmData::Statement InlineAssemblyParser::parseStatement() +{ + switch (m_scanner->currentToken()) + { + case Token::Let: + return parseVariableDeclaration(); + case Token::LBrace: + return parseBlock(); + case Token::Assign: + { + m_scanner->next(); + expectToken(Token::Colon); + string name = m_scanner->currentLiteral(); + expectToken(Token::Identifier); + return AsmData::Assignment{AsmData::Identifier{name}}; + } + default: + break; + } + // Options left: + // Simple instruction (might turn into functional), + // literal, + // identifier (might turn into label or functional assignment) + AsmData::Statement statement(parseElementaryOperation()); + switch (m_scanner->currentToken()) + { + case Token::LParen: + return parseFunctionalInstruction(statement); + case Token::Colon: + { + if (statement.type() != typeid(AsmData::Identifier)) + fatalParserError("Label name / variable name must precede \":\"."); + string const& name = boost::get<AsmData::Identifier>(statement).name; + m_scanner->next(); + if (m_scanner->currentToken() == Token::Assign) + { + // functional assignment + m_scanner->next(); + unique_ptr<AsmData::Statement> value; + value.reset(new AsmData::Statement(parseExpression())); + return AsmData::FunctionalAssignment{{move(name)}, move(value)}; + } + else + // label + return AsmData::Label{name}; + } + default: + break; + } + return statement; +} + +AsmData::Statement InlineAssemblyParser::parseExpression() +{ + AsmData::Statement operation = parseElementaryOperation(true); + if (m_scanner->currentToken() == Token::LParen) + return parseFunctionalInstruction(operation); + else + return operation; +} + +AsmData::Statement InlineAssemblyParser::parseElementaryOperation(bool _onlySinglePusher) +{ + // Allowed instructions, lowercase names. + static map<string, eth::Instruction> s_instructions; + if (s_instructions.empty()) + for (auto const& instruction: eth::c_instructions) + { + if ( + instruction.second == eth::Instruction::JUMPDEST || + (eth::Instruction::PUSH1 <= instruction.second && instruction.second <= eth::Instruction::PUSH32) + ) + continue; + string name = instruction.first; + transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); + s_instructions[name] = instruction.second; + } + + //@TODO track location + + switch (m_scanner->currentToken()) + { + case Token::Identifier: + { + string literal = m_scanner->currentLiteral(); + // first search the set of instructions. + if (s_instructions.count(literal)) + { + eth::Instruction const& instr = s_instructions[literal]; + if (_onlySinglePusher) + { + eth::InstructionInfo info = eth::instructionInfo(instr); + if (info.ret != 1) + fatalParserError("Instruction " + info.name + " not allowed in this context."); + } + m_scanner->next(); + return AsmData::Instruction{instr}; + } + else + m_scanner->next(); + return AsmData::Identifier{literal}; + break; + } + case Token::StringLiteral: + case Token::Number: + { + AsmData::Literal literal{ + m_scanner->currentToken() == Token::Number, + m_scanner->currentLiteral() + }; + m_scanner->next(); + return literal; + } + default: + break; + } + fatalParserError("Expected elementary inline assembly operation."); + return {}; +} + +AsmData::VariableDeclaration InlineAssemblyParser::parseVariableDeclaration() +{ + expectToken(Token::Let); + string name = m_scanner->currentLiteral(); + expectToken(Token::Identifier); + expectToken(Token::Colon); + expectToken(Token::Assign); + unique_ptr<AsmData::Statement> value; + value.reset(new AsmData::Statement(parseExpression())); + return AsmData::VariableDeclaration{name, move(value)}; +} + +AsmData::FunctionalInstruction InlineAssemblyParser::parseFunctionalInstruction(AsmData::Statement const& _instruction) +{ + if (_instruction.type() != typeid(AsmData::Instruction)) + fatalParserError("Assembly instruction required in front of \"(\")"); + eth::Instruction instr = boost::get<AsmData::Instruction>(_instruction).instruction; + eth::InstructionInfo instrInfo = eth::instructionInfo(instr); + if (eth::Instruction::DUP1 <= instr && instr <= eth::Instruction::DUP16) + fatalParserError("DUPi instructions not allowed for functional notation"); + if (eth::Instruction::SWAP1 <= instr && instr <= eth::Instruction::SWAP16) + fatalParserError("SWAPi instructions not allowed for functional notation"); + + expectToken(Token::LParen); + vector<AsmData::Statement> arguments; + unsigned args = unsigned(instrInfo.args); + for (unsigned i = 0; i < args; ++i) + { + arguments.push_back(parseExpression()); + if (i != args - 1) + expectToken(Token::Comma); + } + expectToken(Token::RParen); + return AsmData::FunctionalInstruction{{instr}, move(arguments)}; +} diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h new file mode 100644 index 00000000..fe84470d --- /dev/null +++ b/libsolidity/inlineasm/AsmParser.h @@ -0,0 +1,55 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Solidity inline assembly parser. + */ + +#pragma once + +#include <memory> +#include <vector> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/parsing/ParserBase.h> + +namespace dev +{ +namespace solidity +{ + +class InlineAssemblyParser: public ParserBase +{ +public: + InlineAssemblyParser(ErrorList& _errors): ParserBase(_errors) {} + + /// Parses an inline assembly block starting with `{` and ending with `}`. + /// @returns an empty shared pointer on error. + std::shared_ptr<AsmData> parse(std::shared_ptr<Scanner> const& _scanner); + +protected: + AsmData::Block parseBlock(); + AsmData::Statement parseStatement(); + /// Parses a functional expression that has to push exactly one stack element + AsmData::Statement parseExpression(); + AsmData::Statement parseElementaryOperation(bool _onlySinglePusher = false); + AsmData::VariableDeclaration parseVariableDeclaration(); + AsmData::FunctionalInstruction parseFunctionalInstruction(AsmData::Statement const& _instruction); +}; + +} +} diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 169e5c18..65d7fbc8 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -21,7 +21,6 @@ */ #include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/interface/Exceptions.h> @@ -85,7 +84,7 @@ void SourceReferenceFormatter::printExceptionInformation( ostream& _stream, Exception const& _exception, string const& _name, - CompilerStack const& _compiler + function<Scanner const&(string const&)> const& _scannerFromSourceName ) { SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); @@ -94,7 +93,7 @@ void SourceReferenceFormatter::printExceptionInformation( if (location) { - scannerPtr = &_compiler.scanner(*location->sourceName); + scannerPtr = &_scannerFromSourceName(*location->sourceName); printSourceName(_stream, *location, *scannerPtr); } @@ -104,7 +103,7 @@ void SourceReferenceFormatter::printExceptionInformation( if (location) { - scannerPtr = &_compiler.scanner(*location->sourceName); + scannerPtr = &_scannerFromSourceName(*location->sourceName); printSourceLocation(_stream, *location, *scannerPtr); } @@ -112,7 +111,7 @@ void SourceReferenceFormatter::printExceptionInformation( { for (auto info: secondarylocation->infos) { - scannerPtr = &_compiler.scanner(*info.second.sourceName); + scannerPtr = &_scannerFromSourceName(*info.second.sourceName); _stream << info.first << " "; printSourceName(_stream, info.second, *scannerPtr); _stream << endl; diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h index dd258c27..1fc42753 100644 --- a/libsolidity/interface/SourceReferenceFormatter.h +++ b/libsolidity/interface/SourceReferenceFormatter.h @@ -23,6 +23,7 @@ #pragma once #include <ostream> +#include <functional> #include <libevmasm/SourceLocation.h> namespace dev @@ -44,7 +45,7 @@ public: std::ostream& _stream, Exception const& _exception, std::string const& _name, - CompilerStack const& _compiler + std::function<Scanner const&(std::string const&)> const& _scannerFromSourceName ); private: static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 7bda3610..29377380 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -20,11 +20,13 @@ * Solidity parser. */ +#include <ctype.h> #include <vector> #include <libdevcore/Log.h> #include <libevmasm/SourceLocation.h> #include <libsolidity/parsing/Parser.h> #include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/InterfaceHandler.h> @@ -712,6 +714,8 @@ ASTPointer<Statement> Parser::parseStatement() m_scanner->next(); break; } + case Token::Assembly: + return parseInlineAssembly(docString); case Token::Identifier: if (m_insideModifier && m_scanner->currentLiteral() == "_") { @@ -727,6 +731,22 @@ ASTPointer<Statement> Parser::parseStatement() return statement; } +ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Assembly); + if (m_scanner->currentToken() != Token::StringLiteral) + fatalParserError("Expected assembly name."); + if (m_scanner->currentLiteral() != "evmasm") + fatalParserError("Only \"evmasm\" supported."); + m_scanner->next(); + + InlineAssemblyParser parser(m_errors); + shared_ptr<InlineAssemblyBlock> operations = parser.parse(m_scanner); + nodeFactory.markEndPosition(); + return nodeFactory.createNode<InlineAssembly>(_docString, operations); +} + ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString) { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index a093cc5b..d776c3fd 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -81,6 +81,7 @@ private: ); ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {}); ASTPointer<Statement> parseStatement(); + ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {}); ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString); ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 499b1f7a..1cf87bc9 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -45,6 +45,7 @@ #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/SourceReferenceFormatter.h> #include <libsolidity/interface/GasEstimator.h> +#include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/formal/Why3Translator.h> using namespace std; @@ -431,6 +432,10 @@ Allowed options)", ) (g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.") ( + "assemble", + "Switch to assembly mode, ignoring all options and assumes input is assembly." + ) + ( "link", "Switch to linker mode, ignoring all options apart from --libraries " "and modify binaries in place." @@ -509,6 +514,12 @@ bool CommandLineInterface::processInput() if (!parseLibraryOption(library)) return false; + if (m_args.count("assemble")) + { + // switch to assembly mode + m_onlyAssemble = true; + return assemble(); + } if (m_args.count("link")) { // switch to linker mode @@ -574,6 +585,7 @@ bool CommandLineInterface::processInput() }; m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader)); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; try { for (auto const& sourceCode: m_sourceCodes) @@ -593,7 +605,8 @@ bool CommandLineInterface::processInput() SourceReferenceFormatter::printExceptionInformation( cerr, *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", *m_compiler + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + scannerFromSourceName ); if (!successful) @@ -601,7 +614,7 @@ bool CommandLineInterface::processInput() } catch (CompilerError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", *m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", scannerFromSourceName); return false; } catch (InternalCompilerError const& _exception) @@ -615,7 +628,7 @@ bool CommandLineInterface::processInput() if (_error.type() == Error::Type::DocstringParsingError) cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; else - SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), *m_compiler); + SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), scannerFromSourceName); return false; } @@ -759,7 +772,9 @@ void CommandLineInterface::handleAst(string const& _argStr) void CommandLineInterface::actOnInput() { - if (m_onlyLink) + if (m_onlyAssemble) + return; //@TODO + else if (m_onlyLink) writeLinkedFiles(); else outputCompilationResults(); @@ -812,6 +827,31 @@ void CommandLineInterface::writeLinkedFiles() writeFile(src.first, src.second); } +bool CommandLineInterface::assemble() +{ + //@TODO later, we will use the convenience interface and should also remove the include above + bool successful = true; + ErrorList errors; + map<string, shared_ptr<Scanner>> scanners; + for (auto const& src: m_sourceCodes) + { + auto scanner = make_shared<Scanner>(CharStream(src.second), src.first); + scanners[src.first] = scanner; + InlineAssemblyParser parser(errors); + if (!parser.parse(scanner)) + successful = false; + } + for (auto const& error: errors) + SourceReferenceFormatter::printExceptionInformation( + cerr, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](string const& _source) -> Scanner const& { return *scanners.at(_source); } + ); + + return successful; +} + void CommandLineInterface::outputCompilationResults() { handleCombinedJSON(); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index d288b5c1..1c40e26d 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -50,6 +50,9 @@ private: bool link(); void writeLinkedFiles(); + /// Parse assembly input. + bool assemble(); + void outputCompilationResults(); void handleCombinedJSON(); @@ -73,6 +76,7 @@ private: /// @arg _data to be written void createFile(std::string const& _fileName, std::string const& _data); + bool m_onlyAssemble = false; bool m_onlyLink = false; /// Compiler arguments variable map diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index b5efa94d..c6b40fdc 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -21,6 +21,7 @@ */ #include <string> +#include <functional> #include <iostream> #include <json/json.h> #include <libdevcore/Common.h> @@ -47,10 +48,14 @@ extern "C" { typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); } -string formatError(Exception const& _exception, string const& _name, CompilerStack const& _compiler) +string formatError( + Exception const& _exception, + string const& _name, + function<Scanner const&(string const&)> const& _scannerFromSourceName +) { ostringstream errorOutput; - SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler); + SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName); return errorOutput.str(); } @@ -150,6 +155,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback }; } CompilerStack compiler(true, readCallback); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return compiler.scanner(_sourceName); }; bool success = false; try { @@ -161,22 +167,22 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback errors.append(formatError( *error, (err->type() == Error::Type::Warning) ? "Warning" : "Error", - compiler + scannerFromSourceName )); } success = succ; // keep success false on exception } catch (Error const& error) { - errors.append(formatError(error, error.typeName(), compiler)); + errors.append(formatError(error, error.typeName(), scannerFromSourceName)); } catch (CompilerError const& exception) { - errors.append(formatError(exception, "Compiler error", compiler)); + errors.append(formatError(exception, "Compiler error", scannerFromSourceName)); } catch (InternalCompilerError const& exception) { - errors.append(formatError(exception, "Internal compiler error", compiler)); + errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName)); } catch (Exception const& exception) { diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp new file mode 100644 index 00000000..c435f5d6 --- /dev/null +++ b/test/libsolidity/InlineAssembly.cpp @@ -0,0 +1,138 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Unit tests for inline assembly. + */ + +#include <string> +#include <memory> +#include <libdevcore/Log.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/ast/AST.h> +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ +shared_ptr<InlineAssemblyBlock> parse(std::string const& _source, ErrorList& _errors) +{ + return InlineAssemblyParser(_errors).parse(std::make_shared<Scanner>(CharStream(_source))); +} + +bool successParse(std::string const& _source) +{ + ErrorList errors; + try + { + auto assembly = parse(_source, errors); + if (!assembly) + return false; + } + catch (FatalError const&) + { + if (Error::containsErrorOfType(errors, Error::Type::ParserError)) + return false; + } + if (Error::containsErrorOfType(errors, Error::Type::ParserError)) + return false; + + BOOST_CHECK(Error::containsOnlyWarnings(errors)); + return true; +} + +} + + +BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + BOOST_CHECK(successParse("{ }")); +} + +BOOST_AUTO_TEST_CASE(simple_instructions) +{ + BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub }")); +} + +BOOST_AUTO_TEST_CASE(constants) +{ + BOOST_CHECK(successParse("{ 7 8 mul }")); +} + +BOOST_AUTO_TEST_CASE(vardecl) +{ + BOOST_CHECK(successParse("{ let x := 7 }")); +} + +BOOST_AUTO_TEST_CASE(assignment) +{ + BOOST_CHECK(successParse("{ 7 8 add =: x }")); +} + +BOOST_AUTO_TEST_CASE(label) +{ + BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump }")); +} + +BOOST_AUTO_TEST_CASE(label_complex) +{ + BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) }")); +} + +BOOST_AUTO_TEST_CASE(functional) +{ + BOOST_CHECK(successParse("{ add(7, mul(6, x)) add mul(7, 8) }")); +} + +BOOST_AUTO_TEST_CASE(functional_assignment) +{ + BOOST_CHECK(successParse("{ x := 7 }")); +} + +BOOST_AUTO_TEST_CASE(functional_assignment_complex) +{ + BOOST_CHECK(successParse("{ x := add(7, mul(6, x)) add mul(7, 8) }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_complex) +{ + BOOST_CHECK(successParse("{ let x := add(7, mul(6, x)) add mul(7, 8) }")); +} + +BOOST_AUTO_TEST_CASE(blocks) +{ + BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces |