aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp9
-rw-r--r--libsolidity/inlineasm/AsmData.h7
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp138
-rw-r--r--libsolidity/inlineasm/AsmParser.h4
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp18
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h2
-rw-r--r--test/libsolidity/InlineAssembly.cpp25
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)