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 <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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user