aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/inlineasm
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/inlineasm')
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp368
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h58
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.h19
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp272
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h16
-rw-r--r--libsolidity/inlineasm/AsmData.h41
-rw-r--r--libsolidity/inlineasm/AsmDataForward.h52
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp282
-rw-r--r--libsolidity/inlineasm/AsmParser.h10
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp91
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h24
-rw-r--r--libsolidity/inlineasm/AsmScope.cpp27
-rw-r--r--libsolidity/inlineasm/AsmScope.h38
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp100
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h31
-rw-r--r--libsolidity/inlineasm/AsmStack.cpp95
-rw-r--r--libsolidity/inlineasm/AsmStack.h92
17 files changed, 843 insertions, 773 deletions
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index dad05a78..7e00ffae 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -25,10 +25,10 @@
#include <libsolidity/inlineasm/AsmScope.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
-#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string.hpp>
#include <memory>
#include <functional>
@@ -38,18 +38,15 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-AsmAnalyzer::AsmAnalyzer(
- AsmAnalysisInfo& _analysisInfo,
- ErrorList& _errors,
- ExternalIdentifierAccess::Resolver const& _resolver
-):
- m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors)
-{
+namespace {
+
+set<string> const builtinTypes{"bool", "u8", "s8", "u32", "s32", "u64", "s64", "u128", "s128", "u256", "s256"};
+
}
bool AsmAnalyzer::analyze(Block const& _block)
{
- if (!(ScopeFiller(m_info.scopes, m_errors))(_block))
+ if (!(ScopeFiller(m_info, m_errorReporter))(_block))
return false;
return (*this)(_block);
@@ -57,28 +54,31 @@ bool AsmAnalyzer::analyze(Block const& _block)
bool AsmAnalyzer::operator()(Label const& _label)
{
+ solAssert(!m_julia, "");
m_info.stackHeightInfo[&_label] = m_stackHeight;
return true;
}
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
{
+ solAssert(!m_julia, "");
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
+ warnOnInstructions(_instruction.instruction, _instruction.location);
return true;
}
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
{
+ expectValidType(_literal.type, _literal.location);
++m_stackHeight;
- if (!_literal.isNumber && _literal.value.size() > 32)
+ 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)",
- _literal.location
- ));
+ m_errorReporter.typeError(
+ _literal.location,
+ "String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)"
+ );
return false;
}
m_info.stackHeightInfo[&_literal] = m_stackHeight;
@@ -87,18 +87,17 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
{
- size_t numErrorsBefore = m_errors.size();
+ size_t numErrorsBefore = m_errorReporter.errors().size();
bool success = true;
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
[&](Scope::Variable const& _var)
{
- if (!_var.active)
+ if (!m_activeVariables.count(&_var))
{
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Variable " + _identifier.name + " used before it was declared.",
- _identifier.location
- ));
+ m_errorReporter.declarationError(
+ _identifier.location,
+ "Variable " + _identifier.name + " used before it was declared."
+ );
success = false;
}
++m_stackHeight;
@@ -109,11 +108,10 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
},
[&](Scope::Function const&)
{
- m_errors.push_back(make_shared<Error>(
- Error::Type::TypeError,
- "Function " + _identifier.name + " used without being called.",
- _identifier.location
- ));
+ m_errorReporter.typeError(
+ _identifier.location,
+ "Function " + _identifier.name + " used without being called."
+ );
success = false;
}
)))
@@ -123,16 +121,15 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
{
size_t stackSize(-1);
if (m_resolver)
- stackSize = m_resolver(_identifier, IdentifierContext::RValue);
+ {
+ bool insideFunction = m_currentScope->insideFunction();
+ stackSize = m_resolver(_identifier, julia::IdentifierContext::RValue, insideFunction);
+ }
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
- ));
+ if (numErrorsBefore == m_errorReporter.errors().size())
+ m_errorReporter.declarationError(_identifier.location, "Identifier not found.");
success = false;
}
m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize;
@@ -143,15 +140,11 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
+ solAssert(!m_julia, "");
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
- {
- int const stackHeight = m_stackHeight;
- if (!boost::apply_visitor(*this, arg))
+ if (!expectExpression(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))
@@ -160,15 +153,15 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
return success;
}
-bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
+bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
{
+ solAssert(!m_julia, "");
bool success = checkAssignment(_assignment.variableName, size_t(-1));
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
return success;
}
-
-bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
+bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
{
int const stackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, *_assignment.value);
@@ -181,23 +174,37 @@ bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
{
+ int const expectedItems = _varDecl.variables.size();
int const stackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, *_varDecl.value);
- solAssert(m_stackHeight - stackHeight == 1, "Invalid value size.");
- boost::get<Scope::Variable>(m_currentScope->identifiers.at(_varDecl.name)).active = true;
+ if ((m_stackHeight - stackHeight) != expectedItems)
+ {
+ m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
+ return false;
+ }
+
+ for (auto const& variable: _varDecl.variables)
+ {
+ expectValidType(variable.type, variable.location);
+ m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)));
+ }
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
{
- Scope& bodyScope = scope(&_funDef.body);
+ Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
+ solAssert(virtualBlock, "");
+ Scope& varScope = scope(virtualBlock);
for (auto const& var: _funDef.arguments + _funDef.returns)
- boost::get<Scope::Variable>(bodyScope.identifiers.at(var)).active = true;
+ {
+ expectValidType(var.type, var.location);
+ m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
+ }
int const stackHeight = m_stackHeight;
m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
- m_virtualVariablesInNextBlock = m_stackHeight;
bool success = (*this)(_funDef.body);
@@ -214,141 +221,213 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
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
- ));
+ m_errorReporter.typeError(
+ _funCall.functionName.location,
+ "Attempt to call variable instead of function."
+ );
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
- ));
+ m_errorReporter.typeError(
+ _funCall.functionName.location,
+ "Attempt to call label instead of function."
+ );
success = false;
},
[&](Scope::Function const& _fun)
{
- arguments = _fun.arguments;
- returns = _fun.returns;
+ /// 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
- ));
+ m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
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
- ));
+ m_errorReporter.typeError(
+ _funCall.functionName.location,
+ "Expected " + boost::lexical_cast<string>(arguments) + " arguments but got " +
+ boost::lexical_cast<string>(_funCall.arguments.size()) + "."
+ );
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)))
+ if (!expectExpression(arg))
success = false;
- }
m_stackHeight += int(returns) - int(arguments);
m_info.stackHeightInfo[&_funCall] = m_stackHeight;
return success;
}
+bool AsmAnalyzer::operator()(Switch const& _switch)
+{
+ bool success = true;
+
+ if (!expectExpression(*_switch.expression))
+ success = false;
+
+ set<tuple<LiteralKind, string>> cases;
+ for (auto const& _case: _switch.cases)
+ {
+ if (_case.value)
+ {
+ int const initialStackHeight = m_stackHeight;
+ // We cannot use "expectExpression" here because *_case.value is not a
+ // Statement and would be converted to a Statement otherwise.
+ if (!(*this)(*_case.value))
+ success = false;
+ expectDeposit(1, initialStackHeight, _case.value->location);
+ m_stackHeight--;
+
+ /// Note: the parser ensures there is only one default case
+ auto val = make_tuple(_case.value->kind, _case.value->value);
+ if (!cases.insert(val).second)
+ {
+ m_errorReporter.declarationError(
+ _case.location,
+ "Duplicate case defined"
+ );
+ success = false;
+ }
+ }
+
+ if (!(*this)(_case.body))
+ success = false;
+ }
+
+ m_stackHeight--;
+ m_info.stackHeightInfo[&_switch] = m_stackHeight;
+
+ return success;
+}
+
+bool AsmAnalyzer::operator()(assembly::ForLoop const& _for)
+{
+ Scope* originalScope = m_currentScope;
+
+ bool success = true;
+ if (!(*this)(_for.pre))
+ success = false;
+ // The block was closed already, but we re-open it again and stuff the
+ // condition, the body and the post part inside.
+ m_stackHeight += scope(&_for.pre).numberOfVariables();
+ m_currentScope = &scope(&_for.pre);
+
+ if (!expectExpression(*_for.condition))
+ success = false;
+ m_stackHeight--;
+ if (!(*this)(_for.body))
+ success = false;
+ if (!(*this)(_for.post))
+ success = false;
+
+ m_stackHeight -= scope(&_for.pre).numberOfVariables();
+ m_info.stackHeightInfo[&_for] = m_stackHeight;
+ m_currentScope = originalScope;
+
+ return success;
+}
+
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
+ auto previousScope = m_currentScope;
m_currentScope = &scope(&_block);
- int const initialStackHeight = m_stackHeight - m_virtualVariablesInNextBlock;
- m_virtualVariablesInNextBlock = 0;
+ int const initialStackHeight = m_stackHeight;
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;
+ m_stackHeight -= scope(&_block).numberOfVariables();
int const stackDiff = m_stackHeight - initialStackHeight;
if (stackDiff != 0)
{
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
+ m_errorReporter.declarationError(
+ _block.location,
"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;
+ m_currentScope = previousScope;
+ return success;
+}
+
+bool AsmAnalyzer::expectExpression(Statement const& _statement)
+{
+ bool success = true;
+ int const initialHeight = m_stackHeight;
+ if (!boost::apply_visitor(*this, _statement))
+ success = false;
+ if (!expectDeposit(1, initialHeight, locationOf(_statement)))
+ success = false;
return success;
}
+bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location)
+{
+ if (m_stackHeight - _oldHeight != _deposit)
+ {
+ m_errorReporter.typeError(
+ _location,
+ "Expected expression to return one item to the stack, but did return " +
+ boost::lexical_cast<string>(m_stackHeight - _oldHeight) +
+ " items."
+ );
+ return false;
+ }
+ return true;
+}
+
bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize)
{
bool success = true;
- size_t numErrorsBefore = m_errors.size();
+ size_t numErrorsBefore = m_errorReporter.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
- ));
+ m_errorReporter.typeError(_variable.location, "Assignment requires variable.");
success = false;
}
- else if (!boost::get<Scope::Variable>(*var).active)
+ else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var)))
{
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Variable " + _variable.name + " used before it was declared.",
- _variable.location
- ));
+ m_errorReporter.declarationError(
+ _variable.location,
+ "Variable " + _variable.name + " used before it was declared."
+ );
success = false;
}
variableSize = 1;
}
else if (m_resolver)
- variableSize = m_resolver(_variable, IdentifierContext::LValue);
+ {
+ bool insideFunction = m_currentScope->insideFunction();
+ variableSize = m_resolver(_variable, julia::IdentifierContext::LValue, insideFunction);
+ }
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
- ));
+ if (numErrorsBefore == m_errorReporter.errors().size())
+ m_errorReporter.declarationError(_variable.location, "Variable not found or variable not lvalue.");
success = false;
}
if (_valueSize == size_t(-1))
@@ -358,43 +437,60 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t
if (_valueSize != variableSize && variableSize != size_t(-1))
{
- m_errors.push_back(make_shared<Error>(
- Error::Type::TypeError,
+ m_errorReporter.typeError(
+ _variable.location,
"Variable size (" +
to_string(variableSize) +
") and value size (" +
to_string(_valueSize) +
- ") do not match.",
- _variable.location
- ));
+ ") do not match."
+ );
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)
{
+ solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present.");
auto scopePtr = m_info.scopes.at(_block);
solAssert(scopePtr, "Scope requested but not present.");
return *scopePtr;
}
+void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
+{
+ if (!m_julia)
+ return;
+
+ if (!builtinTypes.count(type))
+ m_errorReporter.typeError(
+ _location,
+ "\"" + type + "\" is not a valid type (user defined types are not yet supported)."
+ );
+}
+
+void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location)
+{
+ static set<solidity::Instruction> futureInstructions{
+ solidity::Instruction::CREATE2,
+ solidity::Instruction::RETURNDATACOPY,
+ solidity::Instruction::RETURNDATASIZE,
+ solidity::Instruction::STATICCALL
+ };
+ if (futureInstructions.count(_instr))
+ m_errorReporter.warning(
+ _location,
+ "The \"" +
+ boost::to_lower_copy(instructionInfo(_instr).name)
+ + "\" instruction is only available after " +
+ "the Metropolis hard fork. Before that it acts as an invalid instruction."
+ );
+
+ if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI)
+ m_errorReporter.warning(
+ _location,
+ "Jump instructions are low-level EVM features that can lead to "
+ "incorrect stack access. Because of that they are discouraged. "
+ "Please consider using \"switch\" or \"for\" statements instead."
+ );
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 426ee0d2..9b2a8f9c 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -20,10 +20,14 @@
#pragma once
-#include <libsolidity/inlineasm/AsmStack.h>
-
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmScope.h>
+
+#include <libjulia/backends/evm/AbstractAssembly.h>
+
+#include <libsolidity/inlineasm/AsmDataForward.h>
+
#include <boost/variant.hpp>
#include <functional>
@@ -33,23 +37,10 @@ namespace dev
{
namespace solidity
{
+class ErrorReporter;
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;
-
struct AsmAnalysisInfo;
/**
@@ -60,11 +51,12 @@ struct AsmAnalysisInfo;
class AsmAnalyzer: public boost::static_visitor<bool>
{
public:
- AsmAnalyzer(
+ explicit AsmAnalyzer(
AsmAnalysisInfo& _analysisInfo,
- ErrorList& _errors,
- ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver()
- );
+ ErrorReporter& _errorReporter,
+ bool _julia = false,
+ julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
+ ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {}
bool analyze(assembly::Block const& _block);
@@ -73,29 +65,37 @@ public:
bool operator()(assembly::Identifier const&);
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label);
- bool operator()(assembly::Assignment const&);
- bool operator()(assembly::FunctionalAssignment const& _functionalAssignment);
+ bool operator()(assembly::StackAssignment const&);
+ bool operator()(assembly::Assignment const& _assignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
bool operator()(assembly::FunctionDefinition const& _functionDefinition);
bool operator()(assembly::FunctionCall const& _functionCall);
+ bool operator()(assembly::Switch const& _switch);
+ bool operator()(assembly::ForLoop const& _forLoop);
bool operator()(assembly::Block const& _block);
private:
+ /// Visits the statement and expects it to deposit one item onto the stack.
+ bool expectExpression(Statement const& _statement);
+ bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location);
+
/// 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);
+ void expectValidType(std::string const& type, SourceLocation const& _location);
+ void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location);
- /// 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;
+ julia::ExternalIdentifierAccess::Resolver m_resolver;
Scope* m_currentScope = nullptr;
+ /// Variables that are active at the current point in assembly (as opposed to
+ /// "part of the scope but not yet declared")
+ std::set<Scope::Variable const*> m_activeVariables;
AsmAnalysisInfo& m_info;
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
+ bool m_julia = false;
};
}
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libsolidity/inlineasm/AsmAnalysisInfo.h
index e21eb2c5..bd3b28c4 100644
--- a/libsolidity/inlineasm/AsmAnalysisInfo.h
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.h
@@ -20,10 +20,13 @@
#pragma once
+#include <libsolidity/inlineasm/AsmDataForward.h>
+
#include <boost/variant.hpp>
#include <map>
#include <memory>
+#include <vector>
namespace dev
{
@@ -32,28 +35,16 @@ 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;
+ /// Virtual blocks which will be used for scopes for function arguments and return values.
+ std::map<FunctionDefinition const*, std::shared_ptr<assembly::Block const>> virtualBlocks;
};
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 9ef3e6e7..74743737 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -32,6 +32,9 @@
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
+#include <libjulia/backends/evm/AbstractAssembly.h>
+#include <libjulia/backends/evm/EVMCodeTransform.h>
+
#include <libdevcore/CommonIO.h>
#include <boost/range/adaptor/reversed.hpp>
@@ -46,268 +49,101 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-struct GeneratorState
-{
- GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly):
- errors(_errors), info(_analysisInfo), assembly(_assembly) {}
-
- size_t newLabelId()
- {
- return assemblyTagToIdentifier(assembly.newTag());
- }
-
- size_t assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
- {
- u256 id = _tag.data();
- solAssert(id <= std::numeric_limits<size_t>::max(), "Tag id too large.");
- return size_t(id);
- }
-
- ErrorList& errors;
- AsmAnalysisInfo info;
- eth::Assembly& assembly;
-};
-
-class CodeTransform: public boost::static_visitor<>
+class EthAssemblyAdapter: public julia::AbstractAssembly
{
public:
- /// Create the code transformer which appends assembly to _state.assembly when called
- /// with parsed assembly data.
- /// @param _identifierAccess used to resolve identifiers external to the inline assembly
- explicit CodeTransform(
- GeneratorState& _state,
- assembly::Block const& _block,
- assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
- ): CodeTransform(_state, _block, _identifierAccess, _state.assembly.deposit())
+ EthAssemblyAdapter(eth::Assembly& _assembly):
+ m_assembly(_assembly)
{
}
-
-private:
- CodeTransform(
- GeneratorState& _state,
- assembly::Block const& _block,
- assembly::ExternalIdentifierAccess const& _identifierAccess,
- int _initialDeposit
- ):
- m_state(_state),
- m_scope(*m_state.info.scopes.at(&_block)),
- m_identifierAccess(_identifierAccess),
- m_initialDeposit(_initialDeposit)
+ virtual void setSourceLocation(SourceLocation const& _location) override
{
- 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);
-
- // pop variables
- for (auto const& identifier: m_scope.identifiers)
- if (identifier.second.type() == typeid(Scope::Variable))
- m_state.assembly.append(solidity::Instruction::POP);
-
- int deposit = m_state.assembly.deposit() - blockStartDeposit;
- solAssert(deposit == 0, "Invalid stack height at end of block.");
+ m_assembly.setSourceLocation(_location);
}
-
-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)
+ virtual int stackHeight() const override { return m_assembly.deposit(); }
+ virtual void appendInstruction(solidity::Instruction _instruction) override
{
- m_state.assembly.setSourceLocation(_literal.location);
- if (_literal.isNumber)
- m_state.assembly.append(u256(_literal.value));
- else
- {
- solAssert(_literal.value.size() <= 32, "");
- m_state.assembly.append(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft)));
- }
- checkStackHeight(&_literal);
+ m_assembly.append(_instruction);
}
- void operator()(assembly::Identifier const& _identifier)
+ virtual void appendConstant(u256 const& _constant) override
{
- m_state.assembly.setSourceLocation(_identifier.location);
- // First search internals, then externals.
- if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor(
- [=](Scope::Variable& _var)
- {
- if (int heightDiff = variableHeightDiff(_var, _identifier.location, false))
- m_state.assembly.append(solidity::dupInstruction(heightDiff));
- else
- // Store something to balance the stack
- m_state.assembly.append(u256(0));
- },
- [=](Scope::Label& _label)
- {
- assignLabelIdIfUnset(_label);
- m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
- },
- [=](Scope::Function&)
- {
- solAssert(false, "Function not removed during desugaring.");
- }
- )))
- {
- return;
- }
- solAssert(
- m_identifierAccess.generateCode,
- "Identifier not found and no external access available."
- );
- m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_state.assembly);
- checkStackHeight(&_identifier);
+ m_assembly.append(_constant);
}
- void operator()(FunctionalInstruction const& _instr)
+ /// Append a label.
+ virtual void appendLabel(LabelID _labelId) override
{
- for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it)
- {
- int height = m_state.assembly.deposit();
- boost::apply_visitor(*this, *it);
- expectDeposit(1, height);
- }
- (*this)(_instr.instruction);
- checkStackHeight(&_instr);
+ m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
}
- void operator()(assembly::FunctionCall const&)
+ /// Append a label reference.
+ virtual void appendLabelReference(LabelID _labelId) override
{
- solAssert(false, "Function call not removed during desugaring phase.");
+ m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
}
- void operator()(Label const& _label)
+ virtual size_t newLabelId() override
{
- m_state.assembly.setSourceLocation(_label.location);
- solAssert(m_scope.identifiers.count(_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);
+ return assemblyTagToIdentifier(m_assembly.newTag());
}
- void operator()(assembly::Assignment const& _assignment)
+ virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
{
- m_state.assembly.setSourceLocation(_assignment.location);
- generateAssignment(_assignment.variableName, _assignment.location);
- checkStackHeight(&_assignment);
+ m_assembly.appendLibraryAddress(_linkerSymbol);
}
- void operator()(FunctionalAssignment const& _assignment)
+ virtual void appendJump(int _stackDiffAfter) override
{
- int height = m_state.assembly.deposit();
- boost::apply_visitor(*this, *_assignment.value);
- expectDeposit(1, height);
- m_state.assembly.setSourceLocation(_assignment.location);
- generateAssignment(_assignment.variableName, _assignment.location);
- checkStackHeight(&_assignment);
+ appendInstruction(solidity::Instruction::JUMP);
+ m_assembly.adjustDeposit(_stackDiffAfter);
}
- void operator()(assembly::VariableDeclaration const& _varDecl)
+ virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override
{
- int height = m_state.assembly.deposit();
- boost::apply_visitor(*this, *_varDecl.value);
- expectDeposit(1, height);
- auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.name));
- var.stackHeight = height;
- var.active = true;
+ appendLabelReference(_labelId);
+ appendJump(_stackDiffAfter);
}
- void operator()(assembly::Block const& _block)
+ virtual void appendJumpToIf(LabelID _labelId) override
{
- CodeTransform(m_state, _block, m_identifierAccess, m_initialDeposit);
- checkStackHeight(&_block);
+ appendLabelReference(_labelId);
+ appendInstruction(solidity::Instruction::JUMPI);
}
- void operator()(assembly::FunctionDefinition const&)
+ virtual void appendBeginsub(LabelID, int) override
{
- solAssert(false, "Function definition not removed during desugaring phase.");
+ // TODO we could emulate that, though
+ solAssert(false, "BEGINSUB not implemented for EVM 1.0");
}
-
-private:
- void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
+ /// Call a subroutine.
+ virtual void appendJumpsub(LabelID, int, int) override
{
- 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
- {
- solAssert(
- m_identifierAccess.generateCode,
- "Identifier not found and no external access available."
- );
- m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_state.assembly);
- }
+ // TODO we could emulate that, though
+ solAssert(false, "JUMPSUB not implemented for EVM 1.0");
}
- /// Determines the stack height difference to the given variables. Automatically generates
- /// errors if it is not yet in scope or the height difference is too large. Returns 0 on
- /// errors and the (positive) stack height difference otherwise.
- int variableHeightDiff(Scope::Variable const& _var, SourceLocation const& _location, bool _forSwap)
+ /// Return from a subroutine.
+ virtual void appendReturnsub(int, int) override
{
- int heightDiff = m_state.assembly.deposit() - _var.stackHeight;
- if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16))
- {
- //@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;
+ // TODO we could emulate that, though
+ solAssert(false, "RETURNSUB not implemented for EVM 1.0");
}
- void expectDeposit(int _deposit, int _oldHeight)
+ virtual void appendAssemblySize() override
{
- solAssert(m_state.assembly.deposit() == _oldHeight + _deposit, "Invalid stack deposit.");
+ m_assembly.appendProgramSize();
}
- 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.
- void assignLabelIdIfUnset(Scope::Label& _label)
+private:
+ LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) const
{
- if (_label.id == Scope::Label::unassignedLabelId)
- _label.id = m_state.newLabelId();
- else if (_label.id == Scope::Label::errorLabelId)
- _label.id = size_t(m_state.assembly.errorTag().data());
+ u256 id = _tag.data();
+ solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
+ return LabelID(id);
}
-
- GeneratorState& m_state;
- Scope& m_scope;
- ExternalIdentifierAccess m_identifierAccess;
- int const m_initialDeposit;
+ eth::Assembly& m_assembly;
};
-eth::Assembly assembly::CodeGenerator::assemble(
- Block const& _parsedData,
- AsmAnalysisInfo& _analysisInfo,
- ExternalIdentifierAccess const& _identifierAccess
-)
-{
- eth::Assembly assembly;
- GeneratorState state(m_errors, _analysisInfo, assembly);
- CodeTransform(state, _parsedData, _identifierAccess);
- return assembly;
-}
-
void assembly::CodeGenerator::assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess
+ julia::ExternalIdentifierAccess const& _identifierAccess
)
{
- GeneratorState state(m_errors, _analysisInfo, _assembly);
- CodeTransform(state, _parsedData, _identifierAccess);
+ EthAssemblyAdapter assemblyAdapter(_assembly);
+ julia::CodeTransform(assemblyAdapter, _analysisInfo, false, false, _identifierAccess)(_parsedData);
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index e830e047..2a36a590 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -23,7 +23,6 @@
#pragma once
#include <libsolidity/inlineasm/AsmAnalysis.h>
-#include <libsolidity/interface/Exceptions.h>
#include <functional>
@@ -42,24 +41,13 @@ struct Block;
class CodeGenerator
{
public:
- CodeGenerator(ErrorList& _errors):
- m_errors(_errors) {}
- /// Performs code generation and @returns the result.
- eth::Assembly assemble(
- Block const& _parsedData,
- AsmAnalysisInfo& _analysisInfo,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
- );
/// Performs code generation and appends generated to to _assembly.
- void assemble(
+ static void assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
);
-
-private:
- ErrorList& m_errors;
};
}
diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h
index d61b5803..db5840bc 100644
--- a/libsolidity/inlineasm/AsmData.h
+++ b/libsolidity/inlineasm/AsmData.h
@@ -22,10 +22,13 @@
#pragma once
-#include <boost/variant.hpp>
+#include <libsolidity/inlineasm/AsmDataForward.h>
+
#include <libevmasm/Instruction.h>
#include <libevmasm/SourceLocation.h>
+#include <boost/variant.hpp>
+
namespace dev
{
namespace solidity
@@ -33,37 +36,39 @@ namespace solidity
namespace assembly
{
-/// What follows are the AST nodes for assembly.
+using Type = std::string;
+
+struct TypedName { SourceLocation location; std::string name; Type type; };
+using TypedNameList = std::vector<TypedName>;
/// Direct EVM instruction (except PUSHi and JUMPDEST)
struct Instruction { SourceLocation location; solidity::Instruction instruction; };
/// Literal number or string (up to 32 bytes)
-struct Literal { SourceLocation location; bool isNumber; std::string value; };
+enum class LiteralKind { Number, Boolean, String };
+struct Literal { SourceLocation location; LiteralKind kind; std::string value; Type type; };
/// External / internal identifier or label reference
struct Identifier { SourceLocation location; std::string name; };
-struct FunctionalInstruction;
/// Jump label ("name:")
struct Label { SourceLocation location; std::string name; };
-/// Assignemnt (":= x", moves stack top into x, potentially multiple slots)
-struct Assignment { SourceLocation location; Identifier variableName; };
-struct FunctionalAssignment;
-struct VariableDeclaration;
-struct FunctionDefinition;
-struct FunctionCall;
-struct Block;
-using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
-/// Functional assignment ("x := mload(20)", expects push-1-expression on the right hand
+/// Assignment from stack (":= x", moves stack top into x, potentially multiple slots)
+struct StackAssignment { SourceLocation location; Identifier variableName; };
+/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand
/// side and requires x to occupy exactly one stack slot.
-struct FunctionalAssignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; };
-/// Functional instruction, e.g. "mul(mload(20), add(2, x))"
+struct Assignment { SourceLocation location; Identifier variableName; std::shared_ptr<Statement> value; };
+/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
-/// Block-scope variable declaration ("let x := mload(20)"), non-hoisted
-struct VariableDeclaration { SourceLocation location; std::string name; std::shared_ptr<Statement> value; };
+/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
+struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; };
/// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
-struct FunctionDefinition { SourceLocation location; std::string name; std::vector<std::string> arguments; std::vector<std::string> returns; Block body; };
+struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; };
+/// Switch case or default case
+struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; };
+/// Switch statement
+struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; };
+struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Statement> condition; Block post; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation>
{
diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h
new file mode 100644
index 00000000..4ead7ff5
--- /dev/null
+++ b/libsolidity/inlineasm/AsmDataForward.h
@@ -0,0 +1,52 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2016
+ * Forward declaration of classes for inline assembly / JULIA AST
+ */
+
+#pragma once
+
+#include <boost/variant.hpp>
+
+namespace dev
+{
+namespace solidity
+{
+namespace assembly
+{
+
+struct Instruction;
+struct Literal;
+struct Label;
+struct StackAssignment;
+struct Identifier;
+struct Assignment;
+struct VariableDeclaration;
+struct FunctionalInstruction;
+struct FunctionDefinition;
+struct FunctionCall;
+struct Switch;
+struct ForLoop;
+struct Block;
+
+using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, ForLoop, Block>;
+
+}
+}
+}
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index d7f78958..d282a30d 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -21,10 +21,10 @@
*/
#include <libsolidity/inlineasm/AsmParser.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <ctype.h>
#include <algorithm>
-#include <libsolidity/parsing/Scanner.h>
-#include <libsolidity/interface/Exceptions.h>
using namespace std;
using namespace dev;
@@ -40,7 +40,7 @@ shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scann
}
catch (FatalError const&)
{
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
}
return nullptr;
@@ -50,16 +50,16 @@ assembly::Block Parser::parseBlock()
{
assembly::Block block = createWithLocation<Block>();
expectToken(Token::LBrace);
- while (m_scanner->currentToken() != Token::RBrace)
+ while (currentToken() != Token::RBrace)
block.statements.emplace_back(parseStatement());
block.location.end = endPosition();
- m_scanner->next();
+ advance();
return block;
}
assembly::Statement Parser::parseStatement()
{
- switch (m_scanner->currentToken())
+ switch (currentToken())
{
case Token::Let:
return parseVariableDeclaration();
@@ -67,23 +67,43 @@ assembly::Statement Parser::parseStatement()
return parseFunctionDefinition();
case Token::LBrace:
return parseBlock();
+ case Token::Switch:
+ {
+ assembly::Switch _switch = createWithLocation<assembly::Switch>();
+ m_scanner->next();
+ _switch.expression = make_shared<Statement>(parseExpression());
+ if (_switch.expression->type() == typeid(assembly::Instruction))
+ fatalParserError("Instructions are not supported as expressions for switch.");
+ while (m_scanner->currentToken() == Token::Case)
+ _switch.cases.emplace_back(parseCase());
+ if (m_scanner->currentToken() == Token::Default)
+ _switch.cases.emplace_back(parseCase());
+ if (m_scanner->currentToken() == Token::Default)
+ fatalParserError("Only one default case allowed.");
+ else if (m_scanner->currentToken() == Token::Case)
+ fatalParserError("Case not allowed after default case.");
+ if (_switch.cases.size() == 0)
+ fatalParserError("Switch statement without any cases.");
+ _switch.location.end = _switch.cases.back().body.location.end;
+ return _switch;
+ }
+ case Token::For:
+ return parseForLoop();
case Token::Assign:
{
if (m_julia)
break;
- assembly::Assignment assignment = createWithLocation<assembly::Assignment>();
- m_scanner->next();
+ assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>();
+ advance();
expectToken(Token::Colon);
assignment.variableName.location = location();
- assignment.variableName.name = m_scanner->currentLiteral();
+ assignment.variableName.name = currentLiteral();
if (!m_julia && instructions().count(assignment.variableName.name))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
return assignment;
}
- case Token::Return: // opcode
- case Token::Byte: // opcode
default:
break;
}
@@ -91,50 +111,100 @@ assembly::Statement Parser::parseStatement()
// Simple instruction (might turn into functional),
// literal,
// identifier (might turn into label or functional assignment)
- Statement statement(parseElementaryOperation());
- switch (m_scanner->currentToken())
+ Statement statement(parseElementaryOperation(false));
+ switch (currentToken())
{
case Token::LParen:
- return parseFunctionalInstruction(std::move(statement));
+ return parseCall(std::move(statement));
case Token::Colon:
{
if (statement.type() != typeid(assembly::Identifier))
fatalParserError("Label name / variable name must precede \":\".");
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
- m_scanner->next();
+ advance();
// identifier:=: should be parsed as identifier: =: (i.e. a label),
// while identifier:= (being followed by a non-colon) as identifier := (assignment).
- if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon)
+ if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
{
- // functional assignment
- FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
+ assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location);
if (!m_julia && instructions().count(identifier.name))
fatalParserError("Cannot use instruction names for identifier names.");
- m_scanner->next();
- funAss.variableName = identifier;
- funAss.value.reset(new Statement(parseExpression()));
- funAss.location.end = locationOf(*funAss.value).end;
- return funAss;
+ advance();
+ assignment.variableName = identifier;
+ assignment.value.reset(new Statement(parseExpression()));
+ assignment.location.end = locationOf(*assignment.value).end;
+ return assignment;
}
else
{
// label
+ if (m_julia)
+ fatalParserError("Labels are not supported.");
Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
return label;
}
}
default:
+ if (m_julia)
+ fatalParserError("Call or assignment expected.");
break;
}
return statement;
}
+assembly::Case Parser::parseCase()
+{
+ assembly::Case _case = createWithLocation<assembly::Case>();
+ if (m_scanner->currentToken() == Token::Default)
+ m_scanner->next();
+ else if (m_scanner->currentToken() == Token::Case)
+ {
+ m_scanner->next();
+ assembly::Statement statement = parseElementaryOperation();
+ if (statement.type() != typeid(assembly::Literal))
+ fatalParserError("Literal expected.");
+ _case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement)));
+ }
+ else
+ fatalParserError("Case or default case expected.");
+ _case.body = parseBlock();
+ _case.location.end = _case.body.location.end;
+ return _case;
+}
+
+assembly::ForLoop Parser::parseForLoop()
+{
+ ForLoop forLoop = createWithLocation<ForLoop>();
+ expectToken(Token::For);
+ forLoop.pre = parseBlock();
+ forLoop.condition = make_shared<Statement>(parseExpression());
+ if (forLoop.condition->type() == typeid(assembly::Instruction))
+ fatalParserError("Instructions are not supported as conditions for the for statement.");
+ forLoop.post = parseBlock();
+ forLoop.body = parseBlock();
+ forLoop.location.end = forLoop.body.location.end;
+ return forLoop;
+}
+
assembly::Statement Parser::parseExpression()
{
Statement operation = parseElementaryOperation(true);
- if (m_scanner->currentToken() == Token::LParen)
- return parseFunctionalInstruction(std::move(operation));
+ if (operation.type() == typeid(Instruction))
+ {
+ Instruction const& instr = boost::get<Instruction>(operation);
+ int args = instructionInfo(instr.instruction).args;
+ if (args > 0 && currentToken() != Token::LParen)
+ fatalParserError(string(
+ "Expected token \"(\" (\"" +
+ instructionNames().at(instr.instruction) +
+ "\" expects " +
+ boost::lexical_cast<string>(args) +
+ " arguments)"
+ ));
+ }
+ if (currentToken() == Token::LParen)
+ return parseCall(std::move(operation));
else
return operation;
}
@@ -159,14 +229,30 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions()
// add alias for suicide
s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT;
+ // add alis for sha3
+ s_instructions["sha3"] = solidity::Instruction::KECCAK256;
}
return s_instructions;
}
+std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
+{
+ static map<dev::solidity::Instruction, string> s_instructionNames;
+ if (s_instructionNames.empty())
+ {
+ for (auto const& instr: instructions())
+ s_instructionNames[instr.second] = instr.first;
+ // set the ambiguous instructions to a clear default
+ s_instructionNames[solidity::Instruction::SELFDESTRUCT] = "selfdestruct";
+ s_instructionNames[solidity::Instruction::KECCAK256] = "keccak256";
+ }
+ return s_instructionNames;
+}
+
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
Statement ret;
- switch (m_scanner->currentToken())
+ switch (currentToken())
{
case Token::Identifier:
case Token::Return:
@@ -174,14 +260,14 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
case Token::Address:
{
string literal;
- if (m_scanner->currentToken() == Token::Return)
+ if (currentToken() == Token::Return)
literal = "return";
- else if (m_scanner->currentToken() == Token::Byte)
+ else if (currentToken() == Token::Byte)
literal = "byte";
- else if (m_scanner->currentToken() == Token::Address)
+ else if (currentToken() == Token::Address)
literal = "address";
else
- literal = m_scanner->currentLiteral();
+ literal = currentLiteral();
// first search the set of instructions.
if (!m_julia && instructions().count(literal))
{
@@ -190,28 +276,62 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
InstructionInfo info = dev::solidity::instructionInfo(instr);
if (info.ret != 1)
- fatalParserError("Instruction " + info.name + " not allowed in this context.");
+ fatalParserError("Instruction \"" + literal + "\" not allowed in this context.");
}
ret = Instruction{location(), instr};
}
else
ret = Identifier{location(), literal};
+ advance();
break;
}
case Token::StringLiteral:
case Token::Number:
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
{
- ret = Literal{
+ LiteralKind kind = LiteralKind::Number;
+ switch (currentToken())
+ {
+ case Token::StringLiteral:
+ kind = LiteralKind::String;
+ break;
+ case Token::Number:
+ kind = LiteralKind::Number;
+ break;
+ case Token::TrueLiteral:
+ case Token::FalseLiteral:
+ kind = LiteralKind::Boolean;
+ break;
+ default:
+ break;
+ }
+
+ Literal literal{
location(),
- m_scanner->currentToken() == Token::Number,
- m_scanner->currentLiteral()
+ kind,
+ currentLiteral(),
+ ""
};
+ advance();
+ if (m_julia)
+ {
+ expectToken(Token::Colon);
+ literal.location.end = endPosition();
+ literal.type = expectAsmIdentifier();
+ }
+ else if (kind == LiteralKind::Boolean)
+ fatalParserError("True and false are not valid literals.");
+ ret = std::move(literal);
break;
}
default:
- fatalParserError("Expected elementary inline assembly operation.");
+ fatalParserError(
+ m_julia ?
+ "Literal or identifier expected." :
+ "Literal, identifier or instruction expected."
+ );
}
- m_scanner->next();
return ret;
}
@@ -219,7 +339,14 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let);
- varDecl.name = expectAsmIdentifier();
+ while (true)
+ {
+ varDecl.variables.emplace_back(parseTypedName());
+ if (currentToken() == Token::Comma)
+ expectToken(Token::Comma);
+ else
+ break;
+ }
expectToken(Token::Colon);
expectToken(Token::Assign);
varDecl.value.reset(new Statement(parseExpression()));
@@ -233,22 +360,22 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::Function);
funDef.name = expectAsmIdentifier();
expectToken(Token::LParen);
- while (m_scanner->currentToken() != Token::RParen)
+ while (currentToken() != Token::RParen)
{
- funDef.arguments.push_back(expectAsmIdentifier());
- if (m_scanner->currentToken() == Token::RParen)
+ funDef.arguments.emplace_back(parseTypedName());
+ if (currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
expectToken(Token::RParen);
- if (m_scanner->currentToken() == Token::Sub)
+ if (currentToken() == Token::Sub)
{
expectToken(Token::Sub);
expectToken(Token::GreaterThan);
while (true)
{
- funDef.returns.push_back(expectAsmIdentifier());
- if (m_scanner->currentToken() == Token::LBrace)
+ funDef.returns.emplace_back(parseTypedName());
+ if (currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
}
@@ -258,7 +385,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
return funDef;
}
-assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _instruction)
+assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
{
if (_instruction.type() == typeid(Instruction))
{
@@ -276,26 +403,40 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
unsigned args = unsigned(instrInfo.args);
for (unsigned i = 0; i < args; ++i)
{
+ /// check for premature closing parentheses
+ if (currentToken() == Token::RParen)
+ fatalParserError(string(
+ "Expected expression (\"" +
+ instructionNames().at(instr) +
+ "\" expects " +
+ boost::lexical_cast<string>(args) +
+ " arguments)"
+ ));
+
ret.arguments.emplace_back(parseExpression());
if (i != args - 1)
{
- if (m_scanner->currentToken() != Token::Comma)
+ if (currentToken() != Token::Comma)
fatalParserError(string(
- "Expected comma (" +
- instrInfo.name +
- " expects " +
+ "Expected comma (\"" +
+ instructionNames().at(instr) +
+ "\" expects " +
boost::lexical_cast<string>(args) +
" arguments)"
));
else
- m_scanner->next();
+ advance();
}
}
ret.location.end = endPosition();
- if (m_scanner->currentToken() == Token::Comma)
- fatalParserError(
- string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
- );
+ if (currentToken() == Token::Comma)
+ fatalParserError(string(
+ "Expected ')' (\"" +
+ instructionNames().at(instr) +
+ "\" expects " +
+ boost::lexical_cast<string>(args) +
+ " arguments)"
+ ));
expectToken(Token::RParen);
return ret;
}
@@ -305,10 +446,10 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
ret.functionName = std::move(boost::get<Identifier>(_instruction));
ret.location = ret.functionName.location;
expectToken(Token::LParen);
- while (m_scanner->currentToken() != Token::RParen)
+ while (currentToken() != Token::RParen)
{
ret.arguments.emplace_back(parseExpression());
- if (m_scanner->currentToken() == Token::RParen)
+ if (currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
}
@@ -317,15 +458,40 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
return ret;
}
else
- fatalParserError("Assembly instruction or function name required in front of \"(\")");
+ fatalParserError(
+ m_julia ?
+ "Function name expected." :
+ "Assembly instruction or function name required in front of \"(\")"
+ );
return {};
}
+TypedName Parser::parseTypedName()
+{
+ TypedName typedName = createWithLocation<TypedName>();
+ typedName.name = expectAsmIdentifier();
+ if (m_julia)
+ {
+ expectToken(Token::Colon);
+ typedName.location.end = endPosition();
+ typedName.type = expectAsmIdentifier();
+ }
+ return typedName;
+}
+
string Parser::expectAsmIdentifier()
{
- string name = m_scanner->currentLiteral();
- if (!m_julia && instructions().count(name))
+ string name = currentLiteral();
+ if (m_julia)
+ {
+ if (currentToken() == Token::Bool)
+ {
+ advance();
+ return name;
+ }
+ }
+ else if (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 c55fd2ac..45708afd 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -37,7 +37,7 @@ namespace assembly
class Parser: public ParserBase
{
public:
- explicit Parser(ErrorList& _errors, bool _julia = false): ParserBase(_errors), m_julia(_julia) {}
+ explicit Parser(ErrorReporter& _errorReporter, bool _julia = false): ParserBase(_errorReporter), m_julia(_julia) {}
/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @returns an empty shared pointer on error.
@@ -62,13 +62,17 @@ protected:
Block parseBlock();
Statement parseStatement();
+ Case parseCase();
+ ForLoop parseForLoop();
/// Parses a functional expression that has to push exactly one stack element
Statement parseExpression();
- std::map<std::string, dev::solidity::Instruction> const& instructions();
+ static std::map<std::string, dev::solidity::Instruction> const& instructions();
+ static std::map<dev::solidity::Instruction, std::string> const& instructionNames();
Statement parseElementaryOperation(bool _onlySinglePusher = false);
VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition();
- Statement parseFunctionalInstruction(Statement&& _instruction);
+ Statement parseCall(Statement&& _instruction);
+ TypedName parseTypedName();
std::string expectAsmIdentifier();
private:
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 252e91f9..062ff453 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -21,8 +21,8 @@
*/
#include <libsolidity/inlineasm/AsmPrinter.h>
-
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -40,13 +40,22 @@ using namespace dev::solidity::assembly;
string AsmPrinter::operator()(assembly::Instruction const& _instruction)
{
+ solAssert(!m_julia, "");
return boost::to_lower_copy(instructionInfo(_instruction.instruction).name);
}
string AsmPrinter::operator()(assembly::Literal const& _literal)
{
- if (_literal.isNumber)
- return _literal.value;
+ switch (_literal.kind)
+ {
+ case LiteralKind::Number:
+ return _literal.value + appendTypeName(_literal.type);
+ case LiteralKind::Boolean:
+ return ((_literal.value == "true") ? "true" : "false") + appendTypeName(_literal.type);
+ case LiteralKind::String:
+ break;
+ }
+
string out;
for (char c: _literal.value)
if (c == '\\')
@@ -73,7 +82,7 @@ string AsmPrinter::operator()(assembly::Literal const& _literal)
}
else
out += c;
- return "\"" + out + "\"";
+ return "\"" + out + "\"" + appendTypeName(_literal.type);
}
string AsmPrinter::operator()(assembly::Identifier const& _identifier)
@@ -83,6 +92,7 @@ string AsmPrinter::operator()(assembly::Identifier const& _identifier)
string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction)
{
+ solAssert(!m_julia, "");
return
(*this)(_functionalInstruction.instruction) +
"(" +
@@ -94,29 +104,56 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional
string AsmPrinter::operator()(assembly::Label const& _label)
{
+ solAssert(!m_julia, "");
return _label.name + ":";
}
-string AsmPrinter::operator()(assembly::Assignment const& _assignment)
+string AsmPrinter::operator()(assembly::StackAssignment const& _assignment)
{
+ solAssert(!m_julia, "");
return "=: " + (*this)(_assignment.variableName);
}
-string AsmPrinter::operator()(assembly::FunctionalAssignment const& _functionalAssignment)
+string AsmPrinter::operator()(assembly::Assignment const& _assignment)
{
- return (*this)(_functionalAssignment.variableName) + " := " + boost::apply_visitor(*this, *_functionalAssignment.value);
+ return (*this)(_assignment.variableName) + " := " + boost::apply_visitor(*this, *_assignment.value);
}
string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDeclaration)
{
- return "let " + _variableDeclaration.name + " := " + boost::apply_visitor(*this, *_variableDeclaration.value);
+ string out = "let ";
+ out += boost::algorithm::join(
+ _variableDeclaration.variables | boost::adaptors::transformed(
+ [this](TypedName variable) { return variable.name + appendTypeName(variable.type); }
+ ),
+ ", "
+ );
+ out += " := ";
+ out += boost::apply_visitor(*this, *_variableDeclaration.value);
+ return out;
}
string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition)
{
- string out = "function " + _functionDefinition.name + "(" + boost::algorithm::join(_functionDefinition.arguments, ", ") + ")";
+ string out = "function " + _functionDefinition.name + "(";
+ out += boost::algorithm::join(
+ _functionDefinition.arguments | boost::adaptors::transformed(
+ [this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
+ ),
+ ", "
+ );
+ out += ")";
if (!_functionDefinition.returns.empty())
- out += " -> " + boost::algorithm::join(_functionDefinition.returns, ", ");
+ {
+ out += " -> ";
+ out += boost::algorithm::join(
+ _functionDefinition.returns | boost::adaptors::transformed(
+ [this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
+ ),
+ ", "
+ );
+ }
+
return out + "\n" + (*this)(_functionDefinition.body);
}
@@ -130,6 +167,33 @@ string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall)
")";
}
+string AsmPrinter::operator()(Switch const& _switch)
+{
+ string out = "switch " + boost::apply_visitor(*this, *_switch.expression);
+ for (auto const& _case: _switch.cases)
+ {
+ if (!_case.value)
+ out += "\ndefault ";
+ else
+ out += "\ncase " + (*this)(*_case.value) + " ";
+ out += (*this)(_case.body);
+ }
+ return out;
+}
+
+string AsmPrinter::operator()(assembly::ForLoop const& _forLoop)
+{
+ string out = "for ";
+ out += (*this)(_forLoop.pre);
+ out += "\n";
+ out += boost::apply_visitor(*this, *_forLoop.condition);
+ out += "\n";
+ out += (*this)(_forLoop.post);
+ out += "\n";
+ out += (*this)(_forLoop.body);
+ return out;
+}
+
string AsmPrinter::operator()(Block const& _block)
{
if (_block.statements.empty())
@@ -141,3 +205,10 @@ string AsmPrinter::operator()(Block const& _block)
boost::replace_all(body, "\n", "\n ");
return "{\n " + body + "\n}";
}
+
+string AsmPrinter::appendTypeName(std::string const& _type)
+{
+ if (m_julia)
+ return ":" + _type;
+ return "";
+}
diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h
index a7a1de0a..f57dddc8 100644
--- a/libsolidity/inlineasm/AsmPrinter.h
+++ b/libsolidity/inlineasm/AsmPrinter.h
@@ -22,6 +22,8 @@
#pragma once
+#include <libsolidity/inlineasm/AsmDataForward.h>
+
#include <boost/variant.hpp>
namespace dev
@@ -30,32 +32,30 @@ namespace solidity
{
namespace assembly
{
-struct Instruction;
-struct Literal;
-struct Identifier;
-struct FunctionalInstruction;
-struct Label;
-struct Assignment;
-struct FunctionalAssignment;
-struct VariableDeclaration;
-struct FunctionDefinition;
-struct FunctionCall;
-struct Block;
class AsmPrinter: public boost::static_visitor<std::string>
{
public:
+ explicit AsmPrinter(bool _julia = false): m_julia(_julia) {}
+
std::string operator()(assembly::Instruction const& _instruction);
std::string operator()(assembly::Literal const& _literal);
std::string operator()(assembly::Identifier const& _identifier);
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
std::string operator()(assembly::Label const& _label);
+ std::string operator()(assembly::StackAssignment const& _assignment);
std::string operator()(assembly::Assignment const& _assignment);
- std::string operator()(assembly::FunctionalAssignment const& _functionalAssignment);
std::string operator()(assembly::VariableDeclaration const& _variableDeclaration);
std::string operator()(assembly::FunctionDefinition const& _functionDefinition);
std::string operator()(assembly::FunctionCall const& _functionCall);
+ std::string operator()(assembly::Switch const& _switch);
+ std::string operator()(assembly::ForLoop const& _forLoop);
std::string operator()(assembly::Block const& _block);
+
+private:
+ std::string appendTypeName(std::string const& _type);
+
+ bool m_julia = false;
};
}
diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp
index 609dca16..315d5953 100644
--- a/libsolidity/inlineasm/AsmScope.cpp
+++ b/libsolidity/inlineasm/AsmScope.cpp
@@ -32,19 +32,21 @@ bool Scope::registerLabel(string const& _name)
return true;
}
-bool Scope::registerVariable(string const& _name)
+bool Scope::registerVariable(string const& _name, JuliaType const& _type)
{
if (exists(_name))
return false;
- identifiers[_name] = Variable();
+ Variable variable;
+ variable.type = _type;
+ identifiers[_name] = variable;
return true;
}
-bool Scope::registerFunction(string const& _name, size_t _arguments, size_t _returns)
+bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns)
{
if (exists(_name))
return false;
- identifiers[_name] = Function(_arguments, _returns);
+ identifiers[_name] = Function{_arguments, _returns};
return true;
}
@@ -77,3 +79,20 @@ bool Scope::exists(string const& _name)
else
return false;
}
+
+size_t Scope::numberOfVariables() const
+{
+ size_t count = 0;
+ for (auto const& identifier: identifiers)
+ if (identifier.second.type() == typeid(Scope::Variable))
+ count++;
+ return count;
+}
+
+bool Scope::insideFunction() const
+{
+ for (Scope const* s = this; s; s = s->superScope)
+ if (s->functionScope)
+ return true;
+ return false;
+}
diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h
index 37e0f0b8..cc240565 100644
--- a/libsolidity/inlineasm/AsmScope.h
+++ b/libsolidity/inlineasm/AsmScope.h
@@ -23,6 +23,7 @@
#include <libsolidity/interface/Exceptions.h>
#include <boost/variant.hpp>
+#include <boost/optional.hpp>
#include <functional>
#include <memory>
@@ -61,36 +62,28 @@ struct GenericVisitor<>: public boost::static_visitor<> {
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;
- };
+ using JuliaType = std::string;
+ using LabelID = size_t;
+ struct Variable { JuliaType type; };
+ struct Label { };
struct Function
{
- Function(size_t _arguments, size_t _returns): arguments(_arguments), returns(_returns) {}
- size_t arguments = 0;
- size_t returns = 0;
+ std::vector<JuliaType> arguments;
+ std::vector<JuliaType> returns;
};
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 registerVariable(std::string const& _name, JuliaType const& _type);
bool registerLabel(std::string const& _name);
- bool registerFunction(std::string const& _name, size_t _arguments, size_t _returns);
+ bool registerFunction(
+ std::string const& _name,
+ std::vector<JuliaType> const& _arguments,
+ std::vector<JuliaType> const& _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
@@ -116,6 +109,11 @@ struct Scope
/// across function and assembly boundaries).
bool exists(std::string const& _name);
+ /// @returns the number of variables directly registered inside the scope.
+ size_t numberOfVariables() const;
+ /// @returns true if this scope is inside a function.
+ bool insideFunction() const;
+
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.
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index de6fbdaa..5b3174b8 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -22,9 +22,10 @@
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmScope.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
#include <boost/range/adaptor/reversed.hpp>
@@ -36,13 +37,9 @@ 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)
+ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter):
+ m_info(_info), m_errorReporter(_errorReporter)
{
- // 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);
}
@@ -51,11 +48,10 @@ 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
- ));
+ m_errorReporter.declarationError(
+ _item.location,
+ "Label name " + _item.name + " already taken in this scope."
+ );
return false;
}
return true;
@@ -63,32 +59,75 @@ bool ScopeFiller::operator()(Label const& _item)
bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl)
{
- return registerVariable(_varDecl.name, _varDecl.location, *m_currentScope);
+ for (auto const& variable: _varDecl.variables)
+ if (!registerVariable(variable, _varDecl.location, *m_currentScope))
+ return false;
+ return true;
}
bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
{
bool success = true;
- if (!m_currentScope->registerFunction(_funDef.name, _funDef.arguments.size(), _funDef.returns.size()))
+ vector<Scope::JuliaType> arguments;
+ for (auto const& _argument: _funDef.arguments)
+ arguments.push_back(_argument.type);
+ vector<Scope::JuliaType> returns;
+ for (auto const& _return: _funDef.returns)
+ returns.push_back(_return.type);
+ if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
{
//@TODO secondary location
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Function name " + _funDef.name + " already taken in this scope.",
- _funDef.location
- ));
+ m_errorReporter.declarationError(
+ _funDef.location,
+ "Function name " + _funDef.name + " already taken in this scope."
+ );
success = false;
}
- Scope& body = scope(&_funDef.body);
- body.superScope = m_currentScope;
- body.functionScope = true;
+
+ auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>();
+ Scope& varScope = scope(virtualBlock.get());
+ varScope.superScope = m_currentScope;
+ m_currentScope = &varScope;
+ varScope.functionScope = true;
for (auto const& var: _funDef.arguments + _funDef.returns)
- if (!registerVariable(var, _funDef.location, body))
+ if (!registerVariable(var, _funDef.location, varScope))
success = false;
if (!(*this)(_funDef.body))
success = false;
+ solAssert(m_currentScope == &varScope, "");
+ m_currentScope = m_currentScope->superScope;
+
+ return success;
+}
+
+bool ScopeFiller::operator()(Switch const& _switch)
+{
+ bool success = true;
+ for (auto const& _case: _switch.cases)
+ if (!(*this)(_case.body))
+ success = false;
+ return success;
+}
+
+bool ScopeFiller::operator()(ForLoop const& _forLoop)
+{
+ Scope* originalScope = m_currentScope;
+
+ bool success = true;
+ if (!(*this)(_forLoop.pre))
+ success = false;
+ m_currentScope = &scope(&_forLoop.pre);
+ if (!boost::apply_visitor(*this, *_forLoop.condition))
+ success = false;
+ if (!(*this)(_forLoop.body))
+ success = false;
+ if (!(*this)(_forLoop.post))
+ success = false;
+
+ m_currentScope = originalScope;
+
return success;
}
@@ -106,16 +145,15 @@ bool ScopeFiller::operator()(Block const& _block)
return success;
}
-bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _location, Scope& _scope)
+bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope)
{
- if (!_scope.registerVariable(_name))
+ if (!_scope.registerVariable(_name.name, _name.type))
{
//@TODO secondary location
- m_errors.push_back(make_shared<Error>(
- Error::Type::DeclarationError,
- "Variable name " + _name + " already taken in this scope.",
- _location
- ));
+ m_errorReporter.declarationError(
+ _location,
+ "Variable name " + _name.name + " already taken in this scope."
+ );
return false;
}
return true;
@@ -123,7 +161,7 @@ bool ScopeFiller::registerVariable(string const& _name, SourceLocation const& _l
Scope& ScopeFiller::scope(Block const* _block)
{
- auto& scope = m_scopes[_block];
+ auto& scope = m_info.scopes[_block];
if (!scope)
scope = make_shared<Scope>();
return *scope;
diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h
index bb62948b..80c03d2c 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.h
+++ b/libsolidity/inlineasm/AsmScopeFiller.h
@@ -20,7 +20,7 @@
#pragma once
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmDataForward.h>
#include <boost/variant.hpp>
@@ -29,24 +29,16 @@
namespace dev
{
+struct SourceLocation;
namespace solidity
{
+class ErrorReporter;
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 TypedName;
struct Scope;
+struct AsmAnalysisInfo;
/**
* Fills scopes with identifiers and checks for name clashes.
@@ -55,24 +47,25 @@ struct Scope;
class ScopeFiller: public boost::static_visitor<bool>
{
public:
- using Scopes = std::map<assembly::Block const*, std::shared_ptr<Scope>>;
- ScopeFiller(Scopes& _scopes, ErrorList& _errors);
+ ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter);
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::StackAssignment const&) { return true; }
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::Switch const& _switch);
+ bool operator()(assembly::ForLoop const& _forLoop);
bool operator()(assembly::Block const& _block);
private:
bool registerVariable(
- std::string const& _name,
+ TypedName const& _name,
SourceLocation const& _location,
Scope& _scope
);
@@ -80,8 +73,8 @@ private:
Scope& scope(assembly::Block const* _block);
Scope* m_currentScope = nullptr;
- Scopes& m_scopes;
- ErrorList& m_errors;
+ AsmAnalysisInfo& m_info;
+ ErrorReporter& m_errorReporter;
};
}
diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp
deleted file mode 100644
index c2a7d8ea..00000000
--- a/libsolidity/inlineasm/AsmStack.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- 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/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2016
- * Full-stack Solidity inline assember.
- */
-
-#include <libsolidity/inlineasm/AsmStack.h>
-
-#include <libsolidity/inlineasm/AsmParser.h>
-#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>
-
-#include <libevmasm/Assembly.h>
-#include <libevmasm/SourceLocation.h>
-
-#include <memory>
-
-using namespace std;
-using namespace dev;
-using namespace dev::solidity;
-using namespace dev::solidity::assembly;
-
-bool InlineAssemblyStack::parse(
- shared_ptr<Scanner> const& _scanner,
- ExternalIdentifierAccess::Resolver const& _resolver
-)
-{
- m_parserResult = make_shared<Block>();
- Parser parser(m_errors);
- auto result = parser.parse(_scanner);
- if (!result)
- return false;
-
- *m_parserResult = std::move(*result);
- AsmAnalysisInfo analysisInfo;
- return (AsmAnalyzer(analysisInfo, m_errors, _resolver)).analyze(*m_parserResult);
-}
-
-string InlineAssemblyStack::toString()
-{
- return AsmPrinter()(*m_parserResult);
-}
-
-eth::Assembly InlineAssemblyStack::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,
- ExternalIdentifierAccess const& _identifierAccess
-)
-{
- ErrorList errors;
- auto scanner = make_shared<Scanner>(CharStream(_input), "--CODEGEN--");
- auto parserResult = Parser(errors).parse(scanner);
- if (!errors.empty())
- return false;
- solAssert(parserResult, "");
-
- 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.
- return errors.empty();
-}
-
diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h
deleted file mode 100644
index 77a7e02a..00000000
--- a/libsolidity/inlineasm/AsmStack.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- 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/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2016
- * Full-stack Solidity inline assember.
- */
-
-#pragma once
-
-#include <libsolidity/interface/Exceptions.h>
-
-#include <string>
-#include <functional>
-
-namespace dev
-{
-namespace eth
-{
-class Assembly;
-}
-namespace solidity
-{
-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,
- 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();
-
- eth::Assembly assemble();
-
- /// Parse and assemble a string in one run - for use in Solidity code generation itself.
- bool parseAndAssemble(
- std::string const& _input,
- eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
- );
-
- ErrorList const& errors() const { return m_errors; }
-
-private:
- std::shared_ptr<Block> m_parserResult;
- ErrorList m_errors;
-};
-
-}
-}
-}