diff options
Diffstat (limited to 'libsolidity/inlineasm')
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 45 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.h | 9 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmData.h | 18 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmDataForward.h | 14 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 159 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.h | 16 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.cpp | 13 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmPrinter.h | 1 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmScopeFiller.cpp | 11 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmScopeFiller.h | 1 |
10 files changed, 188 insertions, 99 deletions
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2804ddfc..2d6e58de 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -54,14 +54,15 @@ bool AsmAnalyzer::analyze(Block const& _block) bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); m_info.stackHeightInfo[&_label] = m_stackHeight; + warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; } bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instruction] = m_stackHeight; @@ -140,22 +141,34 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(!m_julia, ""); + solAssert(m_flavour != AsmFlavour::IULIA, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) success = false; // Parser already checks that the number of arguments is correct. - solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), ""); - if (!(*this)(_instr.instruction)) - success = false; + auto const& info = instructionInfo(_instr.instruction); + solAssert(info.args == int(_instr.arguments.size()), ""); + m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instr] = m_stackHeight; + warnOnInstructions(_instr.instruction, _instr.location); + return success; +} + +bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) +{ + size_t initialStackHeight = m_stackHeight; + bool success = boost::apply_visitor(*this, _statement.expression); + if (m_flavour != AsmFlavour::Loose) + if (!expectDeposit(0, initialStackHeight, _statement.location)) + success = false; + m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; @@ -217,14 +230,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); solAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) { expectValidType(var.type, var.location); m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name))); } int const stackHeight = m_stackHeight; - m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); + m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size(); bool success = (*this)(_funDef.body); @@ -405,13 +418,13 @@ bool AsmAnalyzer::operator()(Block const& _block) return success; } -bool AsmAnalyzer::expectExpression(Statement const& _statement) +bool AsmAnalyzer::expectExpression(Expression const& _expr) { bool success = true; int const initialHeight = m_stackHeight; - if (!boost::apply_visitor(*this, _statement)) + if (!boost::apply_visitor(*this, _expr)) success = false; - if (!expectDeposit(1, initialHeight, locationOf(_statement))) + if (!expectDeposit(1, initialHeight, locationOf(_expr))) success = false; return success; } @@ -495,7 +508,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) { - if (!m_julia) + if (m_flavour != AsmFlavour::IULIA) return; if (!builtinTypes.count(type)) @@ -522,11 +535,11 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); - if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI) + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, - "Jump instructions are low-level EVM features that can lead to " + "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " - "Please consider using \"switch\" or \"for\" statements instead." + "Please consider using \"switch\", \"if\" or \"for\" statements instead." ); } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index e484b876..7a81dbf8 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -54,9 +54,9 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, - bool _julia = false, + AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {} bool analyze(assembly::Block const& _block); @@ -65,6 +65,7 @@ public: bool operator()(assembly::Identifier const&); bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); bool operator()(assembly::Label const& _label); + bool operator()(assembly::ExpressionStatement const&); bool operator()(assembly::StackAssignment const&); bool operator()(assembly::Assignment const& _assignment); bool operator()(assembly::VariableDeclaration const& _variableDeclaration); @@ -77,7 +78,7 @@ public: private: /// Visits the statement and expects it to deposit one item onto the stack. - bool expectExpression(Statement const& _statement); + bool expectExpression(Expression const& _expr); bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); /// Verifies that a variable to be assigned to exists and has the same size @@ -96,7 +97,7 @@ private: std::set<Scope::Variable const*> m_activeVariables; AsmAnalysisInfo& m_info; ErrorReporter& m_errorReporter; - bool m_julia = false; + AsmFlavour m_flavour = AsmFlavour::Loose; }; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index a792a1b8..2982d5e0 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Statement> value; }; +struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; }; -struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; }; +struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Expression> arguments; }; +struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Expression> arguments; }; +/// Statement that contains only a single expression +struct ExpressionStatement { SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; }; +struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> 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; TypedNameList arguments; TypedNameList returns; Block body; }; +struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { SourceLocation location; std::shared_ptr<Statement> condition; Block body; }; +struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; /// Switch case or default case struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; }; /// Switch statement -struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; }; -struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Statement> condition; Block post; Block body; }; +struct Switch { SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; +struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; struct LocationExtractor: boost::static_visitor<SourceLocation> { diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index d627b41a..3a9600fe 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -43,10 +43,22 @@ struct FunctionDefinition; struct FunctionCall; struct If; struct Switch; +struct Case; struct ForLoop; +struct ExpressionStatement; struct Block; -using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; +struct TypedName; + +using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; +using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; + +enum class AsmFlavour +{ + Loose, // no types, EVM instructions as function, jumps and direct stack manipulations + Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations + IULIA // same as Strict mode with types +}; } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 8f171005..306b07e6 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement() { assembly::If _if = createWithLocation<assembly::If>(); m_scanner->next(); - _if.condition = make_shared<Statement>(parseExpression()); - if (_if.condition->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as conditions for if - try to append \"()\"."); + _if.condition = make_shared<Expression>(parseExpression()); _if.body = parseBlock(); return _if; } @@ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement() { 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 - try to append \"()\"."); + _switch.expression = make_shared<Expression>(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) @@ -107,14 +103,14 @@ assembly::Statement Parser::parseStatement() return parseForLoop(); case Token::Assign: { - if (m_julia) + if (m_flavour != AsmFlavour::Loose) break; assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = currentLiteral(); - if (!m_julia && instructions().count(assignment.variableName.name)) + if (instructions().count(assignment.variableName.name)) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); @@ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - Statement statement(parseElementaryOperation(false)); + ElementaryOperation elementary(parseElementaryOperation()); switch (currentToken()) { case Token::LParen: - return parseCall(std::move(statement)); + { + Expression expr = parseCall(std::move(elementary)); + return ExpressionStatement{locationOf(expr), expr}; + } case Token::Comma: { // if a comma follows, a multiple assignment is assumed - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); - assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); + assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary); Assignment assignment = createWithLocation<Assignment>(identifier.location); assignment.variableNames.emplace_back(identifier); @@ -146,43 +145,43 @@ assembly::Statement Parser::parseStatement() do { expectToken(Token::Comma); - statement = parseElementaryOperation(false); - if (statement.type() != typeid(assembly::Identifier)) + elementary = parseElementaryOperation(); + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignemnt."); - assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(statement)); + assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary)); } while (currentToken() == Token::Comma); expectToken(Token::Colon); expectToken(Token::Assign); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } case Token::Colon: { - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); - assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); + assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary); advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location); - if (!m_julia && instructions().count(identifier.name)) + if (m_flavour != AsmFlavour::IULIA && instructions().count(identifier.name)) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } else { // label - if (m_julia) + if (m_flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation<Label>(identifier.location); label.name = identifier.name; @@ -190,11 +189,25 @@ assembly::Statement Parser::parseStatement() } } default: - if (m_julia) + if (m_flavour != AsmFlavour::Loose) fatalParserError("Call or assignment expected."); break; } - return statement; + if (elementary.type() == typeid(assembly::Identifier)) + { + Expression expr = boost::get<assembly::Identifier>(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else if (elementary.type() == typeid(assembly::Literal)) + { + Expression expr = boost::get<assembly::Literal>(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else + { + solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation."); + return boost::get<assembly::Instruction>(elementary); + } } assembly::Case Parser::parseCase() @@ -206,10 +219,10 @@ assembly::Case Parser::parseCase() else if (m_scanner->currentToken() == Token::Case) { m_scanner->next(); - assembly::Statement statement = parseElementaryOperation(); - if (statement.type() != typeid(assembly::Literal)) + ElementaryOperation literal = parseElementaryOperation(); + if (literal.type() != typeid(assembly::Literal)) fatalParserError("Literal expected."); - _case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement))); + _case.value = make_shared<Literal>(boost::get<assembly::Literal>(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -224,22 +237,39 @@ 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.condition = make_shared<Expression>(parseExpression()); forLoop.post = parseBlock(); forLoop.body = parseBlock(); forLoop.location.end = forLoop.body.location.end; return forLoop; } -assembly::Statement Parser::parseExpression() +assembly::Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - Statement operation = parseElementaryOperation(true); + // In strict mode, this might parse a plain Instruction, but + // it will be converted to a FunctionalInstruction inside + // parseCall below. + ElementaryOperation operation = parseElementaryOperation(); if (operation.type() == typeid(Instruction)) { Instruction const& instr = boost::get<Instruction>(operation); + // Disallow instructions returning multiple values (and DUP/SWAP) as expression. + if ( + instructionInfo(instr.instruction).ret != 1 || + isDupInstruction(instr.instruction) || + isSwapInstruction(instr.instruction) + ) + fatalParserError( + "Instruction \"" + + instructionNames().at(instr.instruction) + + "\" not allowed in this context." + ); + if (m_flavour != AsmFlavour::Loose && currentToken() != Token::LParen) + fatalParserError( + "Non-functional instructions are not allowed in this context." + ); + // Enforce functional notation for instructions requiring multiple arguments. int args = instructionInfo(instr.instruction).args; if (args > 0 && currentToken() != Token::LParen) fatalParserError(string( @@ -252,8 +282,20 @@ assembly::Statement Parser::parseExpression() } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); + else if (operation.type() == typeid(Instruction)) + { + // Instructions not taking arguments are allowed as expressions. + solAssert(m_flavour == AsmFlavour::Loose, ""); + Instruction& instr = boost::get<Instruction>(operation); + return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; + } + else if (operation.type() == typeid(assembly::Identifier)) + return boost::get<assembly::Identifier>(operation); else - return operation; + { + solAssert(operation.type() == typeid(assembly::Literal), ""); + return boost::get<assembly::Literal>(operation); + } } std::map<string, dev::solidity::Instruction> const& Parser::instructions() @@ -296,10 +338,10 @@ std::map<dev::solidity::Instruction, string> const& Parser::instructionNames() return s_instructionNames; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +Parser::ElementaryOperation Parser::parseElementaryOperation() { RecursionGuard recursionGuard(*this); - Statement ret; + ElementaryOperation ret; switch (currentToken()) { case Token::Identifier: @@ -317,15 +359,9 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) else literal = currentLiteral(); // first search the set of instructions. - if (!m_julia && instructions().count(literal)) + if (m_flavour != AsmFlavour::IULIA && instructions().count(literal)) { dev::solidity::Instruction const& instr = instructions().at(literal); - if (_onlySinglePusher) - { - InstructionInfo info = dev::solidity::instructionInfo(instr); - if (info.ret != 1) - fatalParserError("Instruction \"" + literal + "\" not allowed in this context."); - } ret = Instruction{location(), instr}; } else @@ -364,7 +400,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) "" }; advance(); - if (m_julia) + if (m_flavour == AsmFlavour::IULIA) { expectToken(Token::Colon); literal.location.end = endPosition(); @@ -377,7 +413,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) } default: fatalParserError( - m_julia ? + m_flavour == AsmFlavour::IULIA ? "Literal or identifier expected." : "Literal, identifier or instruction expected." ); @@ -402,7 +438,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::Colon); expectToken(Token::Assign); - varDecl.value.reset(new Statement(parseExpression())); + varDecl.value.reset(new Expression(parseExpression())); varDecl.location.end = locationOf(*varDecl.value).end; } else @@ -419,7 +455,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::LParen); while (currentToken() != Token::RParen) { - funDef.arguments.emplace_back(parseTypedName()); + funDef.parameters.emplace_back(parseTypedName()); if (currentToken() == Token::RParen) break; expectToken(Token::Comma); @@ -431,7 +467,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::GreaterThan); while (true) { - funDef.returns.emplace_back(parseTypedName()); + funDef.returnVariables.emplace_back(parseTypedName()); if (currentToken() == Token::LBrace) break; expectToken(Token::Comma); @@ -442,16 +478,17 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() return funDef; } -assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) +assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) { RecursionGuard recursionGuard(*this); - if (_instruction.type() == typeid(Instruction)) + if (_initialOp.type() == typeid(Instruction)) { - solAssert(!m_julia, "Instructions are invalid in JULIA"); + solAssert(m_flavour != AsmFlavour::IULIA, "Instructions are invalid in JULIA"); + Instruction& instruction = boost::get<Instruction>(_initialOp); FunctionalInstruction ret; - ret.instruction = std::move(boost::get<Instruction>(_instruction)); - ret.location = ret.instruction.location; - solidity::Instruction instr = ret.instruction.instruction; + ret.instruction = instruction.instruction; + ret.location = std::move(instruction.location); + solidity::Instruction instr = ret.instruction; InstructionInfo instrInfo = instructionInfo(instr); if (solidity::isDupInstruction(instr)) fatalParserError("DUPi instructions not allowed for functional notation"); @@ -498,10 +535,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) expectToken(Token::RParen); return ret; } - else if (_instruction.type() == typeid(Identifier)) + else if (_initialOp.type() == typeid(Identifier)) { FunctionCall ret; - ret.functionName = std::move(boost::get<Identifier>(_instruction)); + ret.functionName = std::move(boost::get<Identifier>(_initialOp)); ret.location = ret.functionName.location; expectToken(Token::LParen); while (currentToken() != Token::RParen) @@ -517,7 +554,7 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) } else fatalParserError( - m_julia ? + m_flavour == AsmFlavour::IULIA ? "Function name expected." : "Assembly instruction or function name required in front of \"(\")" ); @@ -530,7 +567,7 @@ TypedName Parser::parseTypedName() RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation<TypedName>(); typedName.name = expectAsmIdentifier(); - if (m_julia) + if (m_flavour == AsmFlavour::IULIA) { expectToken(Token::Colon); typedName.location.end = endPosition(); @@ -542,12 +579,18 @@ TypedName Parser::parseTypedName() string Parser::expectAsmIdentifier() { string name = currentLiteral(); - if (m_julia) + if (m_flavour == AsmFlavour::IULIA) { - if (currentToken() == Token::Bool) + switch (currentToken()) { + case Token::Return: + case Token::Byte: + case Token::Address: + case Token::Bool: advance(); return name; + default: + break; } } else if (instructions().count(name)) diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index e46d1732..015aeef3 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -37,13 +37,16 @@ namespace assembly class Parser: public ParserBase { public: - explicit Parser(ErrorReporter& _errorReporter, bool _julia = false): ParserBase(_errorReporter), m_julia(_julia) {} + explicit Parser(ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose): + ParserBase(_errorReporter), m_flavour(_flavour) {} /// Parses an inline assembly block starting with `{` and ending with `}`. /// @returns an empty shared pointer on error. std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner); protected: + using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>; + /// Creates an inline assembly node with the given source location. template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const { @@ -65,20 +68,23 @@ protected: Case parseCase(); ForLoop parseForLoop(); /// Parses a functional expression that has to push exactly one stack element - Statement parseExpression(); + assembly::Expression parseExpression(); static std::map<std::string, dev::solidity::Instruction> const& instructions(); static std::map<dev::solidity::Instruction, std::string> const& instructionNames(); - Statement parseElementaryOperation(bool _onlySinglePusher = false); + /// Parses an elementary operation, i.e. a literal, identifier or instruction. + /// This will parse instructions even in strict mode as part of the full parser + /// for FunctionalInstruction. + ElementaryOperation parseElementaryOperation(); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); - Statement parseCall(Statement&& _instruction); + assembly::Expression parseCall(ElementaryOperation&& _initialOp); TypedName parseTypedName(); std::string expectAsmIdentifier(); static bool isValidNumberLiteral(std::string const& _literal); private: - bool m_julia = false; + AsmFlavour m_flavour = AsmFlavour::Loose; }; } diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 0f183244..bacd7a94 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -94,7 +94,7 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional { solAssert(!m_julia, ""); return - (*this)(_functionalInstruction.instruction) + + boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), @@ -102,6 +102,11 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional ")"; } +string AsmPrinter::operator()(ExpressionStatement const& _statement) +{ + return boost::apply_visitor(*this, _statement.expression); +} + string AsmPrinter::operator()(assembly::Label const& _label) { solAssert(!m_julia, ""); @@ -144,17 +149,17 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin { string out = "function " + _functionDefinition.name + "("; out += boost::algorithm::join( - _functionDefinition.arguments | boost::adaptors::transformed( + _functionDefinition.parameters | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " ); out += ")"; - if (!_functionDefinition.returns.empty()) + if (!_functionDefinition.returnVariables.empty()) { out += " -> "; out += boost::algorithm::join( - _functionDefinition.returns | boost::adaptors::transformed( + _functionDefinition.returnVariables | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index eadf81d9..5bd87aba 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -42,6 +42,7 @@ public: std::string operator()(assembly::Literal const& _literal); std::string operator()(assembly::Identifier const& _identifier); std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction); + std::string operator()(assembly::ExpressionStatement const& _expr); std::string operator()(assembly::Label const& _label); std::string operator()(assembly::StackAssignment const& _assignment); std::string operator()(assembly::Assignment const& _assignment); diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 77ae9102..86f3809c 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): m_currentScope = &scope(nullptr); } +bool ScopeFiller::operator()(ExpressionStatement const& _expr) +{ + return boost::apply_visitor(*this, _expr.expression); +} + bool ScopeFiller::operator()(Label const& _item) { if (!m_currentScope->registerLabel(_item.name)) @@ -71,10 +76,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; vector<Scope::JuliaType> arguments; - for (auto const& _argument: _funDef.arguments) + for (auto const& _argument: _funDef.parameters) arguments.push_back(_argument.type); vector<Scope::JuliaType> returns; - for (auto const& _return: _funDef.returns) + for (auto const& _return: _funDef.returnVariables) returns.push_back(_return.type); if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { @@ -91,7 +96,7 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) varScope.superScope = m_currentScope; m_currentScope = &varScope; varScope.functionScope = true; - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) if (!registerVariable(var, _funDef.location, varScope)) success = false; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index ed28abbf..bb023f61 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -53,6 +53,7 @@ public: bool operator()(assembly::Literal const&) { return true; } bool operator()(assembly::Identifier const&) { return true; } bool operator()(assembly::FunctionalInstruction const&) { return true; } + bool operator()(assembly::ExpressionStatement const& _expr); bool operator()(assembly::Label const& _label); bool operator()(assembly::StackAssignment const&) { return true; } bool operator()(assembly::Assignment const&) { return true; } |