aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/CMakeLists.txt4
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp6
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp22
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h1
-rw-r--r--libsolidity/analysis/TypeChecker.cpp11
-rw-r--r--libsolidity/ast/AST.h2
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp6
-rw-r--r--libsolidity/codegen/CompilerContext.cpp10
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp8
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp102
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h21
-rw-r--r--libsolidity/inlineasm/AsmAnalysisInfo.h7
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp252
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h4
-rw-r--r--libsolidity/inlineasm/AsmData.h36
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp145
-rw-r--r--libsolidity/inlineasm/AsmParser.h3
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp31
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h6
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp14
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h8
-rw-r--r--libsolidity/inlineasm/AsmStack.cpp8
-rw-r--r--libsolidity/inlineasm/AsmStack.h23
-rw-r--r--libsolidity/interface/AssemblyStack.cpp85
-rw-r--r--libsolidity/interface/AssemblyStack.h85
-rw-r--r--libsolidity/interface/CompilerStack.cpp4
-rw-r--r--libsolidity/parsing/Parser.cpp44
-rw-r--r--libsolidity/parsing/Parser.h5
-rw-r--r--libsolidity/parsing/ParserBase.cpp56
-rw-r--r--libsolidity/parsing/ParserBase.h10
30 files changed, 567 insertions, 452 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index bcc47e5a..2342f0f9 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -7,10 +7,12 @@ aux_source_directory(formal SRC_LIST)
aux_source_directory(interface SRC_LIST)
aux_source_directory(parsing SRC_LIST)
aux_source_directory(inlineasm SRC_LIST)
+# Until we have a clear separation, libjulia has to be included here
+aux_source_directory(../libjulia SRC_LIST)
set(EXECUTABLE solidity)
-file(GLOB HEADERS "*/*.h")
+file(GLOB HEADERS "*/*.h" "../libjulia/backends/evm/*")
include_directories(BEFORE ..)
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 9433976a..f5fffb57 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -166,8 +166,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// The only purpose of this step is to fill the inline assembly annotation with
// external references.
ErrorList errorsIgnored;
- assembly::ExternalIdentifierAccess::Resolver resolver =
- [&](assembly::Identifier const& _identifier, assembly::IdentifierContext) {
+ julia::ExternalIdentifierAccess::Resolver resolver =
+ [&](assembly::Identifier const& _identifier, julia::IdentifierContext) {
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name);
bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset");
@@ -194,7 +194,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer(analysisInfo, errorsIgnored, resolver).analyze(_inlineAssembly.operations());
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 369376fa..cfc863e0 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -21,14 +21,13 @@
*/
#include <libsolidity/analysis/StaticAnalyzer.h>
-#include <memory>
#include <libsolidity/ast/AST.h>
+#include <memory>
using namespace std;
using namespace dev;
using namespace dev::solidity;
-
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
@@ -128,3 +127,22 @@ void StaticAnalyzer::warning(SourceLocation const& _location, string const& _des
m_errors.push_back(err);
}
+
+bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly)
+{
+ if (!m_currentFunction)
+ return true;
+
+ for (auto const& ref: _inlineAssembly.annotation().externalReferences)
+ {
+ if (auto var = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
+ {
+ solAssert(!var->name().empty(), "");
+ if (var->isLocalVariable())
+ m_localVarUseCount[var] += 1;
+ }
+ }
+
+ return true;
+}
+
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index ab72e7d9..458bab2a 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -65,6 +65,7 @@ private:
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(Return const& _return) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
+ virtual bool visit(InlineAssembly const& _inlineAssembly) override;
ErrorList& m_errors;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 8161a3a1..528a23da 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -631,9 +631,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
// External references have already been resolved in a prior stage and stored in the annotation.
// We run the resolve step again regardless.
- assembly::ExternalIdentifierAccess::Resolver identifierAccess = [&](
+ julia::ExternalIdentifierAccess::Resolver identifierAccess = [&](
assembly::Identifier const& _identifier,
- assembly::IdentifierContext _context
+ julia::IdentifierContext _context
)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
@@ -650,7 +650,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return size_t(-1);
}
- else if (_context != assembly::IdentifierContext::RValue)
+ else if (_context != julia::IdentifierContext::RValue)
{
typeError(_identifier.location, "Storage variables cannot be assigned to.");
return size_t(-1);
@@ -677,13 +677,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return size_t(-1);
}
}
- else if (_context == assembly::IdentifierContext::LValue)
+ else if (_context == julia::IdentifierContext::LValue)
{
typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
return size_t(-1);
}
- if (_context == assembly::IdentifierContext::RValue)
+ if (_context == julia::IdentifierContext::RValue)
{
solAssert(!!declaration->type(), "Type of declaration required but not yet determined.");
if (dynamic_cast<FunctionDefinition const*>(declaration))
@@ -711,6 +711,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errors,
+ false,
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 71b81a69..ba1d0589 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -873,6 +873,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& parameterTypes() const { return m_parameterTypes->parameters(); }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameterTypes() const { return m_returnTypes->parameters(); }
+ ASTPointer<ParameterList> const& parameterTypeList() const { return m_parameterTypes; }
+ ASTPointer<ParameterList> const& returnParameterTypeList() const { return m_returnTypes; }
Declaration::Visibility visibility() const
{
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 40c552a3..cdf70cf9 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -178,7 +178,7 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Iden
tuple["declaration"] = idOrNull(_info.second.declaration);
tuple["isSlot"] = Json::Value(_info.second.isSlot);
tuple["isOffset"] = Json::Value(_info.second.isOffset);
- tuple["valueSize"] = _info.second.valueSize;
+ tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize));
return tuple;
}
@@ -420,8 +420,8 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
make_pair("payable", _node.isPayable()),
make_pair("visibility", visibility(_node.visibility())),
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
- make_pair("parameterTypes", toJson(_node.parameterTypes())),
- make_pair("returnParameterTypes", toJson(_node.returnParameterTypes())),
+ make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
+ make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
});
return false;
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 5c4f88c4..404a3af6 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -266,10 +266,10 @@ void CompilerContext::appendInlineAssembly(
int startStackHeight = stackHeight();
- assembly::ExternalIdentifierAccess identifierAccess;
+ julia::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&](
assembly::Identifier const& _identifier,
- assembly::IdentifierContext
+ julia::IdentifierContext
)
{
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
@@ -277,7 +277,7 @@ void CompilerContext::appendInlineAssembly(
};
identifierAccess.generateCode = [&](
assembly::Identifier const& _identifier,
- assembly::IdentifierContext _context,
+ julia::IdentifierContext _context,
julia::AbstractAssembly& _assembly
)
{
@@ -285,14 +285,14 @@ void CompilerContext::appendInlineAssembly(
solAssert(it != _localVariables.end(), "");
int stackDepth = _localVariables.end() - it;
int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth;
- if (_context == assembly::IdentifierContext::LValue)
+ if (_context == julia::IdentifierContext::LValue)
stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
);
- if (_context == assembly::IdentifierContext::RValue)
+ if (_context == julia::IdentifierContext::RValue)
_assembly.appendInstruction(dupInstruction(stackDiff));
else
{
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 41940e1a..e79bb6dc 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -522,21 +522,21 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
ErrorList errors;
assembly::CodeGenerator codeGen(errors);
unsigned startStackHeight = m_context.stackHeight();
- assembly::ExternalIdentifierAccess identifierAccess;
- identifierAccess.resolve = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext)
+ julia::ExternalIdentifierAccess identifierAccess;
+ identifierAccess.resolve = [&](assembly::Identifier const& _identifier, julia::IdentifierContext)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
return size_t(-1);
return ref->second.valueSize;
};
- identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, assembly::IdentifierContext _context, julia::AbstractAssembly& _assembly)
+ identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, julia::IdentifierContext _context, julia::AbstractAssembly& _assembly)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), "");
Declaration const* decl = ref->second.declaration;
solAssert(!!decl, "");
- if (_context == assembly::IdentifierContext::RValue)
+ if (_context == julia::IdentifierContext::RValue)
{
int const depositBefore = _assembly.stackHeight();
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index e03eea2e..eeb7d0a6 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -38,13 +38,10 @@ 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)
@@ -57,12 +54,14 @@ 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;
@@ -71,6 +70,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
{
+ expectValidType(_literal.type, _literal.location);
++m_stackHeight;
if (_literal.kind == assembly::LiteralKind::String && _literal.value.size() > 32)
{
@@ -123,7 +123,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
{
size_t stackSize(-1);
if (m_resolver)
- stackSize = m_resolver(_identifier, IdentifierContext::RValue);
+ stackSize = m_resolver(_identifier, julia::IdentifierContext::RValue);
if (stackSize == size_t(-1))
{
// Only add an error message if the callback did not do it.
@@ -143,6 +143,7 @@ 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)
{
@@ -160,15 +161,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,10 +182,24 @@ 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.variable.name)).active = true;
+ if ((m_stackHeight - stackHeight) != expectedItems)
+ {
+ m_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Variable count mismatch.",
+ _varDecl.location
+ ));
+ return false;
+ }
+
+ for (auto const& variable: _varDecl.variables)
+ {
+ expectValidType(variable.type, variable.location);
+ boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name)).active = true;
+ }
m_info.stackHeightInfo[&_varDecl] = m_stackHeight;
return success;
}
@@ -193,7 +208,10 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
{
Scope& bodyScope = scope(&_funDef.body);
for (auto const& var: _funDef.arguments + _funDef.returns)
+ {
+ expectValidType(var.type, var.location);
boost::get<Scope::Variable>(bodyScope.identifiers.at(var.name)).active = true;
+ }
int const stackHeight = m_stackHeight;
m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
@@ -274,6 +292,48 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
return success;
}
+bool AsmAnalyzer::operator()(Switch const& _switch)
+{
+ bool success = true;
+
+ int const initialStackHeight = m_stackHeight;
+ if (!boost::apply_visitor(*this, *_switch.expression))
+ success = false;
+ expectDeposit(1, initialStackHeight, locationOf(*_switch.expression));
+
+ set<tuple<LiteralKind, string>> cases;
+ for (auto const& _case: _switch.cases)
+ {
+ if (_case.value)
+ {
+ int const initialStackHeight = m_stackHeight;
+ 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_errors.push_back(make_shared<Error>(
+ Error::Type::DeclarationError,
+ "Duplicate case defined",
+ _case.location
+ ));
+ success = false;
+ }
+ }
+
+ if (!(*this)(_case.body))
+ success = false;
+ }
+
+ m_stackHeight--;
+
+ return success;
+}
+
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
@@ -340,7 +400,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t
variableSize = 1;
}
else if (m_resolver)
- variableSize = m_resolver(_variable, IdentifierContext::LValue);
+ variableSize = m_resolver(_variable, julia::IdentifierContext::LValue);
if (variableSize == size_t(-1))
{
// Only add message if the callback did not.
@@ -395,7 +455,21 @@ bool AsmAnalyzer::expectDeposit(int const _deposit, int const _oldHeight, Source
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_errors.push_back(make_shared<Error>(
+ Error::Type::TypeError,
+ "\"" + type + "\" is not a valid type (user defined types are not yet supported).",
+ _location
+ ));
+}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 426ee0d2..87d41e11 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -40,13 +40,14 @@ struct Literal;
struct Block;
struct Label;
struct FunctionalInstruction;
-struct FunctionalAssignment;
+struct Assignment;
struct VariableDeclaration;
struct Instruction;
struct Identifier;
-struct Assignment;
+struct StackAssignment;
struct FunctionDefinition;
struct FunctionCall;
+struct Switch;
struct Scope;
@@ -60,11 +61,12 @@ struct AsmAnalysisInfo;
class AsmAnalyzer: public boost::static_visitor<bool>
{
public:
- AsmAnalyzer(
+ explicit AsmAnalyzer(
AsmAnalysisInfo& _analysisInfo,
ErrorList& _errors,
- ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver()
- );
+ bool _julia = false,
+ julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
+ ): m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors), m_julia(_julia) {}
bool analyze(assembly::Block const& _block);
@@ -73,11 +75,12 @@ 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::Block const& _block);
private:
@@ -86,16 +89,18 @@ private:
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);
/// 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 const& m_resolver;
Scope* m_currentScope = nullptr;
AsmAnalysisInfo& m_info;
ErrorList& m_errors;
+ bool m_julia = false;
};
}
diff --git a/libsolidity/inlineasm/AsmAnalysisInfo.h b/libsolidity/inlineasm/AsmAnalysisInfo.h
index e21eb2c5..18382db0 100644
--- a/libsolidity/inlineasm/AsmAnalysisInfo.h
+++ b/libsolidity/inlineasm/AsmAnalysisInfo.h
@@ -36,17 +36,18 @@ struct Literal;
struct Block;
struct Label;
struct FunctionalInstruction;
-struct FunctionalAssignment;
+struct Assignment;
struct VariableDeclaration;
struct Instruction;
struct Identifier;
-struct Assignment;
+struct StackAssignment;
struct FunctionDefinition;
struct FunctionCall;
+struct Switch;
struct Scope;
-using Statement = boost::variant<Instruction, Literal, Label, Assignment, Identifier, FunctionalAssignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Block>;
+using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>;
struct AsmAnalysisInfo
{
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index b8af9dc6..6a44faac 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -32,7 +32,8 @@
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
-#include <libjulia/backends/AbstractAssembly.h>
+#include <libjulia/backends/evm/AbstractAssembly.h>
+#include <libjulia/backends/evm/EVMCodeTransform.h>
#include <libdevcore/CommonIO.h>
@@ -48,15 +49,6 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
-struct GeneratorState
-{
- GeneratorState(ErrorList& _errors, AsmAnalysisInfo& _analysisInfo):
- errors(_errors), info(_analysisInfo) {}
-
- ErrorList& errors;
- AsmAnalysisInfo info;
-};
-
class EthAssemblyAdapter: public julia::AbstractAssembly
{
public:
@@ -107,246 +99,15 @@ private:
eth::Assembly& m_assembly;
};
-class CodeTransform: public boost::static_visitor<>
-{
-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,
- julia::AbstractAssembly& _assembly,
- assembly::Block const& _block,
- assembly::ExternalIdentifierAccess const& _identifierAccess = assembly::ExternalIdentifierAccess()
- ): CodeTransform(_state, _assembly, _block, _identifierAccess, _assembly.stackHeight())
- {
- }
-
-private:
- CodeTransform(
- GeneratorState& _state,
- julia::AbstractAssembly& _assembly,
- assembly::Block const& _block,
- assembly::ExternalIdentifierAccess const& _identifierAccess,
- int _initialStackHeight
- ):
- m_state(_state),
- m_assembly(_assembly),
- m_scope(*m_state.info.scopes.at(&_block)),
- m_identifierAccess(_identifierAccess),
- m_initialStackHeight(_initialStackHeight)
- {
- int blockStartStackHeight = m_assembly.stackHeight();
- std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
-
- m_assembly.setSourceLocation(_block.location);
-
- // pop variables
- for (auto const& identifier: m_scope.identifiers)
- if (identifier.second.type() == typeid(Scope::Variable))
- m_assembly.appendInstruction(solidity::Instruction::POP);
-
- int deposit = m_assembly.stackHeight() - blockStartStackHeight;
- solAssert(deposit == 0, "Invalid stack height at end of block.");
- }
-
-public:
- void operator()(assembly::Instruction const& _instruction)
- {
- m_assembly.setSourceLocation(_instruction.location);
- m_assembly.appendInstruction(_instruction.instruction);
- checkStackHeight(&_instruction);
- }
- void operator()(assembly::Literal const& _literal)
- {
- m_assembly.setSourceLocation(_literal.location);
- if (_literal.kind == assembly::LiteralKind::Number)
- m_assembly.appendConstant(u256(_literal.value));
- else if (_literal.kind == assembly::LiteralKind::Boolean)
- {
- if (_literal.value == "true")
- m_assembly.appendConstant(u256(1));
- else
- m_assembly.appendConstant(u256(0));
- }
- else
- {
- solAssert(_literal.value.size() <= 32, "");
- m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft)));
- }
- checkStackHeight(&_literal);
- }
- void operator()(assembly::Identifier const& _identifier)
- {
- m_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_assembly.appendInstruction(solidity::dupInstruction(heightDiff));
- else
- // Store something to balance the stack
- m_assembly.appendConstant(u256(0));
- },
- [=](Scope::Label& _label)
- {
- assignLabelIdIfUnset(_label);
- m_assembly.appendLabelReference(*_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_assembly);
- checkStackHeight(&_identifier);
- }
- void operator()(FunctionalInstruction const& _instr)
- {
- for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it)
- {
- int height = m_assembly.stackHeight();
- boost::apply_visitor(*this, *it);
- expectDeposit(1, height);
- }
- (*this)(_instr.instruction);
- checkStackHeight(&_instr);
- }
- void operator()(assembly::FunctionCall const&)
- {
- solAssert(false, "Function call not removed during desugaring phase.");
- }
- void operator()(Label const& _label)
- {
- m_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_assembly.appendLabel(*label.id);
- checkStackHeight(&_label);
- }
- void operator()(assembly::Assignment const& _assignment)
- {
- m_assembly.setSourceLocation(_assignment.location);
- generateAssignment(_assignment.variableName, _assignment.location);
- checkStackHeight(&_assignment);
- }
- void operator()(FunctionalAssignment const& _assignment)
- {
- int height = m_assembly.stackHeight();
- boost::apply_visitor(*this, *_assignment.value);
- expectDeposit(1, height);
- m_assembly.setSourceLocation(_assignment.location);
- generateAssignment(_assignment.variableName, _assignment.location);
- checkStackHeight(&_assignment);
- }
- void operator()(assembly::VariableDeclaration const& _varDecl)
- {
- int height = m_assembly.stackHeight();
- boost::apply_visitor(*this, *_varDecl.value);
- expectDeposit(1, height);
- auto& var = boost::get<Scope::Variable>(m_scope.identifiers.at(_varDecl.variable.name));
- var.stackHeight = height;
- var.active = true;
- }
- void operator()(assembly::Block const& _block)
- {
- CodeTransform(m_state, m_assembly, _block, m_identifierAccess, m_initialStackHeight);
- checkStackHeight(&_block);
- }
- void operator()(assembly::FunctionDefinition const&)
- {
- solAssert(false, "Function definition not removed during desugaring phase.");
- }
-
-private:
- void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
- {
- 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_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1));
- m_assembly.appendInstruction(solidity::Instruction::POP);
- }
- else
- {
- solAssert(
- m_identifierAccess.generateCode,
- "Identifier not found and no external access available."
- );
- m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly);
- }
- }
-
- /// 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)
- {
- int heightDiff = m_assembly.stackHeight() - _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;
- }
-
- void expectDeposit(int _deposit, int _oldHeight)
- {
- solAssert(m_assembly.stackHeight() == _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_assembly.stackHeight() - m_initialStackHeight,
- "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)
- {
- if (!_label.id)
- _label.id.reset(m_assembly.newLabelId());
- }
-
-
- GeneratorState& m_state;
- julia::AbstractAssembly& m_assembly;
- Scope& m_scope;
- ExternalIdentifierAccess m_identifierAccess;
- int const m_initialStackHeight;
-};
-
eth::Assembly assembly::CodeGenerator::assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
- ExternalIdentifierAccess const& _identifierAccess
+ julia::ExternalIdentifierAccess const& _identifierAccess
)
{
eth::Assembly assembly;
- GeneratorState state(m_errors, _analysisInfo);
EthAssemblyAdapter assemblyAdapter(assembly);
- CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess);
+ julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess);
return assembly;
}
@@ -354,10 +115,9 @@ void assembly::CodeGenerator::assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess
+ julia::ExternalIdentifierAccess const& _identifierAccess
)
{
- GeneratorState state(m_errors, _analysisInfo);
EthAssemblyAdapter assemblyAdapter(_assembly);
- CodeTransform(state, assemblyAdapter, _parsedData, _identifierAccess);
+ julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess);
}
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index e830e047..1b43d2f6 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -48,14 +48,14 @@ public:
eth::Assembly assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
);
/// Performs code generation and appends generated to to _assembly.
void assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
);
private:
diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h
index 8efe1f07..72afeef1 100644
--- a/libsolidity/inlineasm/AsmData.h
+++ b/libsolidity/inlineasm/AsmData.h
@@ -40,6 +40,21 @@ using TypedNameList = std::vector<TypedName>;
/// What follows are the AST nodes for assembly.
+struct Instruction;
+struct Literal;
+struct Label;
+struct StackAssignment;
+struct Identifier;
+struct Assignment;
+struct VariableDeclaration;
+struct FunctionalInstruction;
+struct FunctionDefinition;
+struct FunctionCall;
+struct Switch;
+struct Block;
+
+using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, Switch, Block>;
+
/// Direct EVM instruction (except PUSHi and JUMPDEST)
struct Instruction { SourceLocation location; solidity::Instruction instruction; };
/// Literal number or string (up to 32 bytes)
@@ -47,29 +62,26 @@ 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:u256)", 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; };
+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:u256 := mload(20:u256)"), non-hoisted
-struct VariableDeclaration { SourceLocation location; TypedName variable; std::shared_ptr<Statement> value; };
+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; 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 LocationExtractor: boost::static_visitor<SourceLocation>
{
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index a96984f5..80409c63 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -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,24 +67,41 @@ 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::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
- case Token::Address: // opcode
default:
break;
}
@@ -93,29 +110,28 @@ assembly::Statement Parser::parseStatement()
// literal,
// identifier (might turn into label or functional assignment)
Statement statement(parseElementaryOperation(false));
- switch (m_scanner->currentToken())
+ 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
{
@@ -135,11 +151,31 @@ assembly::Statement Parser::parseStatement()
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::Statement Parser::parseExpression()
{
Statement operation = parseElementaryOperation(true);
- if (m_scanner->currentToken() == Token::LParen)
- return parseFunctionalInstruction(std::move(operation));
+ if (currentToken() == Token::LParen)
+ return parseCall(std::move(operation));
else
return operation;
}
@@ -171,7 +207,7 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions()
assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
{
Statement ret;
- switch (m_scanner->currentToken())
+ switch (currentToken())
{
case Token::Identifier:
case Token::Return:
@@ -179,14 +215,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))
{
@@ -201,7 +237,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
}
else
ret = Identifier{location(), literal};
- m_scanner->next();
+ advance();
break;
}
case Token::StringLiteral:
@@ -210,7 +246,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
case Token::FalseLiteral:
{
LiteralKind kind = LiteralKind::Number;
- switch (m_scanner->currentToken())
+ switch (currentToken())
{
case Token::StringLiteral:
kind = LiteralKind::String;
@@ -229,10 +265,10 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
Literal literal{
location(),
kind,
- m_scanner->currentLiteral(),
+ currentLiteral(),
""
};
- m_scanner->next();
+ advance();
if (m_julia)
{
expectToken(Token::Colon);
@@ -248,7 +284,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
fatalParserError(
m_julia ?
"Literal or identifier expected." :
- "Expected elementary inline assembly operation."
+ "Literal, identifier or instruction expected."
);
}
return ret;
@@ -258,7 +294,14 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
expectToken(Token::Let);
- varDecl.variable = parseTypedName();
+ 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()));
@@ -272,22 +315,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.emplace_back(parseTypedName());
- if (m_scanner->currentToken() == Token::RParen)
+ 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.emplace_back(parseTypedName());
- if (m_scanner->currentToken() == Token::LBrace)
+ if (currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
}
@@ -297,7 +340,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))
{
@@ -315,10 +358,20 @@ 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 (" +
+ instrInfo.name +
+ " 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 +
@@ -327,11 +380,11 @@ assembly::Statement Parser::parseFunctionalInstruction(assembly::Statement&& _in
" arguments)"
));
else
- m_scanner->next();
+ advance();
}
}
ret.location.end = endPosition();
- if (m_scanner->currentToken() == Token::Comma)
+ if (currentToken() == Token::Comma)
fatalParserError(
string("Expected ')' (" + instrInfo.name + " expects " + boost::lexical_cast<string>(args) + " arguments)")
);
@@ -344,10 +397,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);
}
@@ -380,12 +433,12 @@ TypedName Parser::parseTypedName()
string Parser::expectAsmIdentifier()
{
- string name = m_scanner->currentLiteral();
+ string name = currentLiteral();
if (m_julia)
{
- if (m_scanner->currentToken() == Token::Bool)
+ if (currentToken() == Token::Bool)
{
- m_scanner->next();
+ advance();
return name;
}
}
diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h
index addc1725..138af337 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -62,13 +62,14 @@ protected:
Block parseBlock();
Statement parseStatement();
+ Case parseCase();
/// Parses a functional expression that has to push exactly one stack element
Statement parseExpression();
std::map<std::string, dev::solidity::Instruction> const& instructions();
Statement parseElementaryOperation(bool _onlySinglePusher = false);
VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition();
- Statement parseFunctionalInstruction(Statement&& _instruction);
+ Statement parseCall(Statement&& _instruction);
TypedName parseTypedName();
std::string expectAsmIdentifier();
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 636e61b8..e282e5e8 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -108,20 +108,29 @@ string AsmPrinter::operator()(assembly::Label const& _label)
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.variable.name + appendTypeName(_variableDeclaration.variable.type) + " := " + 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)
@@ -158,6 +167,20 @@ 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()(Block const& _block)
{
if (_block.statements.empty())
diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h
index 282fd7e3..b0d7fc09 100644
--- a/libsolidity/inlineasm/AsmPrinter.h
+++ b/libsolidity/inlineasm/AsmPrinter.h
@@ -35,11 +35,12 @@ struct Literal;
struct Identifier;
struct FunctionalInstruction;
struct Label;
+struct StackAssignment;
struct Assignment;
-struct FunctionalAssignment;
struct VariableDeclaration;
struct FunctionDefinition;
struct FunctionCall;
+struct Switch;
struct Block;
class AsmPrinter: public boost::static_visitor<std::string>
@@ -52,11 +53,12 @@ public:
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::Block const& _block);
private:
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index eb10dbb3..7eb6a9ed 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -59,7 +59,10 @@ bool ScopeFiller::operator()(Label const& _item)
bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl)
{
- return registerVariable(_varDecl.variable, _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)
@@ -94,6 +97,15 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
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()(Block const& _block)
{
bool success = true;
diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h
index 61428eea..c7179b3b 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.h
+++ b/libsolidity/inlineasm/AsmScopeFiller.h
@@ -39,13 +39,14 @@ struct Literal;
struct Block;
struct Label;
struct FunctionalInstruction;
-struct FunctionalAssignment;
+struct Assignment;
struct VariableDeclaration;
struct Instruction;
struct Identifier;
-struct Assignment;
+struct StackAssignment;
struct FunctionDefinition;
struct FunctionCall;
+struct Switch;
struct Scope;
@@ -64,11 +65,12 @@ public:
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::Block const& _block);
private:
diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp
index c2a7d8ea..fe443c08 100644
--- a/libsolidity/inlineasm/AsmStack.cpp
+++ b/libsolidity/inlineasm/AsmStack.cpp
@@ -42,7 +42,7 @@ using namespace dev::solidity::assembly;
bool InlineAssemblyStack::parse(
shared_ptr<Scanner> const& _scanner,
- ExternalIdentifierAccess::Resolver const& _resolver
+ julia::ExternalIdentifierAccess::Resolver const& _resolver
)
{
m_parserResult = make_shared<Block>();
@@ -53,7 +53,7 @@ bool InlineAssemblyStack::parse(
*m_parserResult = std::move(*result);
AsmAnalysisInfo analysisInfo;
- return (AsmAnalyzer(analysisInfo, m_errors, _resolver)).analyze(*m_parserResult);
+ return (AsmAnalyzer(analysisInfo, m_errors, false, _resolver)).analyze(*m_parserResult);
}
string InlineAssemblyStack::toString()
@@ -73,7 +73,7 @@ eth::Assembly InlineAssemblyStack::assemble()
bool InlineAssemblyStack::parseAndAssemble(
string const& _input,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess
+ julia::ExternalIdentifierAccess const& _identifierAccess
)
{
ErrorList errors;
@@ -84,7 +84,7 @@ bool InlineAssemblyStack::parseAndAssemble(
solAssert(parserResult, "");
AsmAnalysisInfo analysisInfo;
- AsmAnalyzer analyzer(analysisInfo, errors, _identifierAccess.resolve);
+ AsmAnalyzer analyzer(analysisInfo, errors, false, _identifierAccess.resolve);
solAssert(analyzer.analyze(*parserResult), "");
CodeGenerator(errors).assemble(*parserResult, analysisInfo, _assembly, _identifierAccess);
diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h
index e223ccc9..23072a88 100644
--- a/libsolidity/inlineasm/AsmStack.h
+++ b/libsolidity/inlineasm/AsmStack.h
@@ -24,7 +24,7 @@
#include <libsolidity/interface/Exceptions.h>
-#include <libjulia/backends/AbstractAssembly.h>
+#include <libjulia/backends/evm/AbstractAssembly.h>
#include <string>
#include <functional>
@@ -43,23 +43,6 @@ 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, julia::AbstractAssembly&)>;
- /// 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:
@@ -67,7 +50,7 @@ public:
/// @return false or error.
bool parse(
std::shared_ptr<Scanner> const& _scanner,
- ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = ExternalIdentifierAccess::Resolver()
+ julia::ExternalIdentifierAccess::Resolver const& _externalIdentifierResolver = julia::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).
@@ -79,7 +62,7 @@ public:
bool parseAndAssemble(
std::string const& _input,
eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess()
+ julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess()
);
ErrorList const& errors() const { return m_errors; }
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
new file mode 100644
index 00000000..c4bd63c4
--- /dev/null
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -0,0 +1,85 @@
+/*
+ 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/>.
+*/
+/**
+ * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * eWasm as output.
+ */
+
+
+#include <libsolidity/interface/AssemblyStack.h>
+
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/inlineasm/AsmPrinter.h>
+#include <libsolidity/inlineasm/AsmParser.h>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmCodeGen.h>
+
+#include <libevmasm/Assembly.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+Scanner const& AssemblyStack::scanner() const
+{
+ solAssert(m_scanner, "");
+ return *m_scanner;
+}
+
+bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
+{
+ m_analysisSuccessful = false;
+ m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
+ m_parserResult = assembly::Parser(m_errors, m_language == Language::JULIA).parse(m_scanner);
+ if (!m_errors.empty())
+ return false;
+ solAssert(m_parserResult, "");
+
+ m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
+ assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errors);
+ m_analysisSuccessful = analyzer.analyze(*m_parserResult);
+ return m_analysisSuccessful;
+}
+
+eth::LinkerObject AssemblyStack::assemble(Machine _machine)
+{
+ solAssert(m_analysisSuccessful, "");
+ solAssert(m_parserResult, "");
+ solAssert(m_analysisInfo, "");
+
+ switch (_machine)
+ {
+ case Machine::EVM:
+ {
+ auto assembly = assembly::CodeGenerator(m_errors).assemble(*m_parserResult, *m_analysisInfo);
+ return assembly.assemble();
+ }
+ case Machine::EVM15:
+ solUnimplemented("EVM 1.5 backend is not yet implemented.");
+ case Machine::eWasm:
+ solUnimplemented("eWasm backend is not yet implemented.");
+ }
+ // unreachable
+ return eth::LinkerObject();
+}
+
+string AssemblyStack::print()
+{
+ solAssert(m_parserResult, "");
+ return assembly::AsmPrinter(m_language == Language::JULIA)(*m_parserResult);
+}
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
new file mode 100644
index 00000000..40662ac3
--- /dev/null
+++ b/libsolidity/interface/AssemblyStack.h
@@ -0,0 +1,85 @@
+/*
+ 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/>.
+*/
+/**
+ * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * eWasm as output.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
+#include <libevmasm/LinkerObject.h>
+
+#include <string>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+class Scanner;
+namespace assembly
+{
+struct AsmAnalysisInfo;
+struct Block;
+}
+
+/*
+ * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and
+ * eWasm as output.
+ */
+class AssemblyStack
+{
+public:
+ enum class Language { JULIA, Assembly };
+ enum class Machine { EVM, EVM15, eWasm };
+
+ explicit AssemblyStack(Language _language = Language::Assembly):
+ m_language(_language)
+ {}
+
+ /// @returns the scanner used during parsing
+ Scanner const& scanner() const;
+
+ /// Runs parsing and analysis steps, returns false if input cannot be assembled.
+ /// Multiple calls overwrite the previous state.
+ bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
+
+ /// Run the assembly step (should only be called after parseAndAnalyze).
+ eth::LinkerObject assemble(Machine _machine);
+
+ /// @returns the errors generated during parsing, analysis (and potentially assembly).
+ ErrorList const& errors() const { return m_errors; }
+
+ /// Pretty-print the input after having parsed it.
+ std::string print();
+
+private:
+
+ Language m_language = Language::Assembly;
+
+ std::shared_ptr<Scanner> m_scanner;
+
+ bool m_analysisSuccessful = false;
+ std::shared_ptr<assembly::Block> m_parserResult;
+ std::shared_ptr<assembly::AsmAnalysisInfo> m_analysisInfo;
+ ErrorList m_errors;
+};
+
+}
+}
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 72712298..328df91f 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -507,8 +507,8 @@ string const& CompilerStack::onChainMetadata(string const& _contractName) const
Scanner const& CompilerStack::scanner(string const& _sourceName) const
{
- if (m_stackState < ParsingSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ if (m_stackState < SourcesSet)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set."));
return *source(_sourceName).scanner;
}
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 72150a3e..5b318b2b 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -1437,5 +1437,49 @@ ASTPointer<ParameterList> Parser::createEmptyParameterList()
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
+string Parser::currentTokenName()
+{
+ Token::Value token = m_scanner->currentToken();
+ if (Token::isElementaryTypeName(token)) //for the sake of accuracy in reporting
+ {
+ ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
+ return elemTypeName.toString();
+ }
+ else
+ return Token::name(token);
+}
+
+Token::Value Parser::expectAssignmentOperator()
+{
+ Token::Value op = m_scanner->currentToken();
+ if (!Token::isAssignmentOp(op))
+ fatalParserError(
+ string("Expected assignment operator, got '") +
+ currentTokenName() +
+ string("'")
+ );
+ m_scanner->next();
+ return op;
+}
+
+ASTPointer<ASTString> Parser::expectIdentifierToken()
+{
+ Token::Value id = m_scanner->currentToken();
+ if (id != Token::Identifier)
+ fatalParserError(
+ string("Expected identifier, got '") +
+ currentTokenName() +
+ string("'")
+ );
+ return getLiteralAndAdvance();
+}
+
+ASTPointer<ASTString> Parser::getLiteralAndAdvance()
+{
+ ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->currentLiteral());
+ m_scanner->next();
+ return identifier;
+}
+
}
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 282617ab..5ec3dbed 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -154,6 +154,11 @@ private:
std::vector<ASTPointer<PrimaryExpression>> const& _path,
std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
);
+
+ std::string currentTokenName();
+ Token::Value expectAssignmentOperator();
+ ASTPointer<ASTString> expectIdentifierToken();
+ ASTPointer<ASTString> getLiteralAndAdvance();
///@}
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 87d47f4b..ac103bda 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -80,62 +80,6 @@ void ParserBase::expectToken(Token::Value _value)
m_scanner->next();
}
-Token::Value ParserBase::expectAssignmentOperator()
-{
- Token::Value op = m_scanner->currentToken();
- if (!Token::isAssignmentOp(op))
- {
- if (Token::isElementaryTypeName(op)) //for the sake of accuracy in reporting
- {
- ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
- fatalParserError(
- string("Expected assignment operator, got '") +
- elemTypeName.toString() +
- string("'")
- );
- }
- else
- fatalParserError(
- string("Expected assignment operator, got '") +
- string(Token::name(m_scanner->currentToken())) +
- string("'")
- );
- }
- m_scanner->next();
- return op;
-}
-
-ASTPointer<ASTString> ParserBase::expectIdentifierToken()
-{
- Token::Value id = m_scanner->currentToken();
- if (id != Token::Identifier)
- {
- if (Token::isElementaryTypeName(id)) //for the sake of accuracy in reporting
- {
- ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
- fatalParserError(
- string("Expected identifier, got '") +
- elemTypeName.toString() +
- string("'")
- );
- }
- else
- fatalParserError(
- string("Expected identifier, got '") +
- string(Token::name(id)) +
- string("'")
- );
- }
- return getLiteralAndAdvance();
-}
-
-ASTPointer<ASTString> ParserBase::getLiteralAndAdvance()
-{
- ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->currentLiteral());
- m_scanner->next();
- return identifier;
-}
-
void ParserBase::parserError(string const& _description)
{
auto err = make_shared<Error>(Error::Type::ParserError);
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
index dfb7cab7..18b39a5e 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -24,8 +24,8 @@
#include <memory>
#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Token.h>
-#include <libsolidity/ast/ASTForward.h>
namespace dev
{
@@ -47,14 +47,14 @@ protected:
/// End position of the current token
int endPosition() const;
-
///@{
///@name Helper functions
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
- Token::Value expectAssignmentOperator();
- ASTPointer<ASTString> expectIdentifierToken();
- ASTPointer<ASTString> getLiteralAndAdvance();
+ Token::Value currentToken() const { return m_scanner->currentToken(); }
+ Token::Value peekNextToken() const { return m_scanner->peekNextToken(); }
+ std::string currentLiteral() const { return m_scanner->currentLiteral(); }
+ Token::Value advance() { return m_scanner->next(); }
///@}
/// Creates a @ref ParserError and annotates it with the current position and the