diff options
Diffstat (limited to 'libsolidity/inlineasm/AsmAnalysis.cpp')
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 375 |
1 files changed, 298 insertions, 77 deletions
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index a3ddb61d..e03eea2e 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -21,6 +21,9 @@ #include <libsolidity/inlineasm/AsmAnalysis.h> #include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmScopeFiller.h> +#include <libsolidity/inlineasm/AsmScope.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Utils.h> @@ -35,146 +38,364 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; - -bool Scope::registerLabel(string const& _name) +AsmAnalyzer::AsmAnalyzer( + AsmAnalysisInfo& _analysisInfo, + ErrorList& _errors, + ExternalIdentifierAccess::Resolver const& _resolver +): + m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors) { - if (exists(_name)) - return false; - identifiers[_name] = Label(); - return true; } -bool Scope::registerVariable(string const& _name) +bool AsmAnalyzer::analyze(Block const& _block) { - if (exists(_name)) + if (!(ScopeFiller(m_info.scopes, m_errors))(_block)) return false; - identifiers[_name] = Variable(); - return true; -} -bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns) -{ - if (exists(_name)) - return false; - identifiers[_name] = Function(_arguments, _returns); - return true; + return (*this)(_block); } -Scope::Identifier* Scope::lookup(string const& _name) +bool AsmAnalyzer::operator()(Label const& _label) { - if (identifiers.count(_name)) - return &identifiers[_name]; - else if (superScope && !closedScope) - return superScope->lookup(_name); - else - return nullptr; -} - -bool Scope::exists(string const& _name) -{ - if (identifiers.count(_name)) - return true; - else if (superScope) - return superScope->exists(_name); - else - return false; + m_info.stackHeightInfo[&_label] = m_stackHeight; + return true; } -AsmAnalyzer::AsmAnalyzer(AsmAnalyzer::Scopes& _scopes, ErrorList& _errors): - m_scopes(_scopes), m_errors(_errors) +bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - // Make the Solidity ErrorTag available to inline assembly - m_scopes[nullptr] = make_shared<Scope>(); - Scope::Label errorLabel; - errorLabel.id = Scope::Label::errorLabelId; - m_scopes[nullptr]->identifiers["invalidJumpLabel"] = errorLabel; - m_currentScope = m_scopes[nullptr].get(); + auto const& info = instructionInfo(_instruction.instruction); + m_stackHeight += info.ret - info.args; + m_info.stackHeightInfo[&_instruction] = m_stackHeight; + return true; } bool AsmAnalyzer::operator()(assembly::Literal const& _literal) { - if (!_literal.isNumber && _literal.value.size() > 32) + ++m_stackHeight; + if (_literal.kind == assembly::LiteralKind::String && _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)" + "String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)", + _literal.location )); return false; } + m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } +bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) +{ + size_t numErrorsBefore = m_errors.size(); + bool success = true; + if (m_currentScope->lookup(_identifier.name, Scope::Visitor( + [&](Scope::Variable const& _var) + { + if (!_var.active) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Variable " + _identifier.name + " used before it was declared.", + _identifier.location + )); + success = false; + } + ++m_stackHeight; + }, + [&](Scope::Label const&) + { + ++m_stackHeight; + }, + [&](Scope::Function const&) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Function " + _identifier.name + " used without being called.", + _identifier.location + )); + success = false; + } + ))) + { + } + else + { + size_t stackSize(-1); + if (m_resolver) + stackSize = m_resolver(_identifier, IdentifierContext::RValue); + if (stackSize == size_t(-1)) + { + // Only add an error message if the callback did not do it. + if (numErrorsBefore == m_errors.size()) + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Identifier not found.", + _identifier.location + )); + success = false; + } + m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; + } + m_info.stackHeightInfo[&_identifier] = m_stackHeight; + return success; +} + bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) + { + int const stackHeight = m_stackHeight; if (!boost::apply_visitor(*this, arg)) success = false; + if (!expectDeposit(1, stackHeight, locationOf(arg))) + success = false; + } + // Parser already checks that the number of arguments is correct. + solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), ""); if (!(*this)(_instr.instruction)) success = false; + m_info.stackHeightInfo[&_instr] = m_stackHeight; return success; } -bool AsmAnalyzer::operator()(Label const& _item) +bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment) { - if (!m_currentScope->registerLabel(_item.name)) - { - //@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; - } - return true; + bool success = checkAssignment(_assignment.variableName, size_t(-1)); + m_info.stackHeightInfo[&_assignment] = m_stackHeight; + return success; } + bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment) { - return boost::apply_visitor(*this, *_assignment.value); + int const stackHeight = m_stackHeight; + bool success = boost::apply_visitor(*this, *_assignment.value); + solAssert(m_stackHeight >= stackHeight, "Negative value size."); + if (!checkAssignment(_assignment.variableName, m_stackHeight - stackHeight)) + success = false; + m_info.stackHeightInfo[&_assignment] = m_stackHeight; + return success; } bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) { + int const stackHeight = m_stackHeight; 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; - } + solAssert(m_stackHeight - stackHeight == 1, "Invalid value size."); + boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.variable.name)).active = true; + m_info.stackHeightInfo[&_varDecl] = m_stackHeight; return success; } -bool AsmAnalyzer::operator()(assembly::FunctionDefinition const&) +bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) { - // TODO - we cannot throw an exception here because of some tests. - return true; + Scope& bodyScope = scope(&_funDef.body); + for (auto const& var: _funDef.arguments + _funDef.returns) + boost::get<Scope::Variable>(bodyScope.identifiers.at(var.name)).active = true; + + int const stackHeight = m_stackHeight; + m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); + m_virtualVariablesInNextBlock = m_stackHeight; + + bool success = (*this)(_funDef.body); + + m_stackHeight = stackHeight; + m_info.stackHeightInfo[&_funDef] = m_stackHeight; + return success; } -bool AsmAnalyzer::operator()(assembly::FunctionCall const&) +bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) { - // TODO - we cannot throw an exception here because of some tests. - return true; + bool success = true; + size_t arguments = 0; + size_t returns = 0; + if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( + [&](Scope::Variable const&) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Attempt to call variable instead of function.", + _funCall.functionName.location + )); + success = false; + }, + [&](Scope::Label const&) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Attempt to call label instead of function.", + _funCall.functionName.location + )); + success = false; + }, + [&](Scope::Function const& _fun) + { + /// TODO: compare types too + arguments = _fun.arguments.size(); + returns = _fun.returns.size(); + } + ))) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Function not found.", + _funCall.functionName.location + )); + success = false; + } + if (success) + { + if (_funCall.arguments.size() != arguments) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Expected " + + boost::lexical_cast<string>(arguments) + + " arguments but got " + + boost::lexical_cast<string>(_funCall.arguments.size()) + + ".", + _funCall.functionName.location + )); + success = false; + } + } + for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) + { + int const stackHeight = m_stackHeight; + if (!boost::apply_visitor(*this, arg)) + success = false; + if (!expectDeposit(1, stackHeight, locationOf(arg))) + success = false; + } + m_stackHeight += int(returns) - int(arguments); + m_info.stackHeightInfo[&_funCall] = m_stackHeight; + 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(); + m_currentScope = &scope(&_block); + + int const initialStackHeight = m_stackHeight - m_virtualVariablesInNextBlock; + m_virtualVariablesInNextBlock = 0; for (auto const& s: _block.statements) if (!boost::apply_visitor(*this, s)) success = false; + for (auto const& identifier: scope(&_block).identifiers) + if (identifier.second.type() == typeid(Scope::Variable)) + --m_stackHeight; + + int const stackDiff = m_stackHeight - initialStackHeight; + if (stackDiff != 0) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Unbalanced stack at the end of a block: " + + ( + stackDiff > 0 ? + to_string(stackDiff) + string(" surplus item(s).") : + to_string(-stackDiff) + string(" missing item(s).") + ), + _block.location + )); + success = false; + } + m_currentScope = m_currentScope->superScope; + m_info.stackHeightInfo[&_block] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize) +{ + bool success = true; + size_t numErrorsBefore = m_errors.size(); + size_t variableSize(-1); + if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) + { + // Check that it is a variable + if (var->type() != typeid(Scope::Variable)) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Assignment requires variable.", + _variable.location + )); + success = false; + } + else if (!boost::get<Scope::Variable>(*var).active) + { + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Variable " + _variable.name + " used before it was declared.", + _variable.location + )); + success = false; + } + variableSize = 1; + } + else if (m_resolver) + variableSize = m_resolver(_variable, IdentifierContext::LValue); + if (variableSize == size_t(-1)) + { + // Only add message if the callback did not. + if (numErrorsBefore == m_errors.size()) + m_errors.push_back(make_shared<Error>( + Error::Type::DeclarationError, + "Variable not found or variable not lvalue.", + _variable.location + )); + success = false; + } + if (_valueSize == size_t(-1)) + _valueSize = variableSize == size_t(-1) ? 1 : variableSize; + + m_stackHeight -= _valueSize; + + if (_valueSize != variableSize && variableSize != size_t(-1)) + { + m_errors.push_back(make_shared<Error>( + Error::Type::TypeError, + "Variable size (" + + to_string(variableSize) + + ") and value size (" + + to_string(_valueSize) + + ") do not match.", + _variable.location + )); + success = false; + } return success; } + +bool AsmAnalyzer::expectDeposit(int const _deposit, int const _oldHeight, SourceLocation const& _location) +{ + int stackDiff = m_stackHeight - _oldHeight; + if (stackDiff != _deposit) + { + m_errors.push_back(make_shared<Error>( + 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>(stackDiff) + + " item(s).", + _location + )); + return false; + } + else + return true; +} + +Scope& AsmAnalyzer::scope(Block const* _block) +{ + auto scopePtr = m_info.scopes.at(_block); + solAssert(scopePtr, "Scope requested but not present."); + return *scopePtr; +} |