mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Parsing for inline assembly.
This commit is contained in:
		
							parent
							
								
									8236732e9a
								
							
						
					
					
						commit
						949b00ed59
					
				| @ -6,6 +6,7 @@ aux_source_directory(codegen SRC_LIST) | |||||||
| aux_source_directory(formal SRC_LIST) | aux_source_directory(formal SRC_LIST) | ||||||
| aux_source_directory(interface SRC_LIST) | aux_source_directory(interface SRC_LIST) | ||||||
| aux_source_directory(parsing SRC_LIST) | aux_source_directory(parsing SRC_LIST) | ||||||
|  | aux_source_directory(inlineasm SRC_LIST) | ||||||
| 
 | 
 | ||||||
| set(EXECUTABLE solidity) | set(EXECUTABLE solidity) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ | |||||||
| #include <memory> | #include <memory> | ||||||
| #include <boost/noncopyable.hpp> | #include <boost/noncopyable.hpp> | ||||||
| #include <libevmasm/SourceLocation.h> | #include <libevmasm/SourceLocation.h> | ||||||
|  | #include <libevmcore/Instruction.h> | ||||||
| #include <libsolidity/interface/Utils.h> | #include <libsolidity/interface/Utils.h> | ||||||
| #include <libsolidity/ast/ASTForward.h> | #include <libsolidity/ast/ASTForward.h> | ||||||
| #include <libsolidity/parsing/Token.h> | #include <libsolidity/parsing/Token.h> | ||||||
| @ -854,6 +855,30 @@ public: | |||||||
| 	virtual StatementAnnotation& annotation() const override; | 	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. |  * Brace-enclosed block containing zero or more statements. | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -157,6 +157,12 @@ bool ASTJsonConverter::visit(Mapping const&) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool ASTJsonConverter::visit(InlineAssembly const&) | ||||||
|  | { | ||||||
|  | 	addJsonNode("InlineAssembly", {}, true); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool ASTJsonConverter::visit(Block const&) | bool ASTJsonConverter::visit(Block const&) | ||||||
| { | { | ||||||
| 	addJsonNode("Block", {}, true); | 	addJsonNode("Block", {}, true); | ||||||
| @ -355,6 +361,10 @@ void ASTJsonConverter::endVisit(Mapping const&) | |||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ASTJsonConverter::endVisit(InlineAssembly const&) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ASTJsonConverter::endVisit(Block const&) | void ASTJsonConverter::endVisit(Block const&) | ||||||
| { | { | ||||||
| 	goUp(); | 	goUp(); | ||||||
|  | |||||||
| @ -57,6 +57,7 @@ public: | |||||||
| 	bool visit(ElementaryTypeName const& _node) override; | 	bool visit(ElementaryTypeName const& _node) override; | ||||||
| 	bool visit(UserDefinedTypeName const& _node) override; | 	bool visit(UserDefinedTypeName const& _node) override; | ||||||
| 	bool visit(Mapping const& _node) override; | 	bool visit(Mapping const& _node) override; | ||||||
|  | 	bool visit(InlineAssembly const& _node) override; | ||||||
| 	bool visit(Block const& _node) override; | 	bool visit(Block const& _node) override; | ||||||
| 	bool visit(IfStatement const& _node) override; | 	bool visit(IfStatement const& _node) override; | ||||||
| 	bool visit(WhileStatement const& _node) override; | 	bool visit(WhileStatement const& _node) override; | ||||||
| @ -90,6 +91,7 @@ public: | |||||||
| 	void endVisit(ElementaryTypeName const&) override; | 	void endVisit(ElementaryTypeName const&) override; | ||||||
| 	void endVisit(UserDefinedTypeName const&) override; | 	void endVisit(UserDefinedTypeName const&) override; | ||||||
| 	void endVisit(Mapping const&) override; | 	void endVisit(Mapping const&) override; | ||||||
|  | 	void endVisit(InlineAssembly const&) override; | ||||||
| 	void endVisit(Block const&) override; | 	void endVisit(Block const&) override; | ||||||
| 	void endVisit(IfStatement const&) override; | 	void endVisit(IfStatement const&) override; | ||||||
| 	void endVisit(WhileStatement const&) override; | 	void endVisit(WhileStatement const&) override; | ||||||
|  | |||||||
| @ -171,6 +171,13 @@ bool ASTPrinter::visit(ArrayTypeName const& _node) | |||||||
| 	return goDeeper(); | 	return goDeeper(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool ASTPrinter::visit(InlineAssembly const& _node) | ||||||
|  | { | ||||||
|  | 	writeLine("InlineAssembly"); | ||||||
|  | 	printSourcePart(_node); | ||||||
|  | 	return goDeeper(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool ASTPrinter::visit(Block const& _node) | bool ASTPrinter::visit(Block const& _node) | ||||||
| { | { | ||||||
| 	writeLine("Block"); | 	writeLine("Block"); | ||||||
| @ -433,6 +440,11 @@ void ASTPrinter::endVisit(ArrayTypeName const&) | |||||||
| 	m_indentation--; | 	m_indentation--; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ASTPrinter::endVisit(InlineAssembly const&) | ||||||
|  | { | ||||||
|  | 	m_indentation--; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ASTPrinter::endVisit(Block const&) | void ASTPrinter::endVisit(Block const&) | ||||||
| { | { | ||||||
| 	m_indentation--; | 	m_indentation--; | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ public: | |||||||
| 	bool visit(UserDefinedTypeName const& _node) override; | 	bool visit(UserDefinedTypeName const& _node) override; | ||||||
| 	bool visit(Mapping const& _node) override; | 	bool visit(Mapping const& _node) override; | ||||||
| 	bool visit(ArrayTypeName const& _node) override; | 	bool visit(ArrayTypeName const& _node) override; | ||||||
|  | 	bool visit(InlineAssembly const& _node) override; | ||||||
| 	bool visit(Block const& _node) override; | 	bool visit(Block const& _node) override; | ||||||
| 	bool visit(PlaceholderStatement const& _node) override; | 	bool visit(PlaceholderStatement const& _node) override; | ||||||
| 	bool visit(IfStatement const& _node) override; | 	bool visit(IfStatement const& _node) override; | ||||||
| @ -105,6 +106,7 @@ public: | |||||||
| 	void endVisit(UserDefinedTypeName const&) override; | 	void endVisit(UserDefinedTypeName const&) override; | ||||||
| 	void endVisit(Mapping const&) override; | 	void endVisit(Mapping const&) override; | ||||||
| 	void endVisit(ArrayTypeName const&) override; | 	void endVisit(ArrayTypeName const&) override; | ||||||
|  | 	void endVisit(InlineAssembly const&) override; | ||||||
| 	void endVisit(Block const&) override; | 	void endVisit(Block const&) override; | ||||||
| 	void endVisit(PlaceholderStatement const&) override; | 	void endVisit(PlaceholderStatement const&) override; | ||||||
| 	void endVisit(IfStatement const&) override; | 	void endVisit(IfStatement const&) override; | ||||||
|  | |||||||
| @ -62,6 +62,7 @@ public: | |||||||
| 	virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } | 	virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(Mapping& _node) { return visitNode(_node); } | 	virtual bool visit(Mapping& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(ArrayTypeName& _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(Block& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); } | 	virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(IfStatement& _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(UserDefinedTypeName& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(Mapping& _node) { endVisitNode(_node); } | 	virtual void endVisit(Mapping& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(ArrayTypeName& _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(Block& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); } | 	virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(IfStatement& _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(WhileStatement const& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(ForStatement 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(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(Break const& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(Return const& _node) { return visitNode(_node); } | 	virtual bool visit(Return const& _node) { return visitNode(_node); } | ||||||
| 	virtual bool visit(Throw 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(WhileStatement const& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); } | 	virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(Continue 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(Break const& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(Return const& _node) { endVisitNode(_node); } | 	virtual void endVisit(Return const& _node) { endVisitNode(_node); } | ||||||
| 	virtual void endVisit(Throw const& _node) { endVisitNode(_node); } | 	virtual void endVisit(Throw const& _node) { endVisitNode(_node); } | ||||||
|  | |||||||
| @ -357,6 +357,18 @@ void ArrayTypeName::accept(ASTConstVisitor& _visitor) const | |||||||
| 	_visitor.endVisit(*this); | 	_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) | void Block::accept(ASTVisitor& _visitor) | ||||||
| { | { | ||||||
| 	if (_visitor.visit(*this)) | 	if (_visitor.visit(*this)) | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								libsolidity/inlineasm/AsmData.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								libsolidity/inlineasm/AsmData.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										212
									
								
								libsolidity/inlineasm/AsmParser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								libsolidity/inlineasm/AsmParser.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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)}; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								libsolidity/inlineasm/AsmParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								libsolidity/inlineasm/AsmParser.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
| @ -21,7 +21,6 @@ | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <libsolidity/interface/SourceReferenceFormatter.h> | #include <libsolidity/interface/SourceReferenceFormatter.h> | ||||||
| #include <libsolidity/interface/CompilerStack.h> |  | ||||||
| #include <libsolidity/parsing/Scanner.h> | #include <libsolidity/parsing/Scanner.h> | ||||||
| #include <libsolidity/interface/Exceptions.h> | #include <libsolidity/interface/Exceptions.h> | ||||||
| 
 | 
 | ||||||
| @ -85,7 +84,7 @@ void SourceReferenceFormatter::printExceptionInformation( | |||||||
| 	ostream& _stream, | 	ostream& _stream, | ||||||
| 	Exception const& _exception, | 	Exception const& _exception, | ||||||
| 	string const& _name, | 	string const& _name, | ||||||
| 	CompilerStack const& _compiler | 	function<Scanner const&(string const&)> const& _scannerFromSourceName | ||||||
| ) | ) | ||||||
| { | { | ||||||
| 	SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); | 	SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); | ||||||
| @ -94,7 +93,7 @@ void SourceReferenceFormatter::printExceptionInformation( | |||||||
| 
 | 
 | ||||||
| 	if (location) | 	if (location) | ||||||
| 	{ | 	{ | ||||||
| 		scannerPtr = &_compiler.scanner(*location->sourceName); | 		scannerPtr = &_scannerFromSourceName(*location->sourceName); | ||||||
| 		printSourceName(_stream, *location, *scannerPtr); | 		printSourceName(_stream, *location, *scannerPtr); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -104,7 +103,7 @@ void SourceReferenceFormatter::printExceptionInformation( | |||||||
| 
 | 
 | ||||||
| 	if (location) | 	if (location) | ||||||
| 	{ | 	{ | ||||||
| 		scannerPtr = &_compiler.scanner(*location->sourceName); | 		scannerPtr = &_scannerFromSourceName(*location->sourceName); | ||||||
| 		printSourceLocation(_stream, *location, *scannerPtr); | 		printSourceLocation(_stream, *location, *scannerPtr); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -112,7 +111,7 @@ void SourceReferenceFormatter::printExceptionInformation( | |||||||
| 	{ | 	{ | ||||||
| 		for (auto info: secondarylocation->infos) | 		for (auto info: secondarylocation->infos) | ||||||
| 		{ | 		{ | ||||||
| 			scannerPtr = &_compiler.scanner(*info.second.sourceName); | 			scannerPtr = &_scannerFromSourceName(*info.second.sourceName); | ||||||
| 			_stream << info.first << " "; | 			_stream << info.first << " "; | ||||||
| 			printSourceName(_stream, info.second, *scannerPtr); | 			printSourceName(_stream, info.second, *scannerPtr); | ||||||
| 			_stream << endl; | 			_stream << endl; | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <ostream> | #include <ostream> | ||||||
|  | #include <functional> | ||||||
| #include <libevmasm/SourceLocation.h> | #include <libevmasm/SourceLocation.h> | ||||||
| 
 | 
 | ||||||
| namespace dev | namespace dev | ||||||
| @ -44,7 +45,7 @@ public: | |||||||
| 		std::ostream& _stream, | 		std::ostream& _stream, | ||||||
| 		Exception const& _exception, | 		Exception const& _exception, | ||||||
| 		std::string const& _name, | 		std::string const& _name, | ||||||
| 		CompilerStack const& _compiler | 		std::function<Scanner const&(std::string const&)> const& _scannerFromSourceName | ||||||
| 	); | 	); | ||||||
| private: | private: | ||||||
| 	static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner); | 	static void printSourceName(std::ostream& _stream, SourceLocation const& _location, Scanner const& _scanner); | ||||||
|  | |||||||
| @ -20,11 +20,13 @@ | |||||||
|  * Solidity parser. |  * Solidity parser. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <ctype.h> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <libdevcore/Log.h> | #include <libdevcore/Log.h> | ||||||
| #include <libevmasm/SourceLocation.h> | #include <libevmasm/SourceLocation.h> | ||||||
| #include <libsolidity/parsing/Parser.h> | #include <libsolidity/parsing/Parser.h> | ||||||
| #include <libsolidity/parsing/Scanner.h> | #include <libsolidity/parsing/Scanner.h> | ||||||
|  | #include <libsolidity/inlineasm/AsmParser.h> | ||||||
| #include <libsolidity/interface/Exceptions.h> | #include <libsolidity/interface/Exceptions.h> | ||||||
| #include <libsolidity/interface/InterfaceHandler.h> | #include <libsolidity/interface/InterfaceHandler.h> | ||||||
| 
 | 
 | ||||||
| @ -712,6 +714,8 @@ ASTPointer<Statement> Parser::parseStatement() | |||||||
| 		m_scanner->next(); | 		m_scanner->next(); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  | 	case Token::Assembly: | ||||||
|  | 		return parseInlineAssembly(docString); | ||||||
| 	case Token::Identifier: | 	case Token::Identifier: | ||||||
| 		if (m_insideModifier && m_scanner->currentLiteral() == "_") | 		if (m_insideModifier && m_scanner->currentLiteral() == "_") | ||||||
| 		{ | 		{ | ||||||
| @ -727,6 +731,22 @@ ASTPointer<Statement> Parser::parseStatement() | |||||||
| 	return statement; | 	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) | ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString) | ||||||
| { | { | ||||||
| 	ASTNodeFactory nodeFactory(*this); | 	ASTNodeFactory nodeFactory(*this); | ||||||
|  | |||||||
| @ -81,6 +81,7 @@ private: | |||||||
| 	); | 	); | ||||||
| 	ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {}); | 	ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {}); | ||||||
| 	ASTPointer<Statement> parseStatement(); | 	ASTPointer<Statement> parseStatement(); | ||||||
|  | 	ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {}); | ||||||
| 	ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString); | 	ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString); | ||||||
| 	ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); | 	ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); | ||||||
| 	ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); | 	ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); | ||||||
|  | |||||||
| @ -45,6 +45,7 @@ | |||||||
| #include <libsolidity/interface/CompilerStack.h> | #include <libsolidity/interface/CompilerStack.h> | ||||||
| #include <libsolidity/interface/SourceReferenceFormatter.h> | #include <libsolidity/interface/SourceReferenceFormatter.h> | ||||||
| #include <libsolidity/interface/GasEstimator.h> | #include <libsolidity/interface/GasEstimator.h> | ||||||
|  | #include <libsolidity/inlineasm/AsmParser.h> | ||||||
| #include <libsolidity/formal/Why3Translator.h> | #include <libsolidity/formal/Why3Translator.h> | ||||||
| 
 | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| @ -430,6 +431,10 @@ Allowed options)", | |||||||
| 			"Output a single json document containing the specified information." | 			"Output a single json document containing the specified information." | ||||||
| 		) | 		) | ||||||
| 		(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.") | 		(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", | 			"link", | ||||||
| 			"Switch to linker mode, ignoring all options apart from --libraries " | 			"Switch to linker mode, ignoring all options apart from --libraries " | ||||||
| @ -509,6 +514,12 @@ bool CommandLineInterface::processInput() | |||||||
| 			if (!parseLibraryOption(library)) | 			if (!parseLibraryOption(library)) | ||||||
| 				return false; | 				return false; | ||||||
| 
 | 
 | ||||||
|  | 	if (m_args.count("assemble")) | ||||||
|  | 	{ | ||||||
|  | 		// switch to assembly mode
 | ||||||
|  | 		m_onlyAssemble = true; | ||||||
|  | 		return assemble(); | ||||||
|  | 	} | ||||||
| 	if (m_args.count("link")) | 	if (m_args.count("link")) | ||||||
| 	{ | 	{ | ||||||
| 		// switch to linker mode
 | 		// switch to linker mode
 | ||||||
| @ -574,6 +585,7 @@ bool CommandLineInterface::processInput() | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader)); | 	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 | 	try | ||||||
| 	{ | 	{ | ||||||
| 		for (auto const& sourceCode: m_sourceCodes) | 		for (auto const& sourceCode: m_sourceCodes) | ||||||
| @ -593,7 +605,8 @@ bool CommandLineInterface::processInput() | |||||||
| 			SourceReferenceFormatter::printExceptionInformation( | 			SourceReferenceFormatter::printExceptionInformation( | ||||||
| 				cerr, | 				cerr, | ||||||
| 				*error, | 				*error, | ||||||
| 				(error->type() == Error::Type::Warning) ? "Warning" : "Error", *m_compiler | 				(error->type() == Error::Type::Warning) ? "Warning" : "Error", | ||||||
|  | 				scannerFromSourceName | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 		if (!successful) | 		if (!successful) | ||||||
| @ -601,7 +614,7 @@ bool CommandLineInterface::processInput() | |||||||
| 	} | 	} | ||||||
| 	catch (CompilerError const& _exception) | 	catch (CompilerError const& _exception) | ||||||
| 	{ | 	{ | ||||||
| 		SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", *m_compiler); | 		SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", scannerFromSourceName); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	catch (InternalCompilerError const& _exception) | 	catch (InternalCompilerError const& _exception) | ||||||
| @ -615,7 +628,7 @@ bool CommandLineInterface::processInput() | |||||||
| 		if (_error.type() == Error::Type::DocstringParsingError) | 		if (_error.type() == Error::Type::DocstringParsingError) | ||||||
| 			cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; | 			cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; | ||||||
| 		else | 		else | ||||||
| 			SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), *m_compiler); | 			SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), scannerFromSourceName); | ||||||
| 
 | 
 | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| @ -759,7 +772,9 @@ void CommandLineInterface::handleAst(string const& _argStr) | |||||||
| 
 | 
 | ||||||
| void CommandLineInterface::actOnInput() | void CommandLineInterface::actOnInput() | ||||||
| { | { | ||||||
| 	if (m_onlyLink) | 	if (m_onlyAssemble) | ||||||
|  | 		return; //@TODO
 | ||||||
|  | 	else if (m_onlyLink) | ||||||
| 		writeLinkedFiles(); | 		writeLinkedFiles(); | ||||||
| 	else | 	else | ||||||
| 		outputCompilationResults(); | 		outputCompilationResults(); | ||||||
| @ -812,6 +827,31 @@ void CommandLineInterface::writeLinkedFiles() | |||||||
| 			writeFile(src.first, src.second); | 			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() | void CommandLineInterface::outputCompilationResults() | ||||||
| { | { | ||||||
| 	handleCombinedJSON(); | 	handleCombinedJSON(); | ||||||
|  | |||||||
| @ -50,6 +50,9 @@ private: | |||||||
| 	bool link(); | 	bool link(); | ||||||
| 	void writeLinkedFiles(); | 	void writeLinkedFiles(); | ||||||
| 
 | 
 | ||||||
|  | 	/// Parse assembly input.
 | ||||||
|  | 	bool assemble(); | ||||||
|  | 
 | ||||||
| 	void outputCompilationResults(); | 	void outputCompilationResults(); | ||||||
| 
 | 
 | ||||||
| 	void handleCombinedJSON(); | 	void handleCombinedJSON(); | ||||||
| @ -73,6 +76,7 @@ private: | |||||||
| 	/// @arg _data to be written
 | 	/// @arg _data to be written
 | ||||||
| 	void createFile(std::string const& _fileName, std::string const& _data); | 	void createFile(std::string const& _fileName, std::string const& _data); | ||||||
| 
 | 
 | ||||||
|  | 	bool m_onlyAssemble = false; | ||||||
| 	bool m_onlyLink = false; | 	bool m_onlyLink = false; | ||||||
| 
 | 
 | ||||||
| 	/// Compiler arguments variable map
 | 	/// Compiler arguments variable map
 | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <functional> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <json/json.h> | #include <json/json.h> | ||||||
| #include <libdevcore/Common.h> | #include <libdevcore/Common.h> | ||||||
| @ -47,10 +48,14 @@ extern "C" { | |||||||
| typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); | 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; | 	ostringstream errorOutput; | ||||||
| 	SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler); | 	SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName); | ||||||
| 	return errorOutput.str(); | 	return errorOutput.str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -150,6 +155,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback | |||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 	CompilerStack compiler(true, readCallback); | 	CompilerStack compiler(true, readCallback); | ||||||
|  | 	auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return compiler.scanner(_sourceName); }; | ||||||
| 	bool success = false; | 	bool success = false; | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| @ -161,22 +167,22 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback | |||||||
| 			errors.append(formatError( | 			errors.append(formatError( | ||||||
| 				*error, | 				*error, | ||||||
| 				(err->type() == Error::Type::Warning) ? "Warning" : "Error", | 				(err->type() == Error::Type::Warning) ? "Warning" : "Error", | ||||||
| 				compiler | 				scannerFromSourceName | ||||||
| 			)); | 			)); | ||||||
| 		} | 		} | ||||||
| 		success = succ; // keep success false on exception
 | 		success = succ; // keep success false on exception
 | ||||||
| 	} | 	} | ||||||
| 	catch (Error const& error) | 	catch (Error const& error) | ||||||
| 	{ | 	{ | ||||||
| 		errors.append(formatError(error, error.typeName(), compiler)); | 		errors.append(formatError(error, error.typeName(), scannerFromSourceName)); | ||||||
| 	} | 	} | ||||||
| 	catch (CompilerError const& exception) | 	catch (CompilerError const& exception) | ||||||
| 	{ | 	{ | ||||||
| 		errors.append(formatError(exception, "Compiler error", compiler)); | 		errors.append(formatError(exception, "Compiler error", scannerFromSourceName)); | ||||||
| 	} | 	} | ||||||
| 	catch (InternalCompilerError const& exception) | 	catch (InternalCompilerError const& exception) | ||||||
| 	{ | 	{ | ||||||
| 		errors.append(formatError(exception, "Internal compiler error", compiler)); | 		errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName)); | ||||||
| 	} | 	} | ||||||
| 	catch (Exception const& exception) | 	catch (Exception const& exception) | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
							
								
								
									
										138
									
								
								test/libsolidity/InlineAssembly.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								test/libsolidity/InlineAssembly.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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
 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user