diff options
author | chriseth <chris@ethereum.org> | 2017-02-17 03:12:31 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-17 03:12:31 +0800 |
commit | 7bdc4ddab2be7990d09f102606f788f054da41db (patch) | |
tree | aadf29b475d77125da7101eeebaba7a4c6d8638a | |
parent | 0ad8e53404514413761b198f42424cb2c1989b9a (diff) | |
parent | 01fcd989b57d31fa8fc63720401ffc77698ee57b (diff) | |
download | dexon-solidity-7bdc4ddab2be7990d09f102606f788f054da41db.tar.gz dexon-solidity-7bdc4ddab2be7990d09f102606f788f054da41db.tar.zst dexon-solidity-7bdc4ddab2be7990d09f102606f788f054da41db.zip |
Merge pull request #1627 from ethereum/asmfunctions
Parsing assembly function definitions and calls.
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 9 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmData.h | 7 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 138 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.h | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.cpp | 18 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.h | 2 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 25 |
7 files changed, 162 insertions, 41 deletions
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 43c3b27a..faa7dabd 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -190,6 +190,10 @@ public: } (*this)(_instr.instruction); } + void operator()(assembly::FunctionCall const&) + { + solAssert(false, "Function call not removed during desugaring phase."); + } void operator()(Label const& _label) { m_state.assembly.setSourceLocation(_label.location); @@ -249,7 +253,10 @@ public: _block.location ); } - + } + void operator()(assembly::FunctionDefinition const&) + { + solAssert(false, "Function definition not removed during desugaring phase."); } private: diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index d622ff54..d61b5803 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -48,17 +48,22 @@ struct Label { SourceLocation location; std::string name; }; struct Assignment { SourceLocation location; Identifier variableName; }; struct FunctionalAssignment; struct VariableDeclaration; +struct FunctionDefinition; +struct FunctionCall; struct Block; -using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, Block>; +using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, 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 { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; }; /// Functional instruction, e.g. "mul(mload(20), add(2, x))" struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; }; +struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; /// Block-scope variable declaration ("let x := mload(20)"), non-hoisted struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector<Statement> statements; }; +/// Function definition ("function f(a, b) -> (d, e) { ... }") +struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; }; struct LocationExtractor: boost::static_visitor<SourceLocation> { diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 46a2730d..0fc0a34f 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -62,6 +62,8 @@ assembly::Statement Parser::parseStatement() { case Token::Let: return parseVariableDeclaration(); + case Token::Function: + return parseFunctionDefinition(); case Token::LBrace: return parseBlock(); case Token::Assign: @@ -214,10 +216,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); expectToken(Token::Let); - varDecl.name = m_scanner->currentLiteral(); - if (instructions().count(varDecl.name)) - fatalParserError("Cannot use instruction names for identifier names."); - expectToken(Token::Identifier); + varDecl.name = expectAsmIdentifier(); expectToken(Token::Colon); expectToken(Token::Assign); varDecl.value.reset(new Statement(parseExpression())); @@ -225,44 +224,107 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() return varDecl; } -FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction) +assembly::FunctionDefinition Parser::parseFunctionDefinition() { - if (_instruction.type() != typeid(Instruction)) - fatalParserError("Assembly instruction required in front of \"(\")"); - FunctionalInstruction ret; - ret.instruction = std::move(boost::get<Instruction>(_instruction)); - ret.location = ret.instruction.location; - solidity::Instruction instr = ret.instruction.instruction; - InstructionInfo instrInfo = instructionInfo(instr); - if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16) - fatalParserError("DUPi instructions not allowed for functional notation"); - if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16) - fatalParserError("SWAPi instructions not allowed for functional notation"); - + FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); + expectToken(Token::Function); + funDef.name = expectAsmIdentifier(); expectToken(Token::LParen); - unsigned args = unsigned(instrInfo.args); - for (unsigned i = 0; i < args; ++i) + while (m_scanner->currentToken() != Token::RParen) { - ret.arguments.emplace_back(parseExpression()); - if (i != args - 1) + funDef.arguments.push_back(expectAsmIdentifier()); + if (m_scanner->currentToken() == Token::RParen) + break; + expectToken(Token::Comma); + } + expectToken(Token::RParen); + if (m_scanner->currentToken() == Token::Sub) + { + expectToken(Token::Sub); + expectToken(Token::GreaterThan); + expectToken(Token::LParen); + while (true) { - if (m_scanner->currentToken() != Token::Comma) - fatalParserError(string( - "Expected comma (" + - instrInfo.name + - " expects " + - boost::lexical_cast<string>(args) + - " arguments)" - )); - else - m_scanner->next(); + funDef.returns.push_back(expectAsmIdentifier()); + if (m_scanner->currentToken() == Token::RParen) + break; + expectToken(Token::Comma); } + expectToken(Token::RParen); } - ret.location.end = endPosition(); - if (m_scanner->currentToken() == Token::Comma) - fatalParserError( - string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)") - ); - expectToken(Token::RParen); - return ret; + funDef.body = parseBlock(); + funDef.location.end = funDef.body.location.end; + return funDef; +} + +assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction) +{ + if (_instruction.type() == typeid(Instruction)) + { + FunctionalInstruction ret; + ret.instruction = std::move(boost::get<Instruction>(_instruction)); + ret.location = ret.instruction.location; + solidity::Instruction instr = ret.instruction.instruction; + InstructionInfo instrInfo = instructionInfo(instr); + if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16) + fatalParserError("DUPi instructions not allowed for functional notation"); + if (solidity::Instruction::SWAP1 <= instr && instr <= solidity::Instruction::SWAP16) + fatalParserError("SWAPi instructions not allowed for functional notation"); + expectToken(Token::LParen); + unsigned args = unsigned(instrInfo.args); + for (unsigned i = 0; i < args; ++i) + { + ret.arguments.emplace_back(parseExpression()); + if (i != args - 1) + { + if (m_scanner->currentToken() != Token::Comma) + fatalParserError(string( + "Expected comma (" + + instrInfo.name + + " expects " + + boost::lexical_cast<string>(args) + + " arguments)" + )); + else + m_scanner->next(); + } + } + ret.location.end = endPosition(); + if (m_scanner->currentToken() == Token::Comma) + fatalParserError( + string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)") + ); + expectToken(Token::RParen); + return ret; + } + else if (_instruction.type() == typeid(Identifier)) + { + FunctionCall ret; + ret.functionName = std::move(boost::get<Identifier>(_instruction)); + ret.location = ret.functionName.location; + expectToken(Token::LParen); + while (m_scanner->currentToken() != Token::RParen) + { + ret.arguments.emplace_back(parseExpression()); + if (m_scanner->currentToken() == Token::RParen) + break; + expectToken(Token::Comma); + } + ret.location.end = endPosition(); + expectToken(Token::RParen); + return ret; + } + else + fatalParserError("Assembly instruction or function name required in front of \"(\")"); + + return {}; +} + +string Parser::expectAsmIdentifier() +{ + string name = m_scanner->currentLiteral(); + if (instructions().count(name)) + fatalParserError("Cannot use instruction names for identifier names."); + expectToken(Token::Identifier); + return name; } diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 643548dd..4b4a24ae 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -67,7 +67,9 @@ protected: std::map<std::string, dev::solidity::Instruction> const& instructions(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); - FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); + FunctionDefinition parseFunctionDefinition(); + Statement parseFunctionalInstruction(Statement&& _instruction); + std::string expectAsmIdentifier(); }; } diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index ab2a03ff..a70b0b78 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -112,6 +112,24 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value); } +string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) +{ + string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")"; + if (!_functionDefinition.returns.empty()) + out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")"; + return out + "\n" + (*this)(_functionDefinition.body); +} + +string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall) +{ + return + (*this)(_functionCall.functionName) + "(" + + boost::algorithm::join( + _functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), + ", " ) + + ")"; +} + string AsmPrinter::operator()(Block const& _block) { if (_block.statements.empty()) diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 39069d02..a7a1de0a 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -53,6 +53,8 @@ public: std::string operator()(assembly::Assignment const& _assignment); std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment); std::string operator()(assembly::VariableDeclaration const& _variableDeclaration); + std::string operator()(assembly::FunctionDefinition const& _functionDefinition); + std::string operator()(assembly::FunctionCall const& _functionCall); std::string operator()(assembly::Block const& _block); }; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8744d96f..ddbc3c63 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -159,6 +159,21 @@ BOOST_AUTO_TEST_CASE(blocks) BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); } +BOOST_AUTO_TEST_CASE(function_definitions) +{ + BOOST_CHECK(successParse("{ function f() { } function g(a) -> (x) { } }")); +} + +BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) +{ + BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> (x, y) { } }")); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + BOOST_CHECK(successParse("{ g(1, 2, f(mul(2, 3))) x() }")); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Printing) @@ -209,6 +224,16 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) parsePrintCompare(parsed); } +BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) +{ + parsePrintCompare("{\n function f(a, d)\n {\n mstore(a, d)\n }\n function g(a, d) -> (x, y)\n {\n }\n}"); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + parsePrintCompare("{\n g(1, mul(2, x), f(mul(2, 3)))\n x()\n}"); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Analysis) |