aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/inlineasm
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/inlineasm')
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp372
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h119
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.cpp26
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.h61
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp202
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h33
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp16
-rw-r--r--libsolidity/inlineasm/AsmParser.h5
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp2
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp79
-rw-r--r--libsolidity/inlineasm/AsmScope.h128
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp130
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h89
-rw-r--r--libsolidity/inlineasm/AsmStack.cpp25
-rw-r--r--libsolidity/inlineasm/AsmStack.h29
15 files changed, 1002 insertions, 314 deletions
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index a3ddb61d..dad05a78 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,363 @@ 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)
{
+ ++m_stackHeight;
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)"
+ "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.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)).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)
+ {
+ arguments = _fun.arguments;
+ returns = _fun.returns;
+ }
+ )))
+ {
+ 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;
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 9726210d..426ee0d2 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -20,6 +20,8 @@
#pragma once
+#include <libsolidity/inlineasm/AsmStack.h>
+
#include <libsolidity/interface/Exceptions.h>
#include <boost/variant.hpp>
@@ -46,101 +48,32 @@ struct Assignment;
struct FunctionDefinition;
struct FunctionCall;
-template <class...>
-struct GenericVisitor{};
-
-template <class Visitable, class... Others>
-struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
-{
- using GenericVisitor<Others...>::operator ();
- explicit GenericVisitor(
- std::function<void(Visitable&)> _visitor,
- std::function<void(Others&)>... _otherVisitors
- ):
- GenericVisitor<Others...>(_otherVisitors...),
- m_visitor(_visitor)
- {}
-
- void operator()(Visitable& _v) const { m_visitor(_v); }
-
- std::function<void(Visitable&)> m_visitor;
-};
-template <>
-struct GenericVisitor<>: public boost::static_visitor<> {
- void operator()() const {}
-};
-
-
-struct Scope
-{
- struct Variable
- {
- int stackHeight = 0;
- bool active = false;
- };
-
- struct Label
- {
- size_t id = unassignedLabelId;
- static const size_t errorLabelId = -1;
- static const size_t unassignedLabelId = 0;
- };
-
- struct Function
- {
- Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
- size_t arguments = 0;
- size_t returns = 0;
- };
-
- using Identifier = boost::variant<Variable, Label, Function>;
- using Visitor = GenericVisitor<Variable const, Label const, Function const>;
- using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
-
- bool registerVariable(std::string const& _name);
- bool registerLabel(std::string const& _name);
- bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
-
- /// Looks up the identifier in this or super scopes (stops and function and assembly boundaries)
- /// 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 (stops and function and assembly boundaries)
- /// 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;
- }
- /// @returns true if the name exists in this scope or in super scopes (also searches
- /// across function and assembly boundaries).
- bool exists(std::string const& _name);
- Scope* superScope = nullptr;
- /// If true, identifiers from the super scope are not visible here, but they are still
- /// taken into account to prevent shadowing.
- bool closedScope = false;
- std::map<std::string, Identifier> identifiers;
-};
+struct Scope;
+struct AsmAnalysisInfo;
+/**
+ * Performs the full analysis stage, calls the ScopeFiller internally, then resolves
+ * references and performs other checks.
+ * If all these checks pass, code generation should not throw errors.
+ */
class AsmAnalyzer: public boost::static_visitor<bool>
{
public:
- using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
- AsmAnalyzer(Scopes& _scopes, ErrorList& _errors);
+ AsmAnalyzer(
+ AsmAnalysisInfo& _analysisInfo,
+ ErrorList& _errors,
+ ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver()
+ );
+
+ bool analyze(assembly::Block const& _block);
- bool operator()(assembly::Instruction const&) { return true; }
+ bool operator()(assembly::Instruction const&);
bool operator()(assembly::Literal const& _literal);
- bool operator()(assembly::Identifier const&) { return true; }
+ bool operator()(assembly::Identifier const&);
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label);
- bool operator()(assembly::Assignment const&) { return true; }
+ bool operator()(assembly::Assignment const&);
bool operator()(assembly::FunctionalAssignment const& _functionalAssignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
@@ -148,8 +81,20 @@ public:
bool operator()(assembly::Block const& _block);
private:
+ /// Verifies that a variable to be assigned to exists and has the same size
+ /// as the value, @a _valueSize, unless that is equal to -1.
+ bool checkAssignment(assembly::Identifier const& _assignment, size_t _valueSize = size_t(-1));
+ bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location);
+ Scope& scope(assembly::Block const* _block);
+
+ /// This is used when we enter the body of a function definition. There, the parameters
+ /// and return parameters appear as variables which are already on the stack before
+ /// we enter the block.
+ int m_virtualVariablesInNextBlock = 0;
+ int m_stackHeight = 0;
+ ExternalIdentifierAccess::Resolver const& m_resolver;
Scope* m_currentScope = nullptr;
- Scopes& m_scopes;
+ AsmAnalysisInfo& m_info;
ErrorList& m_errors;
};
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.cpp b/libsolidity/inlineasm/AsmAnalysisInfo.cpp
new file mode 100644
index 00000000..22318b12
--- /dev/null
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.cpp
@@ -0,0 +1,26 @@
+/*
+ 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/>.
+*/
+/**
+ * Information generated during analyzer part of inline assembly.
+ */
+
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+
+#include <libsolidity/inlineasm/AsmScope.h>
+
+#include <ostream>
+
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libsolidity/inlineasm/AsmAnalysisInfo.h
new file mode 100644
index 00000000..e21eb2c5
--- /dev/null
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.h
@@ -0,0 +1,61 @@
+/*
+ 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/>.
+*/
+/**
+ * Information generated during analyzer part of inline assembly.
+ */
+
+#pragma once
+
+#include <boost/variant.hpp>
+
+#include <map>
+#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 FunctionDefinition;
+struct FunctionCall;
+
+struct Scope;
+
+using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
+
+struct AsmAnalysisInfo
+{
+ using StackHeightInfo = std::map<void const*, int>;
+ using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
+ Scopes scopes;
+ StackHeightInfo stackHeightInfo;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 78a9ee27..9ef3e6e7 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -24,7 +24,9 @@
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScope.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
@@ -46,13 +48,8 @@ using namespace dev::solidity::assembly;
struct GeneratorState
{
- GeneratorState(ErrorList& _errors, eth::Assembly& _assembly):
- errors(_errors), assembly(_assembly) {}
-
- void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation())
- {
- errors.push_back(make_shared<Error>(_type, _description, _location));
- }
+ GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly):
+ errors(_errors), info(_analysisInfo), assembly(_assembly) {}
size_t newLabelId()
{
@@ -66,8 +63,8 @@ struct GeneratorState
return size_t(id);
}
- std::map<assembly::Block const*, shared_ptr<Scope>> scopes;
ErrorList& errors;
+ AsmAnalysisInfo info;
eth::Assembly& assembly;
};
@@ -80,13 +77,24 @@ public:
explicit CodeTransform(
GeneratorState& _state,
assembly::Block const& _block,
- assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
+ assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
+ ): CodeTransform(_state, _block, _identifierAccess, _state.assembly.deposit())
+ {
+ }
+
+private:
+ CodeTransform(
+ GeneratorState& _state,
+ assembly::Block const& _block,
+ assembly::ExternalIdentifierAccess const& _identifierAccess,
+ int _initialDeposit
):
m_state(_state),
- m_scope(*m_state.scopes.at(&_block)),
- m_initialDeposit(m_state.assembly.deposit()),
- m_identifierAccess(_identifierAccess)
+ m_scope(*m_state.info.scopes.at(&_block)),
+ m_identifierAccess(_identifierAccess),
+ m_initialDeposit(_initialDeposit)
{
+ int blockStartDeposit = m_state.assembly.deposit();
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
m_state.assembly.setSourceLocation(_block.location);
@@ -96,31 +104,16 @@ public:
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
- );
- }
+ int deposit = m_state.assembly.deposit() - blockStartDeposit;
+ solAssert(deposit == 0, "Invalid stack height at end of block.");
}
+public:
void operator()(assembly::Instruction const& _instruction)
{
m_state.assembly.setSourceLocation(_instruction.location);
m_state.assembly.append(_instruction.instruction);
+ checkStackHeight(&_instruction);
}
void operator()(assembly::Literal const& _literal)
{
@@ -130,8 +123,9 @@ public:
else
{
solAssert(_literal.value.size() <= 32, "");
- m_state.assembly.append(_literal.value);
+ m_state.assembly.append(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft)));
}
+ checkStackHeight(&_literal);
}
void operator()(assembly::Identifier const& _identifier)
{
@@ -153,20 +147,18 @@ public:
},
[=](Scope::Function&)
{
- solAssert(false, "Not yet implemented");
+ solAssert(false, "Function not removed during desugaring.");
}
)))
{
+ return;
}
- else if (!m_identifierAccess || !m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Identifier not found or not unique",
- _identifier.location
- );
- m_state.assembly.append(u256(0));
- }
+ solAssert(
+ m_identifierAccess.generateCode,
+ "Identifier not found and no external access available."
+ );
+ m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly);
+ checkStackHeight(&_identifier);
}
void operator()(FunctionalInstruction const& _instr)
{
@@ -174,9 +166,10 @@ public:
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *it);
- expectDeposit(1, height, locationOf(*it));
+ expectDeposit(1, height);
}
(*this)(_instr.instruction);
+ checkStackHeight(&_instr);
}
void operator()(assembly::FunctionCall const&)
{
@@ -186,36 +179,39 @@ public:
{
m_state.assembly.setSourceLocation(_label.location);
solAssert(m_scope.identifiers.count(_label.name), "");
- Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers[_label.name]);
+ Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers.at(_label.name));
assignLabelIdIfUnset(label);
m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id));
+ checkStackHeight(&_label);
}
void operator()(assembly::Assignment const& _assignment)
{
m_state.assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName, _assignment.location);
+ checkStackHeight(&_assignment);
}
void operator()(FunctionalAssignment const& _assignment)
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_assignment.value);
- expectDeposit(1, height, locationOf(*_assignment.value));
+ expectDeposit(1, height);
m_state.assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName, _assignment.location);
+ checkStackHeight(&_assignment);
}
void operator()(assembly::VariableDeclaration const& _varDecl)
{
int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_varDecl.value);
- expectDeposit(1, height, locationOf(*_varDecl.value));
- solAssert(m_scope.identifiers.count(_varDecl.name), "");
- auto& var = boost::get<Scope::Variable>(m_scope.identifiers[_varDecl.name]);
+ expectDeposit(1, height);
+ auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name));
var.stackHeight = height;
var.active = true;
}
void operator()(assembly::Block const& _block)
{
- CodeTransform(m_state, _block, m_identifierAccess);
+ CodeTransform(m_state, _block, m_identifierAccess, m_initialDeposit);
+ checkStackHeight(&_block);
}
void operator()(assembly::FunctionDefinition const&)
{
@@ -225,35 +221,22 @@ public:
private:
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
{
- if (m_scope.lookup(_variableName.name, Scope::Visitor(
- [=](Scope::Variable const& _var)
- {
- if (int heightDiff = variableHeightDiff(_var, _location, true))
- m_state.assembly.append(solidity::swapInstruction(heightDiff - 1));
- m_state.assembly.append(solidity::Instruction::POP);
- },
- [=](Scope::Label const&)
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Label \"" + string(_variableName.name) + "\" used as variable."
- );
- },
- [=](Scope::Function const&)
- {
- m_state.addError(
- Error::Type::DeclarationError,
- "Function \"" + string(_variableName.name) + "\" used as variable."
- );
- }
- )))
+ auto var = m_scope.lookup(_variableName.name);
+ if (var)
{
+ Scope::Variable const& _var = boost::get<Scope::Variable>(*var);
+ if (int heightDiff = variableHeightDiff(_var, _location, true))
+ m_state.assembly.append(solidity::swapInstruction(heightDiff - 1));
+ m_state.assembly.append(solidity::Instruction::POP);
}
- else if (!m_identifierAccess || !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
- m_state.addError(
- Error::Type::DeclarationError,
- "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
+ else
+ {
+ solAssert(
+ m_identifierAccess.generateCode,
+ "Identifier not found and no external access available."
);
+ m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly);
+ }
}
/// Determines the stack height difference to the given variables. Automatically generates
@@ -261,36 +244,33 @@ private:
/// errors and the (positive) stack height difference otherwise.
int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap)
{
- if (!_var.active)
- {
- m_state.addError( Error::Type::TypeError, "Variable used before it was declared", _location);
- return 0;
- }
int heightDiff = m_state.assembly.deposit() - _var.stackHeight;
if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16))
{
- m_state.addError(
+ //@TODO move this to analysis phase.
+ m_state.errors.push_back(make_shared<Error>(
Error::Type::TypeError,
"Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")",
_location
- );
+ ));
return 0;
}
else
return heightDiff;
}
- void expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location)
+ void expectDeposit(int _deposit, int _oldHeight)
{
- if (m_state.assembly.deposit() != _oldHeight + 1)
- 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).",
- _location
- );
+ solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit.");
+ }
+
+ void checkStackHeight(void const* _astElement)
+ {
+ solAssert(m_state.info.stackHeightInfo.count(_astElement), "Stack height for AST element not found.");
+ solAssert(
+ m_state.info.stackHeightInfo.at(_astElement) == m_state.assembly.deposit() - m_initialDeposit,
+ "Stack height mismatch between analysis and code generation phase."
+ );
}
/// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set.
@@ -305,35 +285,29 @@ private:
GeneratorState& m_state;
Scope& m_scope;
+ ExternalIdentifierAccess m_identifierAccess;
int const m_initialDeposit;
- assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
};
-bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
-{
- size_t initialErrorLen = m_errors.size();
- eth::Assembly assembly;
- GeneratorState state(m_errors, assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- return false;
- CodeTransform(state, m_parsedData, _identifierAccess);
- return m_errors.size() == initialErrorLen;
-}
-
-eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
+eth::Assembly assembly::CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ ExternalIdentifierAccess const& _identifierAccess
+)
{
eth::Assembly assembly;
- GeneratorState state(m_errors, assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- solAssert(false, "Assembly error");
- CodeTransform(state, m_parsedData, _identifierAccess);
+ GeneratorState state(m_errors, _analysisInfo, assembly);
+ CodeTransform(state, _parsedData, _identifierAccess);
return assembly;
}
-void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
+void assembly::CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess
+)
{
- GeneratorState state(m_errors, _assembly);
- if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
- solAssert(false, "Assembly error");
- CodeTransform(state, m_parsedData, _identifierAccess);
+ GeneratorState state(m_errors, _analysisInfo, _assembly);
+ CodeTransform(state, _parsedData, _identifierAccess);
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index bd71812e..e830e047 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -22,9 +22,11 @@
#pragma once
-#include <functional>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/interface/Exceptions.h>
+#include <functional>
+
namespace dev
{
namespace eth
@@ -36,30 +38,27 @@ namespace solidity
namespace assembly
{
struct Block;
-struct Identifier;
class CodeGenerator
{
public:
- enum class IdentifierContext { LValue, RValue };
- /// Function type that is called for external identifiers. Such a function should search for
- /// the identifier and append appropriate assembly items to the assembly. If in lvalue context,
- /// the value to assign is assumed to be on the stack and an assignment is to be performed.
- /// 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):
- m_parsedData(_parsedData), m_errors(_errors) {}
- /// Performs type checks and @returns false on error.
- /// Actually runs the full code generation but discards the result.
- bool typeCheck(IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ CodeGenerator(ErrorList& _errors):
+ m_errors(_errors) {}
/// Performs code generation and @returns the result.
- eth::Assembly assemble(IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ eth::Assembly assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ );
/// Performs code generation and appends generated to to _assembly.
- void assemble(eth::Assembly& _assembly, IdentifierAccess const& _identifierAccess = IdentifierAccess());
+ void assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ );
private:
- Block const& m_parsedData;
ErrorList& m_errors;
};
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 0fc0a34f..d7f78958 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -24,6 +24,7 @@
#include <ctype.h>
#include <algorithm>
#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/Exceptions.h>
using namespace std;
using namespace dev;
@@ -68,12 +69,14 @@ assembly::Statement Parser::parseStatement()
return parseBlock();
case Token::Assign:
{
+ if (m_julia)
+ break;
assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
m_scanner->next();
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = m_scanner->currentLiteral();
- if (instructions().count(assignment.variableName.name))
+ if (!m_julia && instructions().count(assignment.variableName.name))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
@@ -105,7 +108,7 @@ assembly::Statement Parser::parseStatement()
{
// functional assignment
FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
- if (instructions().count(identifier.name))
+ if (!m_julia && instructions().count(identifier.name))
fatalParserError("Cannot use instruction names for identifier names.");
m_scanner->next();
funAss.variableName = identifier;
@@ -180,7 +183,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
else
literal = m_scanner->currentLiteral();
// first search the set of instructions.
- if (instructions().count(literal))
+ if (!m_julia && instructions().count(literal))
{
dev::solidity::Instruction const& instr = instructions().at(literal);
if (_onlySinglePusher)
@@ -242,15 +245,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
{
expectToken(Token::Sub);
expectToken(Token::GreaterThan);
- expectToken(Token::LParen);
while (true)
{
funDef.returns.push_back(expectAsmIdentifier());
- if (m_scanner->currentToken() == Token::RParen)
+ if (m_scanner->currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
}
- expectToken(Token::RParen);
}
funDef.body = parseBlock();
funDef.location.end = funDef.body.location.end;
@@ -261,6 +262,7 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
{
if (_instruction.type() == typeid(Instruction))
{
+ solAssert(!m_julia, "Instructions are invalid in JULIA");
FunctionalInstruction ret;
ret.instruction = std::move(boost::get<Instruction>(_instruction));
ret.location = ret.instruction.location;
@@ -323,7 +325,7 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
string Parser::expectAsmIdentifier()
{
string name = m_scanner->currentLiteral();
- if (instructions().count(name))
+ if (!m_julia && instructions().count(name))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
return name;
diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h
index 4b4a24ae..c55fd2ac 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -37,7 +37,7 @@ namespace assembly
class Parser: public ParserBase
{
public:
- Parser(ErrorList& _errors): ParserBase(_errors) {}
+ explicit Parser(ErrorList& _errors, bool _julia = false): ParserBase(_errors), m_julia(_julia) {}
/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @returns an empty shared pointer on error.
@@ -70,6 +70,9 @@ protected:
FunctionDefinition parseFunctionDefinition();
Statement parseFunctionalInstruction(Statement&& _instruction);
std::string expectAsmIdentifier();
+
+private:
+ bool m_julia = false;
};
}
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index a70b0b78..252e91f9 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -116,7 +116,7 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin
{
string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
if (!_functionDefinition.returns.empty())
- out += " -> (" + boost::algorithm::join(_functionDefinition.returns, ", ") + ")";
+ out += " -> " + boost::algorithm::join(_functionDefinition.returns, ", ");
return out + "\n" + (*this)(_functionDefinition.body);
}
diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp
new file mode 100644
index 00000000..609dca16
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScope.cpp
@@ -0,0 +1,79 @@
+/*
+ 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/>.
+*/
+/**
+ * Scopes for identifiers.
+ */
+
+#include <libsolidity/inlineasm/AsmScope.h>
+
+using namespace std;
+using namespace dev::solidity::assembly;
+
+
+bool Scope::registerLabel(string const& _name)
+{
+ if (exists(_name))
+ return false;
+ identifiers[_name] = Label();
+ return true;
+}
+
+bool Scope::registerVariable(string const& _name)
+{
+ if (exists(_name))
+ 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;
+}
+
+Scope::Identifier* Scope::lookup(string const& _name)
+{
+ bool crossedFunctionBoundary = false;
+ for (Scope* s = this; s; s = s->superScope)
+ {
+ auto id = s->identifiers.find(_name);
+ if (id != s->identifiers.end())
+ {
+ if (crossedFunctionBoundary && id->second.type() == typeid(Scope::Variable))
+ return nullptr;
+ else
+ return &id->second;
+ }
+
+ if (s->functionScope)
+ crossedFunctionBoundary = true;
+ }
+ return nullptr;
+}
+
+bool Scope::exists(string const& _name)
+{
+ if (identifiers.count(_name))
+ return true;
+ else if (superScope)
+ return superScope->exists(_name);
+ else
+ return false;
+}
diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h
new file mode 100644
index 00000000..37e0f0b8
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScope.h
@@ -0,0 +1,128 @@
+/*
+ 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/>.
+*/
+/**
+ * Scopes for identifiers.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/variant.hpp>
+
+#include <functional>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+namespace assembly
+{
+
+template <class...>
+struct GenericVisitor{};
+
+template <class Visitable, class... Others>
+struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
+{
+ using GenericVisitor<Others...>::operator ();
+ explicit GenericVisitor(
+ std::function<void(Visitable&)> _visitor,
+ std::function<void(Others&)>... _otherVisitors
+ ):
+ GenericVisitor<Others...>(_otherVisitors...),
+ m_visitor(_visitor)
+ {}
+
+ void operator()(Visitable& _v) const { m_visitor(_v); }
+
+ std::function<void(Visitable&)> m_visitor;
+};
+template <>
+struct GenericVisitor<>: public boost::static_visitor<> {
+ void operator()() const {}
+};
+
+
+struct Scope
+{
+ struct Variable
+ {
+ /// Used during code generation to store the stack height. @todo move there.
+ int stackHeight = 0;
+ /// Used during analysis to check whether we already passed the declaration inside the block.
+ /// @todo move there.
+ bool active = false;
+ };
+
+ struct Label
+ {
+ size_t id = unassignedLabelId;
+ static const size_t errorLabelId = -1;
+ static const size_t unassignedLabelId = 0;
+ };
+
+ struct Function
+ {
+ Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
+ size_t arguments = 0;
+ size_t returns = 0;
+ };
+
+ using Identifier = boost::variant<Variable, Label, Function>;
+ using Visitor = GenericVisitor<Variable const, Label const, Function const>;
+ using NonconstVisitor = GenericVisitor<Variable, Label, Function>;
+
+ bool registerVariable(std::string const& _name);
+ bool registerLabel(std::string const& _name);
+ bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
+
+ /// Looks up the identifier in this or super scopes and returns a valid pointer if found
+ /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as
+ /// will any lookups across assembly boundaries.
+ /// The pointer will be invalidated if the scope is modified.
+ /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup
+ Identifier* lookup(std::string const& _name);
+ /// Looks up the identifier in this and super scopes (will not find variables across function
+ /// boundaries and generally stops at assembly boundaries) 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;
+ }
+ /// @returns true if the name exists in this scope or in super scopes (also searches
+ /// across function and assembly boundaries).
+ bool exists(std::string const& _name);
+
+ Scope* superScope = nullptr;
+ /// If true, variables from the super scope are not visible here (other identifiers are),
+ /// but they are still taken into account to prevent shadowing.
+ bool functionScope = false;
+ std::map<std::string, Identifier> identifiers;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
new file mode 100644
index 00000000..de6fbdaa
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -0,0 +1,130 @@
+/*
+ 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/>.
+*/
+/**
+ * Module responsible for registering identifiers inside their scopes.
+ */
+
+#include <libsolidity/inlineasm/AsmScopeFiller.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmScope.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;
+
+ScopeFiller::ScopeFiller(ScopeFiller::Scopes& _scopes, ErrorList& _errors):
+ m_scopes(_scopes), m_errors(_errors)
+{
+ // Make the Solidity ErrorTag available to inline assembly
+ Scope::Label errorLabel;
+ errorLabel.id = Scope::Label::errorLabelId;
+ scope(nullptr).identifiers["invalidJumpLabel"] = errorLabel;
+ m_currentScope = &scope(nullptr);
+}
+
+bool ScopeFiller::operator()(Label const& _item)
+{
+ 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 ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl)
+{
+ return registerVariable(_varDecl.name, _varDecl.location, *m_currentScope);
+}
+
+bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
+{
+ bool success = true;
+ if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size()))
+ {
+ //@TODO secondary location
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Function name " + _funDef.name + " already taken in this scope.",
+ _funDef.location
+ ));
+ success = false;
+ }
+ Scope& body = scope(&_funDef.body);
+ body.superScope = m_currentScope;
+ body.functionScope = true;
+ for (auto const& var: _funDef.arguments + _funDef.returns)
+ if (!registerVariable(var, _funDef.location, body))
+ success = false;
+
+ if (!(*this)(_funDef.body))
+ success = false;
+
+ return success;
+}
+
+bool ScopeFiller::operator()(Block const& _block)
+{
+ bool success = true;
+ scope(&_block).superScope = m_currentScope;
+ m_currentScope = &scope(&_block);
+
+ for (auto const& s: _block.statements)
+ if (!boost::apply_visitor(*this, s))
+ success = false;
+
+ m_currentScope = m_currentScope->superScope;
+ return success;
+}
+
+bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _location, Scope& _scope)
+{
+ if (!_scope.registerVariable(_name))
+ {
+ //@TODO secondary location
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable name " + _name + " already taken in this scope.",
+ _location
+ ));
+ return false;
+ }
+ return true;
+}
+
+Scope& ScopeFiller::scope(Block const* _block)
+{
+ auto& scope = m_scopes[_block];
+ if (!scope)
+ scope = make_shared<Scope>();
+ return *scope;
+}
diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h
new file mode 100644
index 00000000..bb62948b
--- /dev/null
+++ b/libsolidity/inlineasm/AsmScopeFiller.h
@@ -0,0 +1,89 @@
+/*
+ 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/>.
+*/
+/**
+ * Module responsible for registering identifiers inside their scopes.
+ */
+
+#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 FunctionDefinition;
+struct FunctionCall;
+
+struct Scope;
+
+/**
+ * Fills scopes with identifiers and checks for name clashes.
+ * Does not resolve references.
+ */
+class ScopeFiller: public boost::static_visitor<bool>
+{
+public:
+ using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
+ ScopeFiller(Scopes& _scopes, ErrorList& _errors);
+
+ bool operator()(assembly::Instruction const&) { return true; }
+ bool operator()(assembly::Literal const&) { return true; }
+ bool operator()(assembly::Identifier const&) { return true; }
+ bool operator()(assembly::FunctionalInstruction const&) { return true; }
+ bool operator()(assembly::Label const& _label);
+ bool operator()(assembly::Assignment const&) { return true; }
+ bool operator()(assembly::FunctionalAssignment const&) { return true; }
+ bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
+ bool operator()(assembly::FunctionDefinition const& _functionDefinition);
+ bool operator()(assembly::FunctionCall const&) { return true; }
+ bool operator()(assembly::Block const& _block);
+
+private:
+ bool registerVariable(
+ std::string const& _name,
+ SourceLocation const& _location,
+ Scope& _scope
+ );
+
+ Scope& scope(assembly::Block const* _block);
+
+ Scope* m_currentScope = nullptr;
+ Scopes& m_scopes;
+ ErrorList& m_errors;
+};
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp
index 266136a1..c2a7d8ea 100644
--- a/libsolidity/inlineasm/AsmStack.cpp
+++ b/libsolidity/inlineasm/AsmStack.cpp
@@ -26,6 +26,7 @@
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/parsing/Scanner.h>
@@ -39,7 +40,10 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
+bool InlineAssemblyStack::parse(
+ shared_ptr<Scanner> const& _scanner,
+ ExternalIdentifierAccess::Resolver const& _resolver
+)
{
m_parserResult = make_shared<Block>();
Parser parser(m_errors);
@@ -48,8 +52,8 @@ bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
return false;
*m_parserResult = std::move(*result);
- AsmAnalyzer::Scopes scopes;
- return (AsmAnalyzer(scopes, m_errors))(*m_parserResult);
+ AsmAnalysisInfo analysisInfo;
+ return (AsmAnalyzer(analysisInfo, m_errors, _resolver)).analyze(*m_parserResult);
}
string InlineAssemblyStack::toString()
@@ -59,14 +63,17 @@ string InlineAssemblyStack::toString()
eth::Assembly InlineAssemblyStack::assemble()
{
- CodeGenerator codeGen(*m_parserResult, m_errors);
- return codeGen.assemble();
+ AsmAnalysisInfo analysisInfo;
+ AsmAnalyzer analyzer(analysisInfo, m_errors);
+ solAssert(analyzer.analyze(*m_parserResult), "");
+ CodeGenerator codeGen(m_errors);
+ return codeGen.assemble(*m_parserResult, analysisInfo);
}
bool InlineAssemblyStack::parseAndAssemble(
string const& _input,
eth::Assembly& _assembly,
- CodeGenerator::IdentifierAccess const& _identifierAccess
+ ExternalIdentifierAccess const& _identifierAccess
)
{
ErrorList errors;
@@ -74,8 +81,12 @@ bool InlineAssemblyStack::parseAndAssemble(
auto parserResult = Parser(errors).parse(scanner);
if (!errors.empty())
return false;
+ solAssert(parserResult, "");
- CodeGenerator(*parserResult, errors).assemble(_assembly, _identifierAccess);
+ AsmAnalysisInfo analysisInfo;
+ AsmAnalyzer analyzer(analysisInfo, errors, _identifierAccess.resolve);
+ solAssert(analyzer.analyze(*parserResult), "");
+ CodeGenerator(errors).assemble(*parserResult, analysisInfo, _assembly, _identifierAccess);
// At this point, the assembly might be messed up, but we should throw an
// internal compiler error anyway.
diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h
index 4d5a99a4..77a7e02a 100644
--- a/libsolidity/inlineasm/AsmStack.h
+++ b/libsolidity/inlineasm/AsmStack.h
@@ -22,10 +22,10 @@
#pragma once
+#include <libsolidity/interface/Exceptions.h>
+
#include <string>
#include <functional>
-#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/inlineasm/AsmCodeGen.h>
namespace dev
{
@@ -39,13 +39,34 @@ class Scanner;
namespace assembly
{
struct Block;
+struct Identifier;
+
+enum class IdentifierContext { LValue, RValue };
+
+/// Object that is used to resolve references and generate code for access to identifiers external
+/// to inline assembly (not used in standalone assembly mode).
+struct ExternalIdentifierAccess
+{
+ using Resolver = std::function<size_t(assembly::Identifier const&, IdentifierContext)>;
+ /// Resolve a an external reference given by the identifier in the given context.
+ /// @returns the size of the value (number of stack slots) or size_t(-1) if not found.
+ Resolver resolve;
+ using CodeGenerator = std::function<void(assembly::Identifier const&, IdentifierContext, eth::Assembly&)>;
+ /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context)
+ /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed
+ /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack.
+ CodeGenerator generateCode;
+};
class InlineAssemblyStack
{
public:
/// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`.
/// @return false or error.
- bool parse(std::shared_ptr<Scanner> const& _scanner);
+ bool parse(
+ std::shared_ptr<Scanner> const& _scanner,
+ ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = ExternalIdentifierAccess::Resolver()
+ );
/// Converts the parser result back into a string form (not necessarily the same form
/// as the source form, but it should parse into the same parsed form again).
std::string toString();
@@ -56,7 +77,7 @@ public:
bool parseAndAssemble(
std::string const& _input,
eth::Assembly& _assembly,
- CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess()
+ ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
);
ErrorList const& errors() const { return m_errors; }