diff --git a/libevmasm/SourceLocation.h b/libevmasm/SourceLocation.h index b8b57b609..8e22a8261 100644 --- a/libevmasm/SourceLocation.h +++ b/libevmasm/SourceLocation.h @@ -26,6 +26,7 @@ #include #include #include +#include // defines noexcept macro for MSVC namespace dev { @@ -36,24 +37,21 @@ namespace dev */ struct SourceLocation { + SourceLocation(): start(-1), end(-1) { } SourceLocation(int _start, int _end, std::shared_ptr _sourceName): start(_start), end(_end), sourceName(_sourceName) { } - SourceLocation(): start(-1), end(-1) { } - - SourceLocation(SourceLocation const& _other): + SourceLocation(SourceLocation&& _other) noexcept: start(_other.start), end(_other.end), - sourceName(_other.sourceName) + sourceName(std::move(_other.sourceName)) {} - - SourceLocation& operator=(SourceLocation const& _other) + SourceLocation(SourceLocation const& _other) = default; + SourceLocation& operator=(SourceLocation const&) = default; + SourceLocation& operator=(SourceLocation&& _other) noexcept { - if (&_other == this) - return *this; - start = _other.start; end = _other.end; - sourceName = _other.sourceName; + sourceName = std::move(_other.sourceName); return *this; } diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 5fa040874..d571ce0d6 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(_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(heightDiff) + ")" + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(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(heightDiff) + ")" + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(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(_deposit) + " item(s) to the stack, but did deposit " + boost::lexical_cast(m_state.assembly.deposit() - _oldHeight) + - " item(s)." + " item(s).", + _location ); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index f749ba502..aaabda457 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; - 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 42f0ae311..d6abf67f5 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -24,6 +24,7 @@ #include #include +#include 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; /// 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 value; }; +struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20), add(2, x))" -struct FunctionalInstruction { Instruction instruction; std::vector arguments; }; +struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector arguments; }; /// Block-scope variable declaration ("let x := mload(20)"), non-hoisted -struct VariableDeclaration { std::string name; std::shared_ptr value; }; +struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr value; }; /// Block that creates a scope (frees declared stack variables) -struct Block { std::vector statements; }; +struct Block { SourceLocation location; std::vector statements; }; + +struct LocationExtractor: boost::static_visitor +{ + template SourceLocation operator()(T const& _node) const + { + return _node.location; + } +}; + +/// Extracts the source location from an inline assembly node. +template 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 33c8efa01..5c7163ee8 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -35,7 +35,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scann try { m_scanner = _scanner; - return make_shared(parseBlock()); + return make_shared(parseBlock()); } catch (FatalError const&) { @@ -47,10 +47,11 @@ shared_ptr Parser::parse(std::shared_ptr const& _scann assembly::Block Parser::parseBlock() { + assembly::Block block = createWithLocation(); 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(); 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(statement).name; + assembly::Identifier const& identifier = boost::get(statement); m_scanner->next(); if (m_scanner->currentToken() == Token::Assign) { // functional assignment + FunctionalAssignment funAss = createWithLocation(identifier.location); m_scanner->next(); - unique_ptr 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