mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Analysis refactoring.
This commit is contained in:
parent
98e343b3fc
commit
7f2def8971
179
libsolidity/inlineasm/AsmAnalysis.cpp
Normal file
179
libsolidity/inlineasm/AsmAnalysis.cpp
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Analyzer part of inline assembly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||||
|
|
||||||
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
|
||||||
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
|
#include <libsolidity/interface/Utils.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
using namespace dev::solidity::assembly;
|
||||||
|
|
||||||
|
|
||||||
|
bool Scope::registerLabel(const string& _name, size_t _id)
|
||||||
|
{
|
||||||
|
if (lookup(_name))
|
||||||
|
return false;
|
||||||
|
identifiers[_name] = Scope::Label(_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Scope::registerVariable(const string& _name)
|
||||||
|
{
|
||||||
|
if (lookup(_name))
|
||||||
|
return false;
|
||||||
|
identifiers[_name] = Variable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope::Identifier* Scope::lookup(string const& _name)
|
||||||
|
{
|
||||||
|
if (identifiers.count(_name))
|
||||||
|
return &identifiers[_name];
|
||||||
|
else if (superScope)
|
||||||
|
return superScope->lookup(_name);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors):
|
||||||
|
m_scopes(_scopes), m_errors(_errors)
|
||||||
|
{
|
||||||
|
// Make the Solidity ErrorTag available to inline assembly
|
||||||
|
m_scopes[nullptr] = make_shared<Scope>();
|
||||||
|
m_scopes[nullptr]->identifiers["invalidJumpLabel"] = Scope::Label(Scope::Label::errorLabelId);
|
||||||
|
m_currentScope = m_scopes[nullptr].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
|
||||||
|
{
|
||||||
|
if (!_literal.isNumber && _literal.value.size() > 32)
|
||||||
|
{
|
||||||
|
m_errors.push_back(make_shared<Error>(
|
||||||
|
Error::Type::TypeError,
|
||||||
|
"string literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)"
|
||||||
|
));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
|
||||||
|
if (!boost::apply_visitor(*this, arg))
|
||||||
|
success = false;
|
||||||
|
if (!(*this)(_instr.instruction))
|
||||||
|
success = false;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(Label const& _item)
|
||||||
|
{
|
||||||
|
if (!m_currentScope->registerLabel(_item.name, Scope::Label::unassignedLabelId))
|
||||||
|
{
|
||||||
|
//@TODO secondary location
|
||||||
|
m_errors.push_back(make_shared<Error>(
|
||||||
|
Error::Type::DeclarationError,
|
||||||
|
"Label name " + _item.name + " already taken in this scope.",
|
||||||
|
_item.location
|
||||||
|
));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool success = true;
|
||||||
|
if (!_item.stackInfo.empty())
|
||||||
|
{
|
||||||
|
Scope::Label& label = boost::get<Scope::Label>(m_currentScope->identifiers[_item.name]);
|
||||||
|
if (_item.stackInfo.size() == 1)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
label.stackAdjustment = boost::lexical_cast<int>(_item.stackInfo[0]);
|
||||||
|
label.resetStackHeight = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (boost::bad_lexical_cast const&)
|
||||||
|
{
|
||||||
|
// Interpret as variable name
|
||||||
|
}
|
||||||
|
label.resetStackHeight = true;
|
||||||
|
for (auto const& stackItem: _item.stackInfo)
|
||||||
|
{
|
||||||
|
if (!m_currentScope->registerVariable(stackItem))
|
||||||
|
{
|
||||||
|
//@TODO secondary location
|
||||||
|
m_errors.push_back(make_shared<Error>(
|
||||||
|
Error::Type::DeclarationError,
|
||||||
|
"Variable name " + stackItem + " already taken in this scope.",
|
||||||
|
_item.location
|
||||||
|
));
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(const FunctionalAssignment& _assignment)
|
||||||
|
{
|
||||||
|
return boost::apply_visitor(*this, *_assignment.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
|
||||||
|
{
|
||||||
|
bool success = boost::apply_visitor(*this, *_varDecl.value);
|
||||||
|
if (!m_currentScope->registerVariable(_varDecl.name))
|
||||||
|
{
|
||||||
|
//@TODO secondary location
|
||||||
|
m_errors.push_back(make_shared<Error>(
|
||||||
|
Error::Type::DeclarationError,
|
||||||
|
"Variable name " + _varDecl.name + " already taken in this scope.",
|
||||||
|
_varDecl.location
|
||||||
|
));
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(Block const& _block)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
auto scope = make_shared<Scope>();
|
||||||
|
scope->superScope = m_currentScope;
|
||||||
|
m_scopes[&_block] = scope;
|
||||||
|
m_currentScope = scope.get();
|
||||||
|
|
||||||
|
for (auto const& s: _block.statements)
|
||||||
|
if (!boost::apply_visitor(*this, s))
|
||||||
|
success = false;
|
||||||
|
|
||||||
|
m_currentScope = m_currentScope->superScope;
|
||||||
|
return success;
|
||||||
|
}
|
152
libsolidity/inlineasm/AsmAnalysis.h
Normal file
152
libsolidity/inlineasm/AsmAnalysis.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Analysis part of inline assembly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
|
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
namespace assembly
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Literal;
|
||||||
|
struct Block;
|
||||||
|
struct Label;
|
||||||
|
struct FunctionalInstruction;
|
||||||
|
struct FunctionalAssignment;
|
||||||
|
struct VariableDeclaration;
|
||||||
|
struct Instruction;
|
||||||
|
struct Identifier;
|
||||||
|
struct Assignment;
|
||||||
|
|
||||||
|
struct Scope
|
||||||
|
{
|
||||||
|
struct Variable
|
||||||
|
{
|
||||||
|
int stackHeight = 0;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Label
|
||||||
|
{
|
||||||
|
Label(size_t _id): id(_id) {}
|
||||||
|
size_t id = 0;
|
||||||
|
int stackAdjustment = 0;
|
||||||
|
bool resetStackHeight = false;
|
||||||
|
static const size_t errorLabelId = -1;
|
||||||
|
static const size_t unassignedLabelId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Identifier = boost::variant<Variable, Label>;
|
||||||
|
|
||||||
|
struct Visitor: public boost::static_visitor<>
|
||||||
|
{
|
||||||
|
Visitor(
|
||||||
|
std::function<void(Variable const&)> _varVisitor,
|
||||||
|
std::function<void(Label const&)> _labelVisitor
|
||||||
|
):
|
||||||
|
m_varVisitor(std::move(_varVisitor)),
|
||||||
|
m_labelVisitor(std::move(_labelVisitor))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()(Variable const& _var) const { m_varVisitor(_var); }
|
||||||
|
void operator()(Label const& _label) const { m_labelVisitor(_label); }
|
||||||
|
|
||||||
|
std::function<void(Variable const&)> m_varVisitor;
|
||||||
|
std::function<void(Label const&)> m_labelVisitor;
|
||||||
|
};
|
||||||
|
struct NonconstVisitor: public boost::static_visitor<>
|
||||||
|
{
|
||||||
|
NonconstVisitor(
|
||||||
|
std::function<void(Variable&)> _varVisitor,
|
||||||
|
std::function<void(Label&)> _labelVisitor
|
||||||
|
):
|
||||||
|
m_varVisitor(std::move(_varVisitor)),
|
||||||
|
m_labelVisitor(std::move(_labelVisitor))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()(Variable& _var) const { m_varVisitor(_var); }
|
||||||
|
void operator()(Label& _label) const { m_labelVisitor(_label); }
|
||||||
|
|
||||||
|
std::function<void(Variable&)> m_varVisitor;
|
||||||
|
std::function<void(Label&)> m_labelVisitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool registerLabel(std::string const& _name, size_t _id);
|
||||||
|
|
||||||
|
bool registerVariable(std::string const& _name);
|
||||||
|
|
||||||
|
/// Looks up the identifier in this or super scopes and returns a valid pointer if
|
||||||
|
/// found or a nullptr if not found.
|
||||||
|
/// The pointer will be invalidated if the scope is modified.
|
||||||
|
Identifier* lookup(std::string const& _name);
|
||||||
|
/// Looks up the identifier in this and super scopes and calls the visitor, returns false if not found.
|
||||||
|
template <class V>
|
||||||
|
bool lookup(std::string const& _name, V const& _visitor)
|
||||||
|
{
|
||||||
|
if (Identifier* id = lookup(_name))
|
||||||
|
{
|
||||||
|
boost::apply_visitor(_visitor, *id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Scope* superScope = nullptr;
|
||||||
|
std::map<std::string, Identifier> identifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AsmAnalyzer: public boost::static_visitor<bool>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
|
||||||
|
AsmAnalyzer(Scopes& _scopes, ErrorList& _errors);
|
||||||
|
|
||||||
|
bool operator()(assembly::Instruction const&) { return true; }
|
||||||
|
bool operator()(assembly::Literal const& _literal);
|
||||||
|
bool operator()(assembly::Identifier const&) { return true; }
|
||||||
|
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
|
||||||
|
bool operator()(assembly::Label const& _label);
|
||||||
|
bool operator()(assembly::Assignment const&) { return true; }
|
||||||
|
bool operator()(assembly::FunctionalAssignment const& _functionalAssignment);
|
||||||
|
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
|
||||||
|
bool operator()(assembly::Block const& _block);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scope* m_currentScope = nullptr;
|
||||||
|
Scopes& m_scopes;
|
||||||
|
ErrorList& m_errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,14 +21,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
#include <libsolidity/inlineasm/AsmParser.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
|
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||||
|
|
||||||
#include <libevmasm/Assembly.h>
|
#include <libevmasm/Assembly.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libsolidity/inlineasm/AsmParser.h>
|
|
||||||
#include <libsolidity/inlineasm/AsmData.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
@ -42,74 +49,26 @@ struct GeneratorState
|
|||||||
|
|
||||||
void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation())
|
void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation())
|
||||||
{
|
{
|
||||||
auto err = make_shared<Error>(_type);
|
errors.push_back(make_shared<Error>(_type, _description, _location));
|
||||||
if (!_location.isEmpty())
|
|
||||||
*err << errinfo_sourceLocation(_location);
|
|
||||||
*err << errinfo_comment(_description);
|
|
||||||
errors.push_back(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int const* findVariable(string const& _variableName) const
|
size_t newLabelId()
|
||||||
{
|
{
|
||||||
auto localVariable = find_if(
|
return assemblyTagToIdentifier(assembly.newTag());
|
||||||
variables.rbegin(),
|
|
||||||
variables.rend(),
|
|
||||||
[&](pair<string, int> const& _var) { return _var.first == _variableName; }
|
|
||||||
);
|
|
||||||
return localVariable != variables.rend() ? &localVariable->second : nullptr;
|
|
||||||
}
|
|
||||||
eth::AssemblyItem const* findLabel(string const& _labelName) const
|
|
||||||
{
|
|
||||||
auto label = find_if(
|
|
||||||
labels.begin(),
|
|
||||||
labels.end(),
|
|
||||||
[&](pair<string, eth::AssemblyItem> const& _label) { return _label.first == _labelName; }
|
|
||||||
);
|
|
||||||
return label != labels.end() ? &label->second : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
map<string, eth::AssemblyItem> labels;
|
size_t assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
|
||||||
vector<pair<string, int>> variables; ///< name plus stack height
|
{
|
||||||
|
u256 id = _tag.data();
|
||||||
|
solAssert(id <= std::numeric_limits<size_t>::max(), "Tag id too large.");
|
||||||
|
return size_t(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<assembly::Block const*, shared_ptr<Scope>> scopes;
|
||||||
ErrorList& errors;
|
ErrorList& errors;
|
||||||
eth::Assembly& assembly;
|
eth::Assembly& assembly;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Scans the inline assembly data for labels, creates tags in the assembly and searches for
|
|
||||||
* duplicate labels.
|
|
||||||
*/
|
|
||||||
class LabelOrganizer: public boost::static_visitor<>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LabelOrganizer(GeneratorState& _state): m_state(_state)
|
|
||||||
{
|
|
||||||
// Make the Solidity ErrorTag available to inline assembly
|
|
||||||
m_state.labels.insert(make_pair("invalidJumpLabel", m_state.assembly.errorTag()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void operator()(T const& /*_item*/) { }
|
|
||||||
void operator()(Label const& _item)
|
|
||||||
{
|
|
||||||
solAssert(_item.stackInfo.empty(), "Labels with stack info not yet supported.");
|
|
||||||
if (m_state.labels.count(_item.name))
|
|
||||||
//@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)
|
|
||||||
{
|
|
||||||
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
GeneratorState& m_state;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CodeTransform: public boost::static_visitor<>
|
class CodeTransform: public boost::static_visitor<>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -126,6 +85,7 @@ public:
|
|||||||
m_identifierAccess = _identifierAccess;
|
m_identifierAccess = _identifierAccess;
|
||||||
else
|
else
|
||||||
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
|
||||||
|
m_currentScope = m_state.scopes[nullptr].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(assembly::Instruction const& _instruction)
|
void operator()(assembly::Instruction const& _instruction)
|
||||||
@ -138,24 +98,30 @@ public:
|
|||||||
m_state.assembly.setSourceLocation(_literal.location);
|
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)
|
|
||||||
{
|
|
||||||
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
|
else
|
||||||
|
{
|
||||||
|
solAssert(_literal.value.size() <= 32, "");
|
||||||
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);
|
m_state.assembly.setSourceLocation(_identifier.location);
|
||||||
// First search local variables, then labels, then externals.
|
// First search internals, then externals.
|
||||||
if (int const* stackHeight = m_state.findVariable(_identifier.name))
|
if (!m_currentScope->lookup(_identifier.name, Scope::NonconstVisitor(
|
||||||
|
[=](Scope::Variable& _var)
|
||||||
{
|
{
|
||||||
int heightDiff = m_state.assembly.deposit() - *stackHeight;
|
if (!_var.active)
|
||||||
|
{
|
||||||
|
m_state.addError(
|
||||||
|
Error::Type::TypeError,
|
||||||
|
"Variable used before it was declared",
|
||||||
|
_identifier.location
|
||||||
|
);
|
||||||
|
m_state.assembly.append(u256(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int heightDiff = m_state.assembly.deposit() - _var.stackHeight;
|
||||||
if (heightDiff <= 0 || heightDiff > 16)
|
if (heightDiff <= 0 || heightDiff > 16)
|
||||||
{
|
{
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
@ -167,11 +133,14 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_state.assembly.append(solidity::dupInstruction(heightDiff));
|
m_state.assembly.append(solidity::dupInstruction(heightDiff));
|
||||||
return;
|
},
|
||||||
|
[=](Scope::Label& _label)
|
||||||
|
{
|
||||||
|
if (_label.id == Scope::Label::unassignedLabelId)
|
||||||
|
_label.id = m_state.newLabelId();
|
||||||
|
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
|
||||||
}
|
}
|
||||||
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(
|
m_state.addError(
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
@ -197,8 +166,13 @@ 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);
|
||||||
m_state.assembly.append(m_state.labels.at(_label.name));
|
solAssert(m_currentScope->identifiers.count(_label.name), "");
|
||||||
|
Scope::Label& label = boost::get<Scope::Label>(m_currentScope->identifiers[_label.name]);
|
||||||
|
if (label.id == Scope::Label::unassignedLabelId)
|
||||||
|
label.id = m_state.newLabelId();
|
||||||
|
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, label.id));
|
||||||
}
|
}
|
||||||
void operator()(assembly::Assignment const& _assignment)
|
void operator()(assembly::Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
@ -218,23 +192,25 @@ 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));
|
||||||
m_state.variables.push_back(make_pair(_varDecl.name, height));
|
solAssert(m_currentScope && m_currentScope->identifiers.count(_varDecl.name), "");
|
||||||
|
auto& var = boost::get<Scope::Variable>(m_currentScope->identifiers[_varDecl.name]);
|
||||||
|
var.stackHeight = height;
|
||||||
|
var.active = true;
|
||||||
}
|
}
|
||||||
void operator()(assembly::Block const& _block)
|
void operator()(assembly::Block const& _block)
|
||||||
{
|
{
|
||||||
size_t numVariables = m_state.variables.size();
|
solAssert(m_state.scopes.count(&_block), "Scope for block not defined.");
|
||||||
|
m_currentScope = m_state.scopes[&_block].get();
|
||||||
int deposit = m_state.assembly.deposit();
|
int deposit = m_state.assembly.deposit();
|
||||||
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
|
|
||||||
while (m_state.variables.size() > numVariables)
|
|
||||||
{
|
|
||||||
m_state.assembly.append(solidity::Instruction::POP);
|
|
||||||
m_state.variables.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_state.assembly.setSourceLocation(_block.location);
|
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;
|
deposit = m_state.assembly.deposit() - deposit;
|
||||||
|
|
||||||
// issue warnings for stack height discrepancies
|
// issue warnings for stack height discrepancies
|
||||||
@ -254,6 +230,8 @@ public:
|
|||||||
_block.location
|
_block.location
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_currentScope = m_currentScope->superScope;
|
||||||
}
|
}
|
||||||
void operator()(assembly::FunctionDefinition const&)
|
void operator()(assembly::FunctionDefinition const&)
|
||||||
{
|
{
|
||||||
@ -263,9 +241,20 @@ public:
|
|||||||
private:
|
private:
|
||||||
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
|
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (int const* stackHeight = m_state.findVariable(_variableName.name))
|
if (!m_currentScope->lookup(_variableName.name, Scope::Visitor(
|
||||||
|
[=](Scope::Variable const& _var)
|
||||||
{
|
{
|
||||||
int heightDiff = m_state.assembly.deposit() - *stackHeight - 1;
|
if (!_var.active)
|
||||||
|
{
|
||||||
|
m_state.addError(
|
||||||
|
Error::Type::TypeError,
|
||||||
|
"Variable used before it was declared",
|
||||||
|
_location
|
||||||
|
);
|
||||||
|
m_state.assembly.append(u256(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int heightDiff = m_state.assembly.deposit() - _var.stackHeight - 1;
|
||||||
if (heightDiff <= 0 || heightDiff > 16)
|
if (heightDiff <= 0 || heightDiff > 16)
|
||||||
m_state.addError(
|
m_state.addError(
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
@ -275,9 +264,15 @@ private:
|
|||||||
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;
|
},
|
||||||
|
[=](Scope::Label const&)
|
||||||
|
{
|
||||||
|
m_state.addError(
|
||||||
|
Error::Type::DeclarationError,
|
||||||
|
"Label \"" + string(_variableName.name) + "\" used as variable."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
|
)) && !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."
|
||||||
@ -298,6 +293,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
GeneratorState& m_state;
|
GeneratorState& m_state;
|
||||||
|
Scope* m_currentScope;
|
||||||
assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
|
assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,24 +302,31 @@ bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAcces
|
|||||||
size_t initialErrorLen = m_errors.size();
|
size_t initialErrorLen = m_errors.size();
|
||||||
eth::Assembly assembly;
|
eth::Assembly assembly;
|
||||||
GeneratorState state(m_errors, assembly);
|
GeneratorState state(m_errors, assembly);
|
||||||
(LabelOrganizer(state))(m_parsedData);
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
|
return false;
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
||||||
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);
|
||||||
(LabelOrganizer(state))(m_parsedData);
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
|
solAssert(false, "Assembly error");
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
||||||
|
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);
|
||||||
(LabelOrganizer(state))(m_parsedData);
|
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
|
||||||
|
solAssert(false, "Assembly error");
|
||||||
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
(CodeTransform(state, _identifierAccess))(m_parsedData);
|
||||||
|
solAssert(m_errors.size() == initialErrorLen, "Assembly error");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +148,10 @@ assembly::Statement Parser::parseStatement()
|
|||||||
break;
|
break;
|
||||||
expectToken(Token::Comma);
|
expectToken(Token::Comma);
|
||||||
}
|
}
|
||||||
|
// Push an empty string to signify that there were brackets, like in
|
||||||
|
// "name[]:", which just resets the stack height to the height of the local variables.
|
||||||
|
if (label.stackInfo.empty())
|
||||||
|
label.stackInfo.push_back("");
|
||||||
expectToken(Token::RBrack);
|
expectToken(Token::RBrack);
|
||||||
label.location.end = endPosition();
|
label.location.end = endPosition();
|
||||||
expectToken(Token::Colon);
|
expectToken(Token::Colon);
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <libsolidity/inlineasm/AsmParser.h>
|
#include <libsolidity/inlineasm/AsmParser.h>
|
||||||
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
||||||
#include <libsolidity/inlineasm/AsmPrinter.h>
|
#include <libsolidity/inlineasm/AsmPrinter.h>
|
||||||
|
#include <libsolidity/inlineasm/AsmAnalysis.h>
|
||||||
|
|
||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
|
|
||||||
@ -45,8 +46,10 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
auto result = parser.parse(_scanner);
|
auto result = parser.parse(_scanner);
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*m_parserResult = std::move(*result);
|
*m_parserResult = std::move(*result);
|
||||||
return true;
|
AsmAnalyzer::Scopes scopes;
|
||||||
|
return (AsmAnalyzer(scopes, m_errors))(*m_parserResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
string InlineAssemblyStack::toString()
|
string InlineAssemblyStack::toString()
|
||||||
|
@ -58,6 +58,14 @@ Error::Error(Type _type): m_type(_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error::Error(Error::Type _type, const std::string& _description, const SourceLocation& _location):
|
||||||
|
Error(_type)
|
||||||
|
{
|
||||||
|
if (!_location.isEmpty())
|
||||||
|
*this << errinfo_sourceLocation(_location);
|
||||||
|
*this << errinfo_comment(_description);
|
||||||
|
}
|
||||||
|
|
||||||
string Exception::lineInfo() const
|
string Exception::lineInfo() const
|
||||||
{
|
{
|
||||||
char const* const* file = boost::get_error_info<boost::throw_file>(*this);
|
char const* const* file = boost::get_error_info<boost::throw_file>(*this);
|
||||||
|
@ -55,6 +55,8 @@ public:
|
|||||||
|
|
||||||
explicit Error(Type _type);
|
explicit Error(Type _type);
|
||||||
|
|
||||||
|
Error(Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation());
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
std::string const& typeName() const { return m_typeName; }
|
std::string const& typeName() const { return m_typeName; }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user