mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Refactor CodeGen to recurse on blocks.
This commit is contained in:
parent
7f2def8971
commit
25dcfa3480
@ -593,7 +593,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
// code-generator and see whether it produces any errors.
|
// code-generator and see whether it produces any errors.
|
||||||
// External references have already been resolved in a prior stage and stored in the annotation.
|
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||||
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
||||||
codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
|
if (!codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
|
||||||
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
||||||
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
||||||
return false;
|
return false;
|
||||||
@ -641,8 +641,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
}))
|
||||||
return false;
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(IfStatement const& _ifStatement)
|
bool TypeChecker::visit(IfStatement const& _ifStatement)
|
||||||
|
@ -36,7 +36,7 @@ using namespace dev::solidity;
|
|||||||
using namespace dev::solidity::assembly;
|
using namespace dev::solidity::assembly;
|
||||||
|
|
||||||
|
|
||||||
bool Scope::registerLabel(const string& _name, size_t _id)
|
bool Scope::registerLabel(string const& _name, size_t _id)
|
||||||
{
|
{
|
||||||
if (lookup(_name))
|
if (lookup(_name))
|
||||||
return false;
|
return false;
|
||||||
@ -45,7 +45,7 @@ bool Scope::registerLabel(const string& _name, size_t _id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Scope::registerVariable(const string& _name)
|
bool Scope::registerVariable(string const& _name)
|
||||||
{
|
{
|
||||||
if (lookup(_name))
|
if (lookup(_name))
|
||||||
return false;
|
return false;
|
||||||
@ -125,7 +125,7 @@ bool AsmAnalyzer::operator()(Label const& _item)
|
|||||||
}
|
}
|
||||||
label.resetStackHeight = true;
|
label.resetStackHeight = true;
|
||||||
for (auto const& stackItem: _item.stackInfo)
|
for (auto const& stackItem: _item.stackInfo)
|
||||||
{
|
if (!stackItem.empty())
|
||||||
if (!m_currentScope->registerVariable(stackItem))
|
if (!m_currentScope->registerVariable(stackItem))
|
||||||
{
|
{
|
||||||
//@TODO secondary location
|
//@TODO secondary location
|
||||||
@ -137,11 +137,10 @@ bool AsmAnalyzer::operator()(Label const& _item)
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsmAnalyzer::operator()(const FunctionalAssignment& _assignment)
|
bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
|
||||||
{
|
{
|
||||||
return boost::apply_visitor(*this, *_assignment.value);
|
return boost::apply_visitor(*this, *_assignment.value);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
#include <boost/range/algorithm/count_if.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -77,15 +79,42 @@ public:
|
|||||||
/// @param _identifierAccess used to resolve identifiers external to the inline assembly
|
/// @param _identifierAccess used to resolve identifiers external to the inline assembly
|
||||||
explicit CodeTransform(
|
explicit CodeTransform(
|
||||||
GeneratorState& _state,
|
GeneratorState& _state,
|
||||||
|
assembly::Block const& _block,
|
||||||
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
|
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
|
||||||
):
|
):
|
||||||
m_state(_state)
|
m_state(_state),
|
||||||
|
m_scope(*m_state.scopes.at(&_block)),
|
||||||
|
m_initialDeposit(m_state.assembly.deposit()),
|
||||||
|
m_identifierAccess(_identifierAccess)
|
||||||
{
|
{
|
||||||
if (_identifierAccess)
|
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
||||||
m_identifierAccess = _identifierAccess;
|
|
||||||
else
|
m_state.assembly.setSourceLocation(_block.location);
|
||||||
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
|
||||||
m_currentScope = m_state.scopes[nullptr].get();
|
// pop variables
|
||||||
|
for (auto const& identifier: m_scope.identifiers)
|
||||||
|
if (identifier.second.type() == typeid(Scope::Variable))
|
||||||
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
|
|
||||||
|
int deposit = m_state.assembly.deposit() - m_initialDeposit;
|
||||||
|
|
||||||
|
// issue warnings for stack height discrepancies
|
||||||
|
if (deposit < 0)
|
||||||
|
{
|
||||||
|
m_state.addError(
|
||||||
|
Error::Type::Warning,
|
||||||
|
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
|
||||||
|
_block.location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (deposit > 0)
|
||||||
|
{
|
||||||
|
m_state.addError(
|
||||||
|
Error::Type::Warning,
|
||||||
|
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
|
||||||
|
_block.location
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(assembly::Instruction const& _instruction)
|
void operator()(assembly::Instruction const& _instruction)
|
||||||
@ -108,7 +137,7 @@ public:
|
|||||||
{
|
{
|
||||||
m_state.assembly.setSourceLocation(_identifier.location);
|
m_state.assembly.setSourceLocation(_identifier.location);
|
||||||
// First search internals, then externals.
|
// First search internals, then externals.
|
||||||
if (!m_currentScope->lookup(_identifier.name, Scope::NonconstVisitor(
|
if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor(
|
||||||
[=](Scope::Variable& _var)
|
[=](Scope::Variable& _var)
|
||||||
{
|
{
|
||||||
if (!_var.active)
|
if (!_var.active)
|
||||||
@ -138,9 +167,14 @@ public:
|
|||||||
{
|
{
|
||||||
if (_label.id == Scope::Label::unassignedLabelId)
|
if (_label.id == Scope::Label::unassignedLabelId)
|
||||||
_label.id = m_state.newLabelId();
|
_label.id = m_state.newLabelId();
|
||||||
|
else if (_label.id == Scope::Label::errorLabelId)
|
||||||
|
_label.id = size_t(m_state.assembly.errorTag().data());
|
||||||
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
|
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
|
||||||
}
|
}
|
||||||
)))
|
)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (!m_identifierAccess || !m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
|
||||||
{
|
{
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
@ -166,13 +200,33 @@ public:
|
|||||||
}
|
}
|
||||||
void operator()(Label const& _label)
|
void operator()(Label const& _label)
|
||||||
{
|
{
|
||||||
solAssert(_label.stackInfo.empty(), "Labels with stack info not yet supported.");
|
|
||||||
m_state.assembly.setSourceLocation(_label.location);
|
m_state.assembly.setSourceLocation(_label.location);
|
||||||
solAssert(m_currentScope->identifiers.count(_label.name), "");
|
solAssert(m_scope.identifiers.count(_label.name), "");
|
||||||
Scope::Label& label = boost::get<Scope::Label>(m_currentScope->identifiers[_label.name]);
|
Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers[_label.name]);
|
||||||
if (label.id == Scope::Label::unassignedLabelId)
|
if (label.id == Scope::Label::unassignedLabelId)
|
||||||
label.id = m_state.newLabelId();
|
label.id = m_state.newLabelId();
|
||||||
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, label.id));
|
else if (label.id == Scope::Label::errorLabelId)
|
||||||
|
label.id = size_t(m_state.assembly.errorTag().data());
|
||||||
|
if (label.resetStackHeight)
|
||||||
|
{
|
||||||
|
size_t numVariables = boost::range::count_if(
|
||||||
|
m_scope.identifiers | boost::adaptors::map_values,
|
||||||
|
[](Scope::Identifier const& var) { return var.type() == typeid(Scope::Variable) && boost::get<Scope::Variable>(var).active; }
|
||||||
|
);
|
||||||
|
for (auto const& identifier: _label.stackInfo)
|
||||||
|
if (!identifier.empty())
|
||||||
|
{
|
||||||
|
solAssert(m_scope.identifiers.count(identifier), "");
|
||||||
|
Scope::Variable& var = boost::get<Scope::Variable>(m_scope.identifiers[identifier]);
|
||||||
|
var.active = true;
|
||||||
|
var.stackHeight = m_initialDeposit + numVariables;
|
||||||
|
numVariables++;
|
||||||
|
}
|
||||||
|
m_state.assembly.setDeposit(m_initialDeposit + numVariables);
|
||||||
|
}
|
||||||
|
else if (label.stackAdjustment != 0)
|
||||||
|
m_state.assembly.adjustDeposit(label.stackAdjustment);
|
||||||
|
m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id));
|
||||||
}
|
}
|
||||||
void operator()(assembly::Assignment const& _assignment)
|
void operator()(assembly::Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
@ -192,46 +246,14 @@ public:
|
|||||||
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, locationOf(*_varDecl.value));
|
expectDeposit(1, height, locationOf(*_varDecl.value));
|
||||||
solAssert(m_currentScope && m_currentScope->identifiers.count(_varDecl.name), "");
|
solAssert(m_scope.identifiers.count(_varDecl.name), "");
|
||||||
auto& var = boost::get<Scope::Variable>(m_currentScope->identifiers[_varDecl.name]);
|
auto& var = boost::get<Scope::Variable>(m_scope.identifiers[_varDecl.name]);
|
||||||
var.stackHeight = height;
|
var.stackHeight = height;
|
||||||
var.active = true;
|
var.active = true;
|
||||||
}
|
}
|
||||||
void operator()(assembly::Block const& _block)
|
void operator()(assembly::Block const& _block)
|
||||||
{
|
{
|
||||||
solAssert(m_state.scopes.count(&_block), "Scope for block not defined.");
|
CodeTransform(m_state, _block, m_identifierAccess);
|
||||||
m_currentScope = m_state.scopes[&_block].get();
|
|
||||||
int deposit = m_state.assembly.deposit();
|
|
||||||
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
|
||||||
|
|
||||||
m_state.assembly.setSourceLocation(_block.location);
|
|
||||||
|
|
||||||
// pop variables
|
|
||||||
for (auto const& identifier: m_currentScope->identifiers)
|
|
||||||
if (identifier.second.type() == typeid(Scope::Variable))
|
|
||||||
m_state.assembly.append(solidity::Instruction::POP);
|
|
||||||
|
|
||||||
deposit = m_state.assembly.deposit() - deposit;
|
|
||||||
|
|
||||||
// issue warnings for stack height discrepancies
|
|
||||||
if (deposit < 0)
|
|
||||||
{
|
|
||||||
m_state.addError(
|
|
||||||
Error::Type::Warning,
|
|
||||||
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
|
|
||||||
_block.location
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (deposit > 0)
|
|
||||||
{
|
|
||||||
m_state.addError(
|
|
||||||
Error::Type::Warning,
|
|
||||||
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
|
|
||||||
_block.location
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_currentScope = m_currentScope->superScope;
|
|
||||||
}
|
}
|
||||||
void operator()(assembly::FunctionDefinition const&)
|
void operator()(assembly::FunctionDefinition const&)
|
||||||
{
|
{
|
||||||
@ -241,7 +263,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
|
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (!m_currentScope->lookup(_variableName.name, Scope::Visitor(
|
if (m_scope.lookup(_variableName.name, Scope::Visitor(
|
||||||
[=](Scope::Variable const& _var)
|
[=](Scope::Variable const& _var)
|
||||||
{
|
{
|
||||||
if (!_var.active)
|
if (!_var.active)
|
||||||
@ -272,7 +294,10 @@ private:
|
|||||||
"Label \"" + string(_variableName.name) + "\" used as variable."
|
"Label \"" + string(_variableName.name) + "\" used as variable."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
)) && !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
|
)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (!m_identifierAccess || !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
|
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
|
||||||
@ -293,7 +318,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
GeneratorState& m_state;
|
GeneratorState& m_state;
|
||||||
Scope* m_currentScope;
|
Scope& m_scope;
|
||||||
|
int const m_initialDeposit;
|
||||||
assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
|
assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -304,29 +330,25 @@ bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAcces
|
|||||||
GeneratorState state(m_errors, assembly);
|
GeneratorState state(m_errors, assembly);
|
||||||
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
return false;
|
return false;
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||||
return m_errors.size() == initialErrorLen;
|
return m_errors.size() == initialErrorLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
|
eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
|
||||||
{
|
{
|
||||||
size_t initialErrorLen = m_errors.size();
|
|
||||||
eth::Assembly assembly;
|
eth::Assembly assembly;
|
||||||
GeneratorState state(m_errors, assembly);
|
GeneratorState state(m_errors, assembly);
|
||||||
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
solAssert(false, "Assembly error");
|
solAssert(false, "Assembly error");
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||||
solAssert(m_errors.size() == initialErrorLen, "Assembly error");
|
|
||||||
return assembly;
|
return assembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
|
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
|
||||||
{
|
{
|
||||||
size_t initialErrorLen = m_errors.size();
|
|
||||||
GeneratorState state(m_errors, _assembly);
|
GeneratorState state(m_errors, _assembly);
|
||||||
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
solAssert(false, "Assembly error");
|
solAssert(false, "Assembly error");
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
CodeTransform(state, m_parsedData, _identifierAccess);
|
||||||
solAssert(m_errors.size() == initialErrorLen, "Assembly error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ assembly::Statement Parser::parseStatement()
|
|||||||
label.stackInfo.push_back("-" + m_scanner->currentLiteral());
|
label.stackInfo.push_back("-" + m_scanner->currentLiteral());
|
||||||
expectToken(Token::Number);
|
expectToken(Token::Number);
|
||||||
}
|
}
|
||||||
else
|
else if (m_scanner->currentToken() != Token::RBrack)
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
label.stackInfo.push_back(expectAsmIdentifier());
|
label.stackInfo.push_back(expectAsmIdentifier());
|
||||||
|
Loading…
Reference in New Issue
Block a user