diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 61 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.h | 2 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmData.h | 33 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 92 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.h | 18 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmStack.cpp | 10 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmStack.h | 2 |
7 files changed, 149 insertions, 69 deletions
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 5fa04087..d571ce0d 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -86,8 +86,12 @@ public: void operator()(Label const& _item) { if (m_state.labels.count(_item.name)) - //@TODO location and secondary location - m_state.addError(Error::Type::DeclarationError, "Label " + _item.name + " declared twice."); + //@TODO secondary location + m_state.addError( + Error::Type::DeclarationError, + "Label " + _item.name + " declared twice.", + _item.location + ); m_state.labels.insert(make_pair(_item.name, m_state.assembly.newTag())); } void operator()(assembly::Block const& _block) @@ -117,34 +121,43 @@ public: m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; }; } - void operator()(dev::solidity::assembly::Instruction const& _instruction) + void operator()(assembly::Instruction const& _instruction) { + m_state.assembly.setSourceLocation(_instruction.location); m_state.assembly.append(_instruction.instruction); } void operator()(assembly::Literal const& _literal) { + m_state.assembly.setSourceLocation(_literal.location); if (_literal.isNumber) m_state.assembly.append(u256(_literal.value)); else if (_literal.value.size() > 32) + { m_state.addError( Error::Type::TypeError, "String literal too long (" + boost::lexical_cast<string>(_literal.value.size()) + " > 32)" ); + m_state.assembly.append(u256(0)); + } else m_state.assembly.append(_literal.value); } void operator()(assembly::Identifier const& _identifier) { + m_state.assembly.setSourceLocation(_identifier.location); // First search local variables, then labels, then externals. if (int const* stackHeight = m_state.findVariable(_identifier.name)) { int heightDiff = m_state.assembly.deposit() - *stackHeight; if (heightDiff <= 0 || heightDiff > 16) - //@TODO location + { m_state.addError( Error::Type::TypeError, - "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")" + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", + _identifier.location ); + m_state.assembly.append(u256(0)); + } else m_state.assembly.append(solidity::dupInstruction(heightDiff)); return; @@ -152,10 +165,14 @@ public: else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name)) m_state.assembly.append(label->pushTag()); else if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue)) + { m_state.addError( Error::Type::DeclarationError, - "Identifier \"" + string(_identifier.name) + "\" not found or not unique" + "Identifier not found or not unique", + _identifier.location ); + m_state.assembly.append(u256(0)); + } } void operator()(FunctionalInstruction const& _instr) { @@ -163,30 +180,33 @@ public: { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *it); - expectDeposit(1, height); + expectDeposit(1, height, locationOf(*it)); } (*this)(_instr.instruction); } void operator()(Label const& _label) { + m_state.assembly.setSourceLocation(_label.location); m_state.assembly.append(m_state.labels.at(_label.name)); } void operator()(assembly::Assignment const& _assignment) { - generateAssignment(_assignment.variableName); + m_state.assembly.setSourceLocation(_assignment.location); + generateAssignment(_assignment.variableName, _assignment.location); } void operator()(FunctionalAssignment const& _assignment) { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *_assignment.value); - expectDeposit(1, height); - generateAssignment(_assignment.variableName); + expectDeposit(1, height, locationOf(*_assignment.value)); + m_state.assembly.setSourceLocation(_assignment.location); + generateAssignment(_assignment.variableName, _assignment.location); } void operator()(assembly::VariableDeclaration const& _varDecl) { int height = m_state.assembly.deposit(); boost::apply_visitor(*this, *_varDecl.value); - expectDeposit(1, height); + expectDeposit(1, height, locationOf(*_varDecl.value)); m_state.variables.push_back(make_pair(_varDecl.name, height)); } void operator()(assembly::Block const& _block) @@ -194,7 +214,8 @@ public: size_t numVariables = m_state.variables.size(); std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); // pop variables - //@TODO check height before and after + // we deliberately do not check stack height + m_state.assembly.setSourceLocation(_block.location); while (m_state.variables.size() > numVariables) { m_state.assembly.append(solidity::Instruction::POP); @@ -203,22 +224,20 @@ public: } private: - void generateAssignment(assembly::Identifier const& _variableName) + void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location) { if (int const* stackHeight = m_state.findVariable(_variableName.name)) { int heightDiff = m_state.assembly.deposit() - *stackHeight - 1; if (heightDiff <= 0 || heightDiff > 16) - //@TODO location m_state.addError( Error::Type::TypeError, - "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")" + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")", + _location ); else - { m_state.assembly.append(solidity::swapInstruction(heightDiff)); - m_state.assembly.append(solidity::Instruction::POP); - } + m_state.assembly.append(solidity::Instruction::POP); return; } else if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue)) @@ -228,16 +247,16 @@ private: ); } - void expectDeposit(int _deposit, int _oldHeight) + void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location) { if (m_state.assembly.deposit() != _oldHeight + 1) - //@TODO location m_state.addError(Error::Type::TypeError, "Expected instruction(s) to deposit " + boost::lexical_cast<string>(_deposit) + " item(s) to the stack, but did deposit " + boost::lexical_cast<string>(m_state.assembly.deposit() - _oldHeight) + - " item(s)." + " item(s).", + _location ); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index f749ba50..aaabda45 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -48,7 +48,7 @@ public: /// If in rvalue context, the function is assumed to append instructions to /// push the value of the identifier onto the stack. On error, the function should return false. using IdentifierAccess = std::function<bool(assembly::Identifier const&, eth::Assembly&, IdentifierContext)>; - CodeGenerator( Block const& _parsedData, ErrorList& _errors): + CodeGenerator(Block const& _parsedData, ErrorList& _errors): m_parsedData(_parsedData), m_errors(_errors) {} /// Performs type checks and @returns false on error. /// Actually runs the full code generation but discards the result. diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 42f0ae31..d6abf67f 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -24,6 +24,7 @@ #include <boost/variant.hpp> #include <libevmasm/Instruction.h> +#include <libevmasm/SourceLocation.h> namespace dev { @@ -35,29 +36,43 @@ namespace assembly /// What follows are the AST nodes for assembly. /// Direct EVM instruction (except PUSHi and JUMPDEST) -struct Instruction { solidity::Instruction instruction; }; +struct Instruction { SourceLocation location; solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) -struct Literal { bool isNumber; std::string value; }; +struct Literal { SourceLocation location; bool isNumber; std::string value; }; /// External / internal identifier or label reference -struct Identifier { std::string name; }; +struct Identifier { SourceLocation location; std::string name; }; struct FunctionalInstruction; /// Jump label ("name:") -struct Label { std::string name; }; +struct Label { SourceLocation location; std::string name; }; /// Assignemnt (":= x", moves stack top into x, potentially multiple slots) -struct Assignment { Identifier variableName; }; +struct Assignment { SourceLocation location; 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; }; +struct FunctionalAssignment { SourceLocation location; 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; }; +struct FunctionalInstruction { SourceLocation location; 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; }; +struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; }; /// Block that creates a scope (frees declared stack variables) -struct Block { std::vector<Statement> statements; }; +struct Block { SourceLocation location; std::vector<Statement> statements; }; + +struct LocationExtractor: boost::static_visitor<SourceLocation> +{ + template <class T> SourceLocation operator()(T const& _node) const + { + return _node.location; + } +}; + +/// Extracts the source location from an inline assembly node. +template <class T> inline SourceLocation locationOf(T const& _node) +{ + return boost::apply_visitor(LocationExtractor(), _node); +} } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 33c8efa0..5c7163ee 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -35,7 +35,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann try { m_scanner = _scanner; - return make_shared<assembly::Block>(parseBlock()); + return make_shared<Block>(parseBlock()); } catch (FatalError const&) { @@ -47,10 +47,11 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann assembly::Block Parser::parseBlock() { + assembly::Block block = createWithLocation<Block>(); expectToken(Token::LBrace); - Block block; while (m_scanner->currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); + block.location.end = endPosition(); m_scanner->next(); return block; } @@ -65,11 +66,14 @@ assembly::Statement Parser::parseStatement() return parseBlock(); case Token::Assign: { + assembly::Assignment assignment = createWithLocation<assembly::Assignment>(); m_scanner->next(); expectToken(Token::Colon); - string name = m_scanner->currentLiteral(); + assignment.variableName.location = location(); + assignment.variableName.name = m_scanner->currentLiteral(); + assignment.location.end = endPosition(); expectToken(Token::Identifier); - return assembly::Assignment{assembly::Identifier{name}}; + return assignment; } case Token::Return: // opcode case Token::Byte: // opcode @@ -84,24 +88,30 @@ assembly::Statement Parser::parseStatement() switch (m_scanner->currentToken()) { case Token::LParen: - return parseFunctionalInstruction(statement); + return parseFunctionalInstruction(std::move(statement)); case Token::Colon: { if (statement.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); - string const& name = boost::get<assembly::Identifier>(statement).name; + assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); m_scanner->next(); if (m_scanner->currentToken() == Token::Assign) { // functional assignment + FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location); m_scanner->next(); - unique_ptr<Statement> value; - value.reset(new Statement(parseExpression())); - return FunctionalAssignment{{std::move(name)}, std::move(value)}; + funAss.variableName = identifier; + funAss.value.reset(new Statement(parseExpression())); + funAss.location.end = locationOf(*funAss.value).end; + return funAss; } else + { // label - return Label{name}; + Label label = createWithLocation<Label>(identifier.location); + label.name = identifier.name; + return label; + } } default: break; @@ -113,7 +123,7 @@ assembly::Statement Parser::parseExpression() { Statement operation = parseElementaryOperation(true); if (m_scanner->currentToken() == Token::LParen) - return parseFunctionalInstruction(operation); + return parseFunctionalInstruction(std::move(operation)); else return operation; } @@ -137,8 +147,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) s_instructions[name] = instruction.second; } - //@TODO track location - + Statement ret; switch (m_scanner->currentToken()) { case Token::Identifier: @@ -162,48 +171,50 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) if (info.ret != 1) fatalParserError("Instruction " + info.name + " not allowed in this context."); } - m_scanner->next(); - return Instruction{instr}; + ret = Instruction{location(), instr}; } else - m_scanner->next(); - return Identifier{literal}; + ret = Identifier{location(), literal}; break; } case Token::StringLiteral: case Token::Number: { - Literal literal{ + ret = Literal{ + location(), m_scanner->currentToken() == Token::Number, m_scanner->currentLiteral() }; - m_scanner->next(); - return literal; + break; } default: - break; + fatalParserError("Expected elementary inline assembly operation."); } - fatalParserError("Expected elementary inline assembly operation."); - return {}; + m_scanner->next(); + return ret; } assembly::VariableDeclaration Parser::parseVariableDeclaration() { + VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); expectToken(Token::Let); - string name = m_scanner->currentLiteral(); + varDecl.name = m_scanner->currentLiteral(); expectToken(Token::Identifier); expectToken(Token::Colon); expectToken(Token::Assign); - unique_ptr<Statement> value; - value.reset(new Statement(parseExpression())); - return VariableDeclaration{name, std::move(value)}; + varDecl.value.reset(new Statement(parseExpression())); + varDecl.location.end = locationOf(*varDecl.value).end; + return varDecl; } -FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement const& _instruction) +FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction) { if (_instruction.type() != typeid(Instruction)) fatalParserError("Assembly instruction required in front of \"(\")"); - solidity::Instruction instr = boost::get<solidity::assembly::Instruction>(_instruction).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"); @@ -211,14 +222,29 @@ FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement con fatalParserError("SWAPi instructions not allowed for functional notation"); expectToken(Token::LParen); - vector<Statement> arguments; unsigned args = unsigned(instrInfo.args); for (unsigned i = 0; i < args; ++i) { - arguments.push_back(parseExpression()); + ret.arguments.emplace_back(parseExpression()); if (i != args - 1) - expectToken(Token::Comma); + { + 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 FunctionalInstruction{{instr}, std::move(arguments)}; + return ret; } diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index b54da941..0a9d51d5 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -44,13 +44,29 @@ public: std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner); protected: + /// Creates an inline assembly node with the given source location. + template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) + { + T r; + r.location = _loc; + if (r.location.isEmpty()) + { + r.location.start = position(); + r.location.end = endPosition(); + } + if (!r.location.sourceName) + r.location.sourceName = sourceName(); + return r; + } + SourceLocation location() const { return SourceLocation(position(), endPosition(), sourceName()); } + Block parseBlock(); Statement parseStatement(); /// Parses a functional expression that has to push exactly one stack element Statement parseExpression(); Statement parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); - FunctionalInstruction parseFunctionalInstruction(Statement const& _instruction); + FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction); }; } diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp index 22042ada..c891678b 100644 --- a/libsolidity/inlineasm/AsmStack.cpp +++ b/libsolidity/inlineasm/AsmStack.cpp @@ -34,14 +34,18 @@ using namespace dev::solidity::assembly; bool InlineAssemblyStack::parse(const std::shared_ptr<Scanner>& _scanner) { + m_parserResult = make_shared<Block>(); Parser parser(m_errors); - m_asmBlock = parser.parse(_scanner); - return !!m_asmBlock; + auto result = parser.parse(_scanner); + if (!result) + return false; + *m_parserResult = std::move(*result); + return true; } eth::Assembly InlineAssemblyStack::assemble() { - CodeGenerator codeGen(*m_asmBlock, m_errors); + CodeGenerator codeGen(*m_parserResult, m_errors); return codeGen.assemble(); } diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index 73ca9583..8a860e46 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -50,7 +50,7 @@ public: ErrorList const& errors() const { return m_errors; } private: - std::shared_ptr<Block> m_asmBlock; + std::shared_ptr<Block> m_parserResult; ErrorList m_errors; }; |