diff options
Diffstat (limited to 'libsolidity/inlineasm/AsmParser.cpp')
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 380 |
1 files changed, 310 insertions, 70 deletions
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index ef3da255..d282a30d 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -21,9 +21,10 @@ */ #include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/interface/ErrorReporter.h> #include <ctype.h> #include <algorithm> -#include <libsolidity/parsing/Scanner.h> using namespace std; using namespace dev; @@ -39,7 +40,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. } return nullptr; @@ -49,34 +50,60 @@ assembly::Block Parser::parseBlock() { assembly::Block block = createWithLocation<Block>(); expectToken(Token::LBrace); - while (m_scanner->currentToken() != Token::RBrace) + while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); block.location.end = endPosition(); - m_scanner->next(); + advance(); return block; } assembly::Statement Parser::parseStatement() { - switch (m_scanner->currentToken()) + switch (currentToken()) { case Token::Let: return parseVariableDeclaration(); + case Token::Function: + return parseFunctionDefinition(); case Token::LBrace: return parseBlock(); - case Token::Assign: + case Token::Switch: { - assembly::Assignment assignment = createWithLocation<assembly::Assignment>(); + assembly::Switch _switch = createWithLocation<assembly::Switch>(); m_scanner->next(); + _switch.expression = make_shared<Statement>(parseExpression()); + if (_switch.expression->type() == typeid(assembly::Instruction)) + fatalParserError("Instructions are not supported as expressions for switch."); + while (m_scanner->currentToken() == Token::Case) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + fatalParserError("Only one default case allowed."); + else if (m_scanner->currentToken() == Token::Case) + fatalParserError("Case not allowed after default case."); + if (_switch.cases.size() == 0) + fatalParserError("Switch statement without any cases."); + _switch.location.end = _switch.cases.back().body.location.end; + return _switch; + } + case Token::For: + return parseForLoop(); + case Token::Assign: + { + if (m_julia) + break; + assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>(); + advance(); expectToken(Token::Colon); assignment.variableName.location = location(); - assignment.variableName.name = m_scanner->currentLiteral(); + assignment.variableName.name = currentLiteral(); + if (!m_julia && instructions().count(assignment.variableName.name)) + fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return assignment; } - case Token::Return: // opcode - case Token::Byte: // opcode default: break; } @@ -84,53 +111,105 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - Statement statement(parseElementaryOperation()); - switch (m_scanner->currentToken()) + Statement statement(parseElementaryOperation(false)); + switch (currentToken()) { case Token::LParen: - return parseFunctionalInstruction(std::move(statement)); + return parseCall(std::move(statement)); case Token::Colon: { if (statement.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); - m_scanner->next(); + advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). - if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon) + if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { - // functional assignment - FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location); - m_scanner->next(); - funAss.variableName = identifier; - funAss.value.reset(new Statement(parseExpression())); - funAss.location.end = locationOf(*funAss.value).end; - return funAss; + assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location); + if (!m_julia && instructions().count(identifier.name)) + fatalParserError("Cannot use instruction names for identifier names."); + advance(); + assignment.variableName = identifier; + assignment.value.reset(new Statement(parseExpression())); + assignment.location.end = locationOf(*assignment.value).end; + return assignment; } else { // label + if (m_julia) + fatalParserError("Labels are not supported."); Label label = createWithLocation<Label>(identifier.location); label.name = identifier.name; return label; } } default: + if (m_julia) + fatalParserError("Call or assignment expected."); break; } return statement; } +assembly::Case Parser::parseCase() +{ + assembly::Case _case = createWithLocation<assembly::Case>(); + if (m_scanner->currentToken() == Token::Default) + m_scanner->next(); + else if (m_scanner->currentToken() == Token::Case) + { + m_scanner->next(); + assembly::Statement statement = parseElementaryOperation(); + if (statement.type() != typeid(assembly::Literal)) + fatalParserError("Literal expected."); + _case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement))); + } + else + fatalParserError("Case or default case expected."); + _case.body = parseBlock(); + _case.location.end = _case.body.location.end; + return _case; +} + +assembly::ForLoop Parser::parseForLoop() +{ + ForLoop forLoop = createWithLocation<ForLoop>(); + expectToken(Token::For); + forLoop.pre = parseBlock(); + forLoop.condition = make_shared<Statement>(parseExpression()); + if (forLoop.condition->type() == typeid(assembly::Instruction)) + fatalParserError("Instructions are not supported as conditions for the for statement."); + forLoop.post = parseBlock(); + forLoop.body = parseBlock(); + forLoop.location.end = forLoop.body.location.end; + return forLoop; +} + assembly::Statement Parser::parseExpression() { Statement operation = parseElementaryOperation(true); - if (m_scanner->currentToken() == Token::LParen) - return parseFunctionalInstruction(std::move(operation)); + if (operation.type() == typeid(Instruction)) + { + Instruction const& instr = boost::get<Instruction>(operation); + int args = instructionInfo(instr.instruction).args; + if (args > 0 && currentToken() != Token::LParen) + fatalParserError(string( + "Expected token \"(\" (\"" + + instructionNames().at(instr.instruction) + + "\" expects " + + boost::lexical_cast<string>(args) + + " arguments)" + )); + } + if (currentToken() == Token::LParen) + return parseCall(std::move(operation)); else return operation; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +std::map<string, dev::solidity::Instruction> const& Parser::instructions() { // Allowed instructions, lowercase names. static map<string, dev::solidity::Instruction> s_instructions; @@ -148,12 +227,32 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) s_instructions[name] = instruction.second; } - // add alias for selfdestruct - s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; + // add alias for suicide + s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT; + // add alis for sha3 + s_instructions["sha3"] = solidity::Instruction::KECCAK256; + } + return s_instructions; +} + +std::map<dev::solidity::Instruction, string> const& Parser::instructionNames() +{ + static map<dev::solidity::Instruction, string> s_instructionNames; + if (s_instructionNames.empty()) + { + for (auto const& instr: instructions()) + s_instructionNames[instr.second] = instr.first; + // set the ambiguous instructions to a clear default + s_instructionNames[solidity::Instruction::SELFDESTRUCT] = "selfdestruct"; + s_instructionNames[solidity::Instruction::KECCAK256] = "keccak256"; } + return s_instructionNames; +} +assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +{ Statement ret; - switch (m_scanner->currentToken()) + switch (currentToken()) { case Token::Identifier: case Token::Return: @@ -161,44 +260,78 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) case Token::Address: { string literal; - if (m_scanner->currentToken() == Token::Return) + if (currentToken() == Token::Return) literal = "return"; - else if (m_scanner->currentToken() == Token::Byte) + else if (currentToken() == Token::Byte) literal = "byte"; - else if (m_scanner->currentToken() == Token::Address) + else if (currentToken() == Token::Address) literal = "address"; else - literal = m_scanner->currentLiteral(); + literal = currentLiteral(); // first search the set of instructions. - if (s_instructions.count(literal)) + if (!m_julia && instructions().count(literal)) { - dev::solidity::Instruction const& instr = s_instructions[literal]; + dev::solidity::Instruction const& instr = instructions().at(literal); if (_onlySinglePusher) { InstructionInfo info = dev::solidity::instructionInfo(instr); if (info.ret != 1) - fatalParserError("Instruction " + info.name + " not allowed in this context."); + fatalParserError("Instruction \"" + literal + "\" not allowed in this context."); } ret = Instruction{location(), instr}; } else ret = Identifier{location(), literal}; + advance(); break; } case Token::StringLiteral: case Token::Number: + case Token::TrueLiteral: + case Token::FalseLiteral: { - ret = Literal{ + LiteralKind kind = LiteralKind::Number; + switch (currentToken()) + { + case Token::StringLiteral: + kind = LiteralKind::String; + break; + case Token::Number: + kind = LiteralKind::Number; + break; + case Token::TrueLiteral: + case Token::FalseLiteral: + kind = LiteralKind::Boolean; + break; + default: + break; + } + + Literal literal{ location(), - m_scanner->currentToken() == Token::Number, - m_scanner->currentLiteral() + kind, + currentLiteral(), + "" }; + advance(); + if (m_julia) + { + expectToken(Token::Colon); + literal.location.end = endPosition(); + literal.type = expectAsmIdentifier(); + } + else if (kind == LiteralKind::Boolean) + fatalParserError("True and false are not valid literals."); + ret = std::move(literal); break; } default: - fatalParserError("Expected elementary inline assembly operation."); + fatalParserError( + m_julia ? + "Literal or identifier expected." : + "Literal, identifier or instruction expected." + ); } - m_scanner->next(); return ret; } @@ -206,8 +339,14 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); expectToken(Token::Let); - varDecl.name = m_scanner->currentLiteral(); - expectToken(Token::Identifier); + while (true) + { + varDecl.variables.emplace_back(parseTypedName()); + if (currentToken() == Token::Comma) + expectToken(Token::Comma); + else + break; + } expectToken(Token::Colon); expectToken(Token::Assign); varDecl.value.reset(new Statement(parseExpression())); @@ -215,44 +354,145 @@ 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 (currentToken() != Token::RParen) + { + funDef.arguments.emplace_back(parseTypedName()); + if (currentToken() == Token::RParen) + break; + expectToken(Token::Comma); + } + expectToken(Token::RParen); + if (currentToken() == Token::Sub) { - ret.arguments.emplace_back(parseExpression()); - if (i != args - 1) + expectToken(Token::Sub); + expectToken(Token::GreaterThan); + while (true) { - if (m_scanner->currentToken() != Token::Comma) + funDef.returns.emplace_back(parseTypedName()); + if (currentToken() == Token::LBrace) + break; + expectToken(Token::Comma); + } + } + funDef.body = parseBlock(); + funDef.location.end = funDef.body.location.end; + return funDef; +} + +assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) +{ + if (_instruction.type() == typeid(Instruction)) + { + solAssert(!m_julia, "Instructions are invalid in JULIA"); + 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) + { + /// check for premature closing parentheses + if (currentToken() == Token::RParen) fatalParserError(string( - "Expected comma (" + - instrInfo.name + - " expects " + + "Expected expression (\"" + + instructionNames().at(instr) + + "\" expects " + boost::lexical_cast<string>(args) + " arguments)" )); - else - m_scanner->next(); + + ret.arguments.emplace_back(parseExpression()); + if (i != args - 1) + { + if (currentToken() != Token::Comma) + fatalParserError(string( + "Expected comma (\"" + + instructionNames().at(instr) + + "\" expects " + + boost::lexical_cast<string>(args) + + " arguments)" + )); + else + advance(); + } + } + ret.location.end = endPosition(); + if (currentToken() == Token::Comma) + fatalParserError(string( + "Expected ')' (\"" + + instructionNames().at(instr) + + "\" 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 (currentToken() != Token::RParen) + { + ret.arguments.emplace_back(parseExpression()); + if (currentToken() == Token::RParen) + break; + expectToken(Token::Comma); } + ret.location.end = endPosition(); + expectToken(Token::RParen); + return ret; } - ret.location.end = endPosition(); - if (m_scanner->currentToken() == Token::Comma) + else fatalParserError( - string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)") + m_julia ? + "Function name expected." : + "Assembly instruction or function name required in front of \"(\")" ); - expectToken(Token::RParen); - return ret; + + return {}; +} + +TypedName Parser::parseTypedName() +{ + TypedName typedName = createWithLocation<TypedName>(); + typedName.name = expectAsmIdentifier(); + if (m_julia) + { + expectToken(Token::Colon); + typedName.location.end = endPosition(); + typedName.type = expectAsmIdentifier(); + } + return typedName; +} + +string Parser::expectAsmIdentifier() +{ + string name = currentLiteral(); + if (m_julia) + { + if (currentToken() == Token::Bool) + { + advance(); + return name; + } + } + else if (instructions().count(name)) + fatalParserError("Cannot use instruction names for identifier names."); + expectToken(Token::Identifier); + return name; } |