mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #514 from chriseth/sourceLoc
Source location for inline assembly.
This commit is contained in:
commit
dd4300d5b8
@ -26,6 +26,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <libdevcore/Common.h> // defines noexcept macro for MSVC
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -36,24 +37,21 @@ namespace dev
|
|||||||
*/
|
*/
|
||||||
struct SourceLocation
|
struct SourceLocation
|
||||||
{
|
{
|
||||||
|
SourceLocation(): start(-1), end(-1) { }
|
||||||
SourceLocation(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
|
SourceLocation(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
|
||||||
start(_start), end(_end), sourceName(_sourceName) { }
|
start(_start), end(_end), sourceName(_sourceName) { }
|
||||||
SourceLocation(): start(-1), end(-1) { }
|
SourceLocation(SourceLocation&& _other) noexcept:
|
||||||
|
|
||||||
SourceLocation(SourceLocation const& _other):
|
|
||||||
start(_other.start),
|
start(_other.start),
|
||||||
end(_other.end),
|
end(_other.end),
|
||||||
sourceName(_other.sourceName)
|
sourceName(std::move(_other.sourceName))
|
||||||
{}
|
{}
|
||||||
|
SourceLocation(SourceLocation const& _other) = default;
|
||||||
SourceLocation& operator=(SourceLocation const& _other)
|
SourceLocation& operator=(SourceLocation const&) = default;
|
||||||
|
SourceLocation& operator=(SourceLocation&& _other) noexcept
|
||||||
{
|
{
|
||||||
if (&_other == this)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
start = _other.start;
|
start = _other.start;
|
||||||
end = _other.end;
|
end = _other.end;
|
||||||
sourceName = _other.sourceName;
|
sourceName = std::move(_other.sourceName);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +86,12 @@ public:
|
|||||||
void operator()(Label const& _item)
|
void operator()(Label const& _item)
|
||||||
{
|
{
|
||||||
if (m_state.labels.count(_item.name))
|
if (m_state.labels.count(_item.name))
|
||||||
//@TODO location and secondary location
|
//@TODO secondary location
|
||||||
m_state.addError(Error::Type::DeclarationError, "Label " + _item.name + " declared twice.");
|
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()));
|
m_state.labels.insert(make_pair(_item.name, m_state.assembly.newTag()));
|
||||||
}
|
}
|
||||||
void operator()(assembly::Block const& _block)
|
void operator()(assembly::Block const& _block)
|
||||||
@ -117,34 +121,43 @@ public:
|
|||||||
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
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);
|
m_state.assembly.append(_instruction.instruction);
|
||||||
}
|
}
|
||||||
void operator()(assembly::Literal const& _literal)
|
void operator()(assembly::Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
m_state.assembly.setSourceLocation(_literal.location);
|
||||||
if (_literal.isNumber)
|
if (_literal.isNumber)
|
||||||
m_state.assembly.append(u256(_literal.value));
|
m_state.assembly.append(u256(_literal.value));
|
||||||
else if (_literal.value.size() > 32)
|
else if (_literal.value.size() > 32)
|
||||||
|
{
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
"String literal too long (" + boost::lexical_cast<string>(_literal.value.size()) + " > 32)"
|
"String literal too long (" + boost::lexical_cast<string>(_literal.value.size()) + " > 32)"
|
||||||
);
|
);
|
||||||
|
m_state.assembly.append(u256(0));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_state.assembly.append(_literal.value);
|
m_state.assembly.append(_literal.value);
|
||||||
}
|
}
|
||||||
void operator()(assembly::Identifier const& _identifier)
|
void operator()(assembly::Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
|
m_state.assembly.setSourceLocation(_identifier.location);
|
||||||
// First search local variables, then labels, then externals.
|
// First search local variables, then labels, then externals.
|
||||||
if (int const* stackHeight = m_state.findVariable(_identifier.name))
|
if (int const* stackHeight = m_state.findVariable(_identifier.name))
|
||||||
{
|
{
|
||||||
int heightDiff = m_state.assembly.deposit() - *stackHeight;
|
int heightDiff = m_state.assembly.deposit() - *stackHeight;
|
||||||
if (heightDiff <= 0 || heightDiff > 16)
|
if (heightDiff <= 0 || heightDiff > 16)
|
||||||
//@TODO location
|
{
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::TypeError,
|
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
|
else
|
||||||
m_state.assembly.append(solidity::dupInstruction(heightDiff));
|
m_state.assembly.append(solidity::dupInstruction(heightDiff));
|
||||||
return;
|
return;
|
||||||
@ -152,10 +165,14 @@ public:
|
|||||||
else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name))
|
else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name))
|
||||||
m_state.assembly.append(label->pushTag());
|
m_state.assembly.append(label->pushTag());
|
||||||
else if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
|
else if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
|
||||||
|
{
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::DeclarationError,
|
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)
|
void operator()(FunctionalInstruction const& _instr)
|
||||||
{
|
{
|
||||||
@ -163,30 +180,33 @@ public:
|
|||||||
{
|
{
|
||||||
int height = m_state.assembly.deposit();
|
int height = m_state.assembly.deposit();
|
||||||
boost::apply_visitor(*this, *it);
|
boost::apply_visitor(*this, *it);
|
||||||
expectDeposit(1, height);
|
expectDeposit(1, height, locationOf(*it));
|
||||||
}
|
}
|
||||||
(*this)(_instr.instruction);
|
(*this)(_instr.instruction);
|
||||||
}
|
}
|
||||||
void operator()(Label const& _label)
|
void operator()(Label const& _label)
|
||||||
{
|
{
|
||||||
|
m_state.assembly.setSourceLocation(_label.location);
|
||||||
m_state.assembly.append(m_state.labels.at(_label.name));
|
m_state.assembly.append(m_state.labels.at(_label.name));
|
||||||
}
|
}
|
||||||
void operator()(assembly::Assignment const& _assignment)
|
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)
|
void operator()(FunctionalAssignment const& _assignment)
|
||||||
{
|
{
|
||||||
int height = m_state.assembly.deposit();
|
int height = m_state.assembly.deposit();
|
||||||
boost::apply_visitor(*this, *_assignment.value);
|
boost::apply_visitor(*this, *_assignment.value);
|
||||||
expectDeposit(1, height);
|
expectDeposit(1, height, locationOf(*_assignment.value));
|
||||||
generateAssignment(_assignment.variableName);
|
m_state.assembly.setSourceLocation(_assignment.location);
|
||||||
|
generateAssignment(_assignment.variableName, _assignment.location);
|
||||||
}
|
}
|
||||||
void operator()(assembly::VariableDeclaration const& _varDecl)
|
void operator()(assembly::VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
int height = m_state.assembly.deposit();
|
int height = m_state.assembly.deposit();
|
||||||
boost::apply_visitor(*this, *_varDecl.value);
|
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));
|
m_state.variables.push_back(make_pair(_varDecl.name, height));
|
||||||
}
|
}
|
||||||
void operator()(assembly::Block const& _block)
|
void operator()(assembly::Block const& _block)
|
||||||
@ -194,7 +214,8 @@ public:
|
|||||||
size_t numVariables = m_state.variables.size();
|
size_t numVariables = m_state.variables.size();
|
||||||
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
||||||
// pop variables
|
// 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)
|
while (m_state.variables.size() > numVariables)
|
||||||
{
|
{
|
||||||
m_state.assembly.append(solidity::Instruction::POP);
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
@ -203,22 +224,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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))
|
if (int const* stackHeight = m_state.findVariable(_variableName.name))
|
||||||
{
|
{
|
||||||
int heightDiff = m_state.assembly.deposit() - *stackHeight - 1;
|
int heightDiff = m_state.assembly.deposit() - *stackHeight - 1;
|
||||||
if (heightDiff <= 0 || heightDiff > 16)
|
if (heightDiff <= 0 || heightDiff > 16)
|
||||||
//@TODO location
|
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::TypeError,
|
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
|
else
|
||||||
{
|
|
||||||
m_state.assembly.append(solidity::swapInstruction(heightDiff));
|
m_state.assembly.append(solidity::swapInstruction(heightDiff));
|
||||||
m_state.assembly.append(solidity::Instruction::POP);
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
|
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)
|
if (m_state.assembly.deposit() != _oldHeight + 1)
|
||||||
//@TODO location
|
|
||||||
m_state.addError(Error::Type::TypeError,
|
m_state.addError(Error::Type::TypeError,
|
||||||
"Expected instruction(s) to deposit " +
|
"Expected instruction(s) to deposit " +
|
||||||
boost::lexical_cast<string>(_deposit) +
|
boost::lexical_cast<string>(_deposit) +
|
||||||
" item(s) to the stack, but did deposit " +
|
" item(s) to the stack, but did deposit " +
|
||||||
boost::lexical_cast<string>(m_state.assembly.deposit() - _oldHeight) +
|
boost::lexical_cast<string>(m_state.assembly.deposit() - _oldHeight) +
|
||||||
" item(s)."
|
" item(s).",
|
||||||
|
_location
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public:
|
|||||||
/// If in rvalue context, the function is assumed to append instructions to
|
/// 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.
|
/// 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)>;
|
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) {}
|
m_parsedData(_parsedData), m_errors(_errors) {}
|
||||||
/// Performs type checks and @returns false on error.
|
/// Performs type checks and @returns false on error.
|
||||||
/// Actually runs the full code generation but discards the result.
|
/// Actually runs the full code generation but discards the result.
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
#include <libevmasm/SourceLocation.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -35,29 +36,43 @@ namespace assembly
|
|||||||
/// What follows are the AST nodes for assembly.
|
/// What follows are the AST nodes for assembly.
|
||||||
|
|
||||||
/// Direct EVM instruction (except PUSHi and JUMPDEST)
|
/// 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)
|
/// 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
|
/// External / internal identifier or label reference
|
||||||
struct Identifier { std::string name; };
|
struct Identifier { SourceLocation location; std::string name; };
|
||||||
struct FunctionalInstruction;
|
struct FunctionalInstruction;
|
||||||
/// Jump label ("name:")
|
/// 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)
|
/// Assignemnt (":= x", moves stack top into x, potentially multiple slots)
|
||||||
struct Assignment { Identifier variableName; };
|
struct Assignment { SourceLocation location; Identifier variableName; };
|
||||||
struct FunctionalAssignment;
|
struct FunctionalAssignment;
|
||||||
struct VariableDeclaration;
|
struct VariableDeclaration;
|
||||||
struct Block;
|
struct Block;
|
||||||
using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionalInstruction, VariableDeclaration, 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
|
/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
|
||||||
/// side and requires x to occupy exactly one stack slot.
|
/// 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))"
|
/// 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
|
/// 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)
|
/// 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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_scanner = _scanner;
|
m_scanner = _scanner;
|
||||||
return make_shared<assembly::Block>(parseBlock());
|
return make_shared<Block>(parseBlock());
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
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 Parser::parseBlock()
|
||||||
{
|
{
|
||||||
|
assembly::Block block = createWithLocation<Block>();
|
||||||
expectToken(Token::LBrace);
|
expectToken(Token::LBrace);
|
||||||
Block block;
|
|
||||||
while (m_scanner->currentToken() != Token::RBrace)
|
while (m_scanner->currentToken() != Token::RBrace)
|
||||||
block.statements.emplace_back(parseStatement());
|
block.statements.emplace_back(parseStatement());
|
||||||
|
block.location.end = endPosition();
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -65,11 +66,14 @@ assembly::Statement Parser::parseStatement()
|
|||||||
return parseBlock();
|
return parseBlock();
|
||||||
case Token::Assign:
|
case Token::Assign:
|
||||||
{
|
{
|
||||||
|
assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
expectToken(Token::Colon);
|
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);
|
expectToken(Token::Identifier);
|
||||||
return assembly::Assignment{assembly::Identifier{name}};
|
return assignment;
|
||||||
}
|
}
|
||||||
case Token::Return: // opcode
|
case Token::Return: // opcode
|
||||||
case Token::Byte: // opcode
|
case Token::Byte: // opcode
|
||||||
@ -84,24 +88,30 @@ assembly::Statement Parser::parseStatement()
|
|||||||
switch (m_scanner->currentToken())
|
switch (m_scanner->currentToken())
|
||||||
{
|
{
|
||||||
case Token::LParen:
|
case Token::LParen:
|
||||||
return parseFunctionalInstruction(statement);
|
return parseFunctionalInstruction(std::move(statement));
|
||||||
case Token::Colon:
|
case Token::Colon:
|
||||||
{
|
{
|
||||||
if (statement.type() != typeid(assembly::Identifier))
|
if (statement.type() != typeid(assembly::Identifier))
|
||||||
fatalParserError("Label name / variable name must precede \":\".");
|
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();
|
m_scanner->next();
|
||||||
if (m_scanner->currentToken() == Token::Assign)
|
if (m_scanner->currentToken() == Token::Assign)
|
||||||
{
|
{
|
||||||
// functional assignment
|
// functional assignment
|
||||||
|
FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
unique_ptr<Statement> value;
|
funAss.variableName = identifier;
|
||||||
value.reset(new Statement(parseExpression()));
|
funAss.value.reset(new Statement(parseExpression()));
|
||||||
return FunctionalAssignment{{std::move(name)}, std::move(value)};
|
funAss.location.end = locationOf(*funAss.value).end;
|
||||||
|
return funAss;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
// label
|
// label
|
||||||
return Label{name};
|
Label label = createWithLocation<Label>(identifier.location);
|
||||||
|
label.name = identifier.name;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -113,7 +123,7 @@ assembly::Statement Parser::parseExpression()
|
|||||||
{
|
{
|
||||||
Statement operation = parseElementaryOperation(true);
|
Statement operation = parseElementaryOperation(true);
|
||||||
if (m_scanner->currentToken() == Token::LParen)
|
if (m_scanner->currentToken() == Token::LParen)
|
||||||
return parseFunctionalInstruction(operation);
|
return parseFunctionalInstruction(std::move(operation));
|
||||||
else
|
else
|
||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
@ -137,8 +147,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
|
|||||||
s_instructions[name] = instruction.second;
|
s_instructions[name] = instruction.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
//@TODO track location
|
Statement ret;
|
||||||
|
|
||||||
switch (m_scanner->currentToken())
|
switch (m_scanner->currentToken())
|
||||||
{
|
{
|
||||||
case Token::Identifier:
|
case Token::Identifier:
|
||||||
@ -162,48 +171,50 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
|
|||||||
if (info.ret != 1)
|
if (info.ret != 1)
|
||||||
fatalParserError("Instruction " + info.name + " not allowed in this context.");
|
fatalParserError("Instruction " + info.name + " not allowed in this context.");
|
||||||
}
|
}
|
||||||
m_scanner->next();
|
ret = Instruction{location(), instr};
|
||||||
return Instruction{instr};
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_scanner->next();
|
ret = Identifier{location(), literal};
|
||||||
return Identifier{literal};
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Token::StringLiteral:
|
case Token::StringLiteral:
|
||||||
case Token::Number:
|
case Token::Number:
|
||||||
{
|
{
|
||||||
Literal literal{
|
ret = Literal{
|
||||||
|
location(),
|
||||||
m_scanner->currentToken() == Token::Number,
|
m_scanner->currentToken() == Token::Number,
|
||||||
m_scanner->currentLiteral()
|
m_scanner->currentLiteral()
|
||||||
};
|
};
|
||||||
m_scanner->next();
|
|
||||||
return literal;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fatalParserError("Expected elementary inline assembly operation.");
|
default:
|
||||||
return {};
|
fatalParserError("Expected elementary inline assembly operation.");
|
||||||
|
}
|
||||||
|
m_scanner->next();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
assembly::VariableDeclaration Parser::parseVariableDeclaration()
|
assembly::VariableDeclaration Parser::parseVariableDeclaration()
|
||||||
{
|
{
|
||||||
|
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
|
||||||
expectToken(Token::Let);
|
expectToken(Token::Let);
|
||||||
string name = m_scanner->currentLiteral();
|
varDecl.name = m_scanner->currentLiteral();
|
||||||
expectToken(Token::Identifier);
|
expectToken(Token::Identifier);
|
||||||
expectToken(Token::Colon);
|
expectToken(Token::Colon);
|
||||||
expectToken(Token::Assign);
|
expectToken(Token::Assign);
|
||||||
unique_ptr<Statement> value;
|
varDecl.value.reset(new Statement(parseExpression()));
|
||||||
value.reset(new Statement(parseExpression()));
|
varDecl.location.end = locationOf(*varDecl.value).end;
|
||||||
return VariableDeclaration{name, std::move(value)};
|
return varDecl;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement const& _instruction)
|
FunctionalInstruction Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
|
||||||
{
|
{
|
||||||
if (_instruction.type() != typeid(Instruction))
|
if (_instruction.type() != typeid(Instruction))
|
||||||
fatalParserError("Assembly instruction required in front of \"(\")");
|
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);
|
InstructionInfo instrInfo = instructionInfo(instr);
|
||||||
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
|
if (solidity::Instruction::DUP1 <= instr && instr <= solidity::Instruction::DUP16)
|
||||||
fatalParserError("DUPi instructions not allowed for functional notation");
|
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");
|
fatalParserError("SWAPi instructions not allowed for functional notation");
|
||||||
|
|
||||||
expectToken(Token::LParen);
|
expectToken(Token::LParen);
|
||||||
vector<Statement> arguments;
|
|
||||||
unsigned args = unsigned(instrInfo.args);
|
unsigned args = unsigned(instrInfo.args);
|
||||||
for (unsigned i = 0; i < args; ++i)
|
for (unsigned i = 0; i < args; ++i)
|
||||||
{
|
{
|
||||||
arguments.push_back(parseExpression());
|
ret.arguments.emplace_back(parseExpression());
|
||||||
if (i != args - 1)
|
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);
|
expectToken(Token::RParen);
|
||||||
return FunctionalInstruction{{instr}, std::move(arguments)};
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,29 @@ public:
|
|||||||
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner);
|
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner);
|
||||||
|
|
||||||
protected:
|
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();
|
Block parseBlock();
|
||||||
Statement parseStatement();
|
Statement parseStatement();
|
||||||
/// Parses a functional expression that has to push exactly one stack element
|
/// Parses a functional expression that has to push exactly one stack element
|
||||||
Statement parseExpression();
|
Statement parseExpression();
|
||||||
Statement parseElementaryOperation(bool _onlySinglePusher = false);
|
Statement parseElementaryOperation(bool _onlySinglePusher = false);
|
||||||
VariableDeclaration parseVariableDeclaration();
|
VariableDeclaration parseVariableDeclaration();
|
||||||
FunctionalInstruction parseFunctionalInstruction(Statement const& _instruction);
|
FunctionalInstruction parseFunctionalInstruction(Statement&& _instruction);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,18 @@ using namespace dev::solidity::assembly;
|
|||||||
|
|
||||||
bool InlineAssemblyStack::parse(const std::shared_ptr<Scanner>& _scanner)
|
bool InlineAssemblyStack::parse(const std::shared_ptr<Scanner>& _scanner)
|
||||||
{
|
{
|
||||||
|
m_parserResult = make_shared<Block>();
|
||||||
Parser parser(m_errors);
|
Parser parser(m_errors);
|
||||||
m_asmBlock = parser.parse(_scanner);
|
auto result = parser.parse(_scanner);
|
||||||
return !!m_asmBlock;
|
if (!result)
|
||||||
|
return false;
|
||||||
|
*m_parserResult = std::move(*result);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::Assembly InlineAssemblyStack::assemble()
|
eth::Assembly InlineAssemblyStack::assemble()
|
||||||
{
|
{
|
||||||
CodeGenerator codeGen(*m_asmBlock, m_errors);
|
CodeGenerator codeGen(*m_parserResult, m_errors);
|
||||||
return codeGen.assemble();
|
return codeGen.assemble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
ErrorList const& errors() const { return m_errors; }
|
ErrorList const& errors() const { return m_errors; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Block> m_asmBlock;
|
std::shared_ptr<Block> m_parserResult;
|
||||||
ErrorList m_errors;
|
ErrorList m_errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -861,7 +861,7 @@ void CommandLineInterface::outputAssembly()
|
|||||||
cout << endl << "======= " << src.first << " =======" << endl;
|
cout << endl << "======= " << src.first << " =======" << endl;
|
||||||
eth::Assembly assembly = m_assemblyStacks[src.first].assemble();
|
eth::Assembly assembly = m_assemblyStacks[src.first].assemble();
|
||||||
cout << assembly.assemble().toHex() << endl;
|
cout << assembly.assemble().toHex() << endl;
|
||||||
cout << assembly.out();
|
assembly.stream(cout, "", m_sourceCodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user