aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2016-04-23 01:28:28 +0800
committerchriseth <c@ethdev.com>2016-04-23 01:28:28 +0800
commitdd4300d5b83fa16c36970ed2dd04aeec70c7b5ef (patch)
treec8671688dba46a49f793371a8eed25a8623ee7f4
parent81ae2a78321fddcd2d32efc51568ebeca28866a8 (diff)
parent8704dd0f7f32e2ebf9ca26d8c93f1d41d51b904a (diff)
downloaddexon-solidity-dd4300d5b83fa16c36970ed2dd04aeec70c7b5ef.tar.gz
dexon-solidity-dd4300d5b83fa16c36970ed2dd04aeec70c7b5ef.tar.zst
dexon-solidity-dd4300d5b83fa16c36970ed2dd04aeec70c7b5ef.zip
Merge pull request #514 from chriseth/sourceLoc
Source location for inline assembly.
-rw-r--r--libevmasm/SourceLocation.h18
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp61
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h2
-rw-r--r--libsolidity/inlineasm/AsmData.h33
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp92
-rw-r--r--libsolidity/inlineasm/AsmParser.h18
-rw-r--r--libsolidity/inlineasm/AsmStack.cpp10
-rw-r--r--libsolidity/inlineasm/AsmStack.h2
-rw-r--r--solc/CommandLineInterface.cpp2
9 files changed, 158 insertions, 80 deletions
diff --git a/libevmasm/SourceLocation.h b/libevmasm/SourceLocation.h
index b8b57b60..8e22a826 100644
--- a/libevmasm/SourceLocation.h
+++ b/libevmasm/SourceLocation.h
@@ -26,6 +26,7 @@
#include <string>
#include <ostream>
#include <tuple>
+#include <libdevcore/Common.h> // 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<std::string const> _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 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;
};
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 7f7655f2..003ad030 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -861,7 +861,7 @@ void CommandLineInterface::outputAssembly()
cout << endl << "======= " << src.first << " =======" << endl;
eth::Assembly assembly = m_assemblyStacks[src.first].assemble();
cout << assembly.assemble().toHex() << endl;
- cout << assembly.out();
+ assembly.stream(cout, "", m_sourceCodes);
}
}