aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-07-03 20:52:29 +0800
committerGitHub <noreply@github.com>2017-07-03 20:52:29 +0800
commit76d3b7c5a160e1f550c710e6850ee6f116142ca1 (patch)
tree93c96f7073617b4f56c8c355cdc30701aec4818b /libsolidity
parent78969364608ba60d1654f4d1738886d13112b6cd (diff)
parent2222ddecf49b5b901f63b9e7449ee76c9f51c47a (diff)
downloaddexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.gz
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.bz2
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.lz
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.xz
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.tar.zst
dexon-solidity-76d3b7c5a160e1f550c710e6850ee6f116142ca1.zip
Merge pull request #2510 from ethereum/develop
Version 0.4.12
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/CMakeLists.txt4
-rw-r--r--libsolidity/analysis/DocStringAnalyser.cpp19
-rw-r--r--libsolidity/analysis/DocStringAnalyser.h8
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp153
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h43
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp19
-rw-r--r--libsolidity/analysis/PostTypeChecker.h8
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp36
-rw-r--r--libsolidity/analysis/ReferencesResolver.h15
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp47
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h7
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp53
-rw-r--r--libsolidity/analysis/SyntaxChecker.h9
-rw-r--r--libsolidity/analysis/TypeChecker.cpp477
-rw-r--r--libsolidity/analysis/TypeChecker.h17
-rw-r--r--libsolidity/ast/AST.cpp20
-rw-r--r--libsolidity/ast/AST.h15
-rw-r--r--libsolidity/ast/ASTAnnotations.h13
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp881
-rw-r--r--libsolidity/ast/ASTJsonConverter.h114
-rw-r--r--libsolidity/ast/Types.cpp13
-rw-r--r--libsolidity/ast/Types.h9
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp6
-rw-r--r--libsolidity/codegen/CompilerContext.cpp63
-rw-r--r--libsolidity/codegen/CompilerContext.h18
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp124
-rw-r--r--libsolidity/codegen/CompilerUtils.h24
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp100
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp37
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
-rw-r--r--libsolidity/codegen/LValue.cpp6
-rw-r--r--libsolidity/formal/Why3Translator.cpp902
-rw-r--r--libsolidity/formal/Why3Translator.h149
-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.h (renamed from libsolidity/interface/Utils.h)39
-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
-rw-r--r--libsolidity/interface/ABI.cpp116
-rw-r--r--libsolidity/interface/ABI.h56
-rw-r--r--libsolidity/interface/AssemblyStack.cpp119
-rw-r--r--libsolidity/interface/AssemblyStack.h96
-rw-r--r--libsolidity/interface/CompilerStack.cpp129
-rw-r--r--libsolidity/interface/CompilerStack.h84
-rw-r--r--libsolidity/interface/ErrorReporter.cpp167
-rw-r--r--libsolidity/interface/ErrorReporter.h102
-rw-r--r--libsolidity/interface/Exceptions.cpp4
-rw-r--r--libsolidity/interface/Exceptions.h12
-rw-r--r--libsolidity/interface/InterfaceHandler.cpp197
-rw-r--r--libsolidity/interface/Natspec.cpp130
-rw-r--r--libsolidity/interface/Natspec.h (renamed from libsolidity/interface/InterfaceHandler.h)9
-rw-r--r--libsolidity/interface/StandardCompiler.cpp32
-rw-r--r--libsolidity/interface/Version.cpp2
-rw-r--r--libsolidity/parsing/DocStringParser.cpp11
-rw-r--r--libsolidity/parsing/DocStringParser.h7
-rw-r--r--libsolidity/parsing/Parser.cpp111
-rw-r--r--libsolidity/parsing/Parser.h7
-rw-r--r--libsolidity/parsing/ParserBase.cpp87
-rw-r--r--libsolidity/parsing/ParserBase.h15
-rw-r--r--libsolidity/parsing/Scanner.cpp2
-rw-r--r--libsolidity/parsing/Token.h1
73 files changed, 3131 insertions, 3379 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/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp
index 58261144..9a846b31 100644
--- a/libsolidity/analysis/DocStringAnalyser.cpp
+++ b/libsolidity/analysis/DocStringAnalyser.cpp
@@ -23,6 +23,7 @@
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/parsing/DocStringParser.h>
using namespace std;
@@ -39,7 +40,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
bool DocStringAnalyser::visit(ContractDefinition const& _node)
{
- static const set<string> validTags = set<string>{"author", "title", "dev", "notice", "why3"};
+ static const set<string> validTags = set<string>{"author", "title", "dev", "notice"};
parseDocStrings(_node, _node.annotation(), validTags, "contracts");
return true;
@@ -65,16 +66,6 @@ bool DocStringAnalyser::visit(EventDefinition const& _node)
return true;
}
-bool DocStringAnalyser::visitNode(ASTNode const& _node)
-{
- if (auto node = dynamic_cast<Statement const*>(&_node))
- {
- static const set<string> validTags = {"why3"};
- parseDocStrings(*node, node->annotation(), validTags, "statements");
- }
- return true;
-}
-
void DocStringAnalyser::handleCallable(
CallableDeclaration const& _callable,
Documented const& _node,
@@ -110,7 +101,7 @@ void DocStringAnalyser::parseDocStrings(
DocStringParser parser;
if (_node.documentation() && !_node.documentation()->empty())
{
- if (!parser.parse(*_node.documentation(), m_errors))
+ if (!parser.parse(*_node.documentation(), m_errorReporter))
m_errorOccured = true;
_annotation.docTags = parser.tags();
}
@@ -121,8 +112,6 @@ void DocStringAnalyser::parseDocStrings(
void DocStringAnalyser::appendError(string const& _description)
{
- auto err = make_shared<Error>(Error::Type::DocstringParsingError);
- *err << errinfo_comment(_description);
- m_errors.push_back(err);
m_errorOccured = true;
+ m_errorReporter.docstringParsingError(_description);
}
diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h
index bfc8befc..158b4060 100644
--- a/libsolidity/analysis/DocStringAnalyser.h
+++ b/libsolidity/analysis/DocStringAnalyser.h
@@ -30,6 +30,8 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
+
/**
* Parses and analyses the doc strings.
* Stores the parsing results in the AST annotations and reports errors.
@@ -37,7 +39,7 @@ namespace solidity
class DocStringAnalyser: private ASTConstVisitor
{
public:
- DocStringAnalyser(ErrorList& _errors): m_errors(_errors) {}
+ DocStringAnalyser(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool analyseDocStrings(SourceUnit const& _sourceUnit);
private:
@@ -46,8 +48,6 @@ private:
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual bool visit(EventDefinition const& _event) override;
- virtual bool visitNode(ASTNode const&) override;
-
void handleCallable(
CallableDeclaration const& _callable,
Documented const& _node,
@@ -64,7 +64,7 @@ private:
void appendError(std::string const& _description);
bool m_errorOccured = false;
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
};
}
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 336dc894..aac90311 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -24,7 +24,9 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ErrorReporter.h>
+
+#include <boost/algorithm/string.hpp>
using namespace std;
@@ -36,10 +38,10 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver(
vector<Declaration const*> const& _globals,
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
- ErrorList& _errors
+ ErrorReporter& _errorReporter
) :
m_scopes(_scopes),
- m_errors(_errors)
+ m_errorReporter(_errorReporter)
{
if (!m_scopes[nullptr])
m_scopes[nullptr].reset(new DeclarationContainer());
@@ -52,11 +54,11 @@ bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode con
// The helper registers all declarations in m_scopes as a side-effect of its construction.
try
{
- DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors, _currentScope);
+ DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope);
}
catch (FatalError const&)
{
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
return false;
}
@@ -73,7 +75,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
string const& path = imp->annotation().absolutePath;
if (!_sourceUnits.count(path))
{
- reportDeclarationError(
+ m_errorReporter.declarationError(
imp->location(),
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
);
@@ -88,7 +90,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
auto declarations = scope->second->resolveName(alias.first->name(), false);
if (declarations.empty())
{
- reportDeclarationError(
+ m_errorReporter.declarationError(
imp->location(),
"Declaration \"" +
alias.first->name() +
@@ -106,7 +108,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
ASTString const* name = alias.second ? alias.second.get() : &declaration->name();
if (!target.registerDeclaration(*declaration, name))
{
- reportDeclarationError(
+ m_errorReporter.declarationError(
imp->location(),
"Identifier \"" + *name + "\" already declared."
);
@@ -119,7 +121,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
for (auto const& declaration: nameAndDeclaration.second)
if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first))
{
- reportDeclarationError(
+ m_errorReporter.declarationError(
imp->location(),
"Identifier \"" + nameAndDeclaration.first + "\" already declared."
);
@@ -137,7 +139,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
}
catch (FatalError const&)
{
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
return false;
}
@@ -152,7 +154,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
}
catch (FatalError const&)
{
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
return false;
}
@@ -214,7 +216,7 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter)
- reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
+ m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
if (uniqueFunctions.end() == find_if(
uniqueFunctions.begin(),
@@ -232,6 +234,26 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
return uniqueFunctions;
}
+void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
+{
+ for (auto const& instruction: c_instructions)
+ {
+ string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
+ auto declarations = nameFromCurrentScope(instructionName);
+ for (Declaration const* const declaration: declarations)
+ {
+ solAssert(!!declaration, "");
+ if (dynamic_cast<MagicVariableDeclaration const* const>(declaration))
+ // Don't warn the user for what the user did not.
+ continue;
+ m_errorReporter.warning(
+ declaration->location(),
+ "Variable is shadowed in inline assembly by an instruction of the same name"
+ );
+ }
+ }
+}
+
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
@@ -290,7 +312,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
{
if (m_scopes.count(&_node))
m_currentScope = m_scopes[&_node].get();
- return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node);
+ return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
}
}
@@ -328,11 +350,10 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
secondDeclarationLocation = declaration->location();
}
- reportDeclarationError(
+ m_errorReporter.declarationError(
secondDeclarationLocation,
- "Identifier already declared.",
- firstDeclarationLocation,
- "The previous declaration is here:"
+ SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
+ "Identifier already declared."
);
}
}
@@ -347,19 +368,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
UserDefinedTypeName const& baseName = baseSpecifier->name();
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
if (!base)
- reportFatalTypeError(baseName.createTypeError("Contract expected."));
+ m_errorReporter.fatalTypeError(baseName.location(), "Contract expected.");
// "push_front" has the effect that bases mentioned later can overwrite members of bases
// mentioned earlier
input.back().push_front(base);
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
if (basesBases.empty())
- reportFatalTypeError(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
+ m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract");
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
}
input.back().push_front(&_contract);
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
- reportFatalTypeError(_contract.createTypeError("Linearization of inheritance graph impossible"));
+ m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible");
_contract.annotation().linearizedBaseContracts = result;
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
}
@@ -415,61 +436,15 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMer
return result;
}
-void NameAndTypeResolver::reportDeclarationError(
- SourceLocation _sourceLoction,
- string const& _description,
- SourceLocation _secondarySourceLocation,
- string const& _secondaryDescription
-)
-{
- auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
- *err <<
- errinfo_sourceLocation(_sourceLoction) <<
- errinfo_comment(_description) <<
- errinfo_secondarySourceLocation(
- SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
- );
-
- m_errors.push_back(err);
-}
-
-void NameAndTypeResolver::reportDeclarationError(SourceLocation _sourceLocation, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
- *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
-
- m_errors.push_back(err);
-}
-
-void NameAndTypeResolver::reportFatalDeclarationError(
- SourceLocation _sourceLocation,
- string const& _description
-)
-{
- reportDeclarationError(_sourceLocation, _description);
- BOOST_THROW_EXCEPTION(FatalError());
-}
-
-void NameAndTypeResolver::reportTypeError(Error const& _e)
-{
- m_errors.push_back(make_shared<Error>(_e));
-}
-
-void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
-{
- reportTypeError(_e);
- BOOST_THROW_EXCEPTION(FatalError());
-}
-
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
- ErrorList& _errors,
+ ErrorReporter& _errorReporter,
ASTNode const* _currentScope
):
m_scopes(_scopes),
m_currentScope(_currentScope),
- m_errors(_errors)
+ m_errorReporter(_errorReporter)
{
_astRoot.accept(*this);
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
@@ -633,11 +608,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
secondDeclarationLocation = _declaration.location();
}
- declarationError(
+ m_errorReporter.declarationError(
secondDeclarationLocation,
- "Identifier already declared.",
- firstDeclarationLocation,
- "The previous declaration is here:"
+ SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
+ "Identifier already declared."
);
}
@@ -665,40 +639,5 @@ string DeclarationRegistrationHelper::currentCanonicalName() const
return ret;
}
-void DeclarationRegistrationHelper::declarationError(
- SourceLocation _sourceLocation,
- string const& _description,
- SourceLocation _secondarySourceLocation,
- string const& _secondaryDescription
-)
-{
- auto err = make_shared<Error>(Error::Type::DeclarationError);
- *err <<
- errinfo_sourceLocation(_sourceLocation) <<
- errinfo_comment(_description) <<
- errinfo_secondarySourceLocation(
- SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
- );
-
- m_errors.push_back(err);
-}
-
-void DeclarationRegistrationHelper::declarationError(SourceLocation _sourceLocation, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::DeclarationError);
- *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
-
- m_errors.push_back(err);
-}
-
-void DeclarationRegistrationHelper::fatalDeclarationError(
- SourceLocation _sourceLocation,
- string const& _description
-)
-{
- declarationError(_sourceLocation, _description);
- BOOST_THROW_EXCEPTION(FatalError());
-}
-
}
}
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 038a887b..84628778 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -35,6 +35,8 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
+
/**
* Resolves name references, typenames and sets the (explicitly given) types for all variable
* declarations.
@@ -48,7 +50,7 @@ public:
NameAndTypeResolver(
std::vector<Declaration const*> const& _globals,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
- ErrorList& _errors
+ ErrorReporter& _errorReporter
);
/// Registers all declarations found in the AST node, usually a source unit.
/// @returns false in case of error.
@@ -88,6 +90,9 @@ public:
std::vector<Declaration const*> const& _declarations
);
+ /// Generate and store warnings about variables that are named like instructions.
+ void warnVariablesNamedLikeInstructions();
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
@@ -103,24 +108,6 @@ private:
template <class _T>
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
- // creates the Declaration error and adds it in the errors list
- void reportDeclarationError(
- SourceLocation _sourceLoction,
- std::string const& _description,
- SourceLocation _secondarySourceLocation,
- std::string const& _secondaryDescription
- );
- // creates the Declaration error and adds it in the errors list
- void reportDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
- // creates the Declaration error and adds it in the errors list and throws FatalError
- void reportFatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
-
- // creates the Declaration error and adds it in the errors list
- void reportTypeError(Error const& _e);
- // creates the Declaration error and adds it in the errors list and throws FatalError
- void reportFatalTypeError(Error const& _e);
-
-
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code.
@@ -128,7 +115,7 @@ private:
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
DeclarationContainer* m_currentScope = nullptr;
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
};
/**
@@ -145,7 +132,7 @@ public:
DeclarationRegistrationHelper(
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
- ErrorList& _errors,
+ ErrorReporter& _errorReporter,
ASTNode const* _currentScope = nullptr
);
@@ -175,23 +162,11 @@ private:
/// @returns the canonical name of the current scope.
std::string currentCanonicalName() const;
- // creates the Declaration error and adds it in the errors list
- void declarationError(
- SourceLocation _sourceLocation,
- std::string const& _description,
- SourceLocation _secondarySourceLocation,
- std::string const& _secondaryDescription
- );
-
- // creates the Declaration error and adds it in the errors list
- void declarationError(SourceLocation _sourceLocation, std::string const& _description);
- // creates the Declaration error and adds it in the errors list and throws FatalError
- void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
ASTNode const* m_currentScope = nullptr;
VariableScope* m_currentFunction = nullptr;
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
};
}
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index e8da3ca4..fbc72e52 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -18,6 +18,7 @@
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
#include <boost/range/adaptor/map.hpp>
@@ -32,17 +33,7 @@ using namespace dev::solidity;
bool PostTypeChecker::check(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
- return Error::containsOnlyWarnings(m_errors);
-}
-
-void PostTypeChecker::typeError(SourceLocation const& _location, std::string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::TypeError);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool PostTypeChecker::visit(ContractDefinition const&)
@@ -57,7 +48,11 @@ void PostTypeChecker::endVisit(ContractDefinition const&)
solAssert(!m_currentConstVariable, "");
for (auto declaration: m_constVariables)
if (auto identifier = findCycle(declaration))
- typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + ".");
+ m_errorReporter.typeError(
+ declaration->location(),
+ "The value of the constant " + declaration->name() +
+ " has a cyclic dependency via " + identifier->name() + "."
+ );
m_constVariables.clear();
m_constVariableDependencies.clear();
diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h
index 8774f413..dbdf50e0 100644
--- a/libsolidity/analysis/PostTypeChecker.h
+++ b/libsolidity/analysis/PostTypeChecker.h
@@ -28,6 +28,8 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
+
/**
* This module performs analyses on the AST that are done after type checking and assignments of types:
* - whether there are circular references in constant state variables
@@ -37,7 +39,7 @@ class PostTypeChecker: private ASTConstVisitor
{
public:
/// @param _errors the reference to the list of errors and warnings to add them found during type checking.
- PostTypeChecker(ErrorList& _errors): m_errors(_errors) {}
+ PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool check(ASTNode const& _astRoot);
@@ -55,10 +57,10 @@ private:
VariableDeclaration const* findCycle(
VariableDeclaration const* _startingFrom,
- std::set<VariableDeclaration const*> const& _seen = {}
+ std::set<VariableDeclaration const*> const& _seen = std::set<VariableDeclaration const*>{}
);
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
VariableDeclaration const* m_currentConstVariable = nullptr;
std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism.
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 9433976a..2a5f27df 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -28,6 +28,7 @@
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <boost/algorithm/string.hpp>
@@ -111,7 +112,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
- typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
@@ -161,13 +162,16 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
+ m_resolver.warnVariablesNamedLikeInstructions();
+
// Errors created in this stage are completely ignored because we do not yet know
// the type and size of external identifiers, which would result in false errors.
// 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) {
+ ErrorList errors;
+ ErrorReporter errorsIgnored(errors);
+ julia::ExternalIdentifierAccess::Resolver resolver =
+ [&](assembly::Identifier const& _identifier, julia::IdentifierContext, bool _crossesFunctionBoundary) {
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");
@@ -186,6 +190,12 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
}
if (declarations.size() != 1)
return size_t(-1);
+ if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
+ if (var->isLocalVariable() && _crossesFunctionBoundary)
+ {
+ declarationError(_identifier.location, "Cannot access local Solidity variables from inside an inline assembly function.");
+ return size_t(-1);
+ }
_inlineAssembly.annotation().externalReferences[&_identifier].isSlot = isSlot;
_inlineAssembly.annotation().externalReferences[&_identifier].isOffset = isOffset;
_inlineAssembly.annotation().externalReferences[&_identifier].declaration = declarations.front();
@@ -194,7 +204,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;
}
@@ -303,29 +313,25 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
{
- auto err = make_shared<Error>(Error::Type::TypeError);
- *err << errinfo_sourceLocation(_location) << errinfo_comment(_description);
m_errorOccurred = true;
- m_errors.push_back(err);
+ m_errorReporter.typeError(_location, _description);
}
void ReferencesResolver::fatalTypeError(SourceLocation const& _location, string const& _description)
{
- typeError(_location, _description);
- BOOST_THROW_EXCEPTION(FatalError());
+ m_errorOccurred = true;
+ m_errorReporter.fatalTypeError(_location, _description);
}
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
{
- auto err = make_shared<Error>(Error::Type::DeclarationError);
- *err << errinfo_sourceLocation(_location) << errinfo_comment(_description);
m_errorOccurred = true;
- m_errors.push_back(err);
+ m_errorReporter.declarationError(_location, _description);
}
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{
- declarationError(_location, _description);
- BOOST_THROW_EXCEPTION(FatalError());
+ m_errorOccurred = true;
+ m_errorReporter.fatalDeclarationError(_location, _description);
}
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index dce343d3..fef2e73f 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -33,6 +33,7 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
class NameAndTypeResolver;
/**
@@ -43,11 +44,11 @@ class ReferencesResolver: private ASTConstVisitor
{
public:
ReferencesResolver(
- ErrorList& _errors,
+ ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver,
bool _resolveInsideCode = false
):
- m_errors(_errors),
+ m_errorReporter(_errorReporter),
m_resolver(_resolver),
m_resolveInsideCode(_resolveInsideCode)
{}
@@ -74,16 +75,16 @@ private:
/// Adds a new error to the list of errors.
void typeError(SourceLocation const& _location, std::string const& _description);
- /// Adds a new error to the list of errors and throws to abort type checking.
+ /// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors.
- void declarationError(const SourceLocation& _location, std::string const& _description);
+ void declarationError(SourceLocation const& _location, std::string const& _description);
- /// Adds a new error to the list of errors and throws to abort type checking.
- void fatalDeclarationError(const SourceLocation& _location, std::string const& _description);
+ /// Adds a new error to the list of errors and throws to abort reference resolving.
+ void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
/// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 369376fa..b1b31163 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -21,18 +21,18 @@
*/
#include <libsolidity/analysis/StaticAnalyzer.h>
-#include <memory>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/ErrorReporter.h>
+#include <memory>
using namespace std;
using namespace dev;
using namespace dev::solidity;
-
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
- return Error::containsOnlyWarnings(m_errors);
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool StaticAnalyzer::visit(ContractDefinition const& _contract)
@@ -63,7 +63,7 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
m_nonPayablePublic = false;
for (auto const& var: m_localVarUseCount)
if (var.second == 0)
- warning(var.first->location(), "Unused local variable");
+ m_errorReporter.warning(var.first->location(), "Unused local variable");
m_localVarUseCount.clear();
}
@@ -105,7 +105,11 @@ bool StaticAnalyzer::visit(Return const& _return)
bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
{
if (_statement.expression().annotation().isPure)
- warning(_statement.location(), "Statement has no effect.");
+ m_errorReporter.warning(
+ _statement.location(),
+ "Statement has no effect."
+ );
+
return true;
}
@@ -114,17 +118,36 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
if (m_nonPayablePublic && !m_library)
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
- warning(_memberAccess.location(), "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?");
+ m_errorReporter.warning(
+ _memberAccess.location(),
+ "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?"
+ );
+
+ if (_memberAccess.memberName() == "callcode")
+ if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ if (type->kind() == FunctionType::Kind::BareCallCode)
+ m_errorReporter.warning(
+ _memberAccess.location(),
+ "\"callcode\" has been deprecated in favour of \"delegatecall\"."
+ );
return true;
}
-void StaticAnalyzer::warning(SourceLocation const& _location, string const& _description)
+bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly)
{
- auto err = make_shared<Error>(Error::Type::Warning);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
+ if (!m_currentFunction)
+ return true;
- m_errors.push_back(err);
+ 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..cd6913b5 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -44,15 +44,13 @@ class StaticAnalyzer: private ASTConstVisitor
{
public:
/// @param _errors the reference to the list of errors and warnings to add them found during static analysis.
- explicit StaticAnalyzer(ErrorList& _errors): m_errors(_errors) {}
+ explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
/// Performs static analysis on the given source unit and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool analyze(SourceUnit const& _sourceUnit);
private:
- /// Adds a new warning to the list of errors.
- void warning(SourceLocation const& _location, std::string const& _description);
virtual bool visit(ContractDefinition const& _contract) override;
virtual void endVisit(ContractDefinition const& _contract) override;
@@ -65,8 +63,9 @@ 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;
+ ErrorReporter& m_errorReporter;
/// Flag that indicates whether the current contract definition is a library.
bool m_library = false;
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 94e82a87..02e2fdcf 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -19,6 +19,7 @@
#include <memory>
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
using namespace std;
@@ -29,27 +30,7 @@ using namespace dev::solidity;
bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
- return Error::containsOnlyWarnings(m_errors);
-}
-
-void SyntaxChecker::warning(SourceLocation const& _location, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::Warning);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
-}
-
-void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::SyntaxError);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool SyntaxChecker::visit(SourceUnit const&)
@@ -74,11 +55,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
to_string(recommendedVersion.patch());
string(";\"");
- auto err = make_shared<Error>(Error::Type::Warning);
- *err <<
- errinfo_sourceLocation(_sourceUnit.location()) <<
- errinfo_comment(errorString);
- m_errors.push_back(err);
+ m_errorReporter.warning(_sourceUnit.location(), errorString);
}
}
@@ -87,7 +64,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
solAssert(!_pragma.tokens().empty(), "");
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity")
- syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
+ m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
else
{
vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
@@ -96,7 +73,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
auto matchExpression = parser.parse();
SemVerVersion currentVersion{string(VersionString)};
if (!matchExpression.matches(currentVersion))
- syntaxError(
+ m_errorReporter.syntaxError(
_pragma.location(),
"Source file requires different compiler version (current compiler is " +
string(VersionString) + " - note that nightly builds are considered to be "
@@ -116,7 +93,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
{
if (!m_placeholderFound)
- syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
+ m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
m_placeholderFound = false;
}
@@ -146,7 +123,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
{
if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error
- syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
+ m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
return true;
}
@@ -154,14 +131,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
{
if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error
- syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
+ m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
return true;
}
bool SyntaxChecker::visit(UnaryOperation const& _operation)
{
if (_operation.getOperator() == Token::Add)
- warning(_operation.location(), "Use of unary + is deprecated.");
+ m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated.");
return true;
}
@@ -171,3 +148,15 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
return true;
}
+bool SyntaxChecker::visit(FunctionTypeName const& _node)
+{
+ for (auto const& decl: _node.parameterTypeList()->parameters())
+ if (!decl->name().empty())
+ m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated.");
+
+ for (auto const& decl: _node.returnParameterTypeList()->parameters())
+ if (!decl->name().empty())
+ m_errorReporter.warning(decl->location(), "Naming function type return parameters is deprecated.");
+
+ return true;
+}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 8d7dcdd3..ec6ac434 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -38,14 +38,11 @@ class SyntaxChecker: private ASTConstVisitor
{
public:
/// @param _errors the reference to the list of errors and warnings to add them found during type checking.
- SyntaxChecker(ErrorList& _errors): m_errors(_errors) {}
+ SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool checkSyntax(ASTNode const& _astRoot);
private:
- /// Adds a new error to the list of errors.
- void warning(SourceLocation const& _location, std::string const& _description);
- void syntaxError(SourceLocation const& _location, std::string const& _description);
virtual bool visit(SourceUnit const& _sourceUnit) override;
virtual void endVisit(SourceUnit const& _sourceUnit) override;
@@ -66,7 +63,9 @@ private:
virtual bool visit(PlaceholderStatement const& _placeholderStatement) override;
- ErrorList& m_errors;
+ virtual bool visit(FunctionTypeName const& _node) override;
+
+ ErrorReporter& m_errorReporter;
/// Flag that indicates whether a function modifier actually contains '_'.
bool m_placeholderFound = false;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 38cdc1f8..1563467c 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -22,11 +22,13 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <memory>
+#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/interface/ErrorReporter.h>
using namespace std;
using namespace dev;
@@ -43,10 +45,10 @@ bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
{
// We got a fatal error which required to stop further type checking, but we can
// continue normally from here.
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
}
- return Error::containsOnlyWarnings(m_errors);
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
}
TypePointer const& TypeChecker::type(Expression const& _expression) const
@@ -81,11 +83,11 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
if (function)
{
if (!function->returnParameters().empty())
- typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
+ m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (function->isDeclaredConst())
- typeError(function->location(), "Constructor cannot be defined as constant.");
+ m_errorReporter.typeError(function->location(), "Constructor cannot be defined as constant.");
if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal)
- typeError(function->location(), "Constructor must be public or internal.");
+ m_errorReporter.typeError(function->location(), "Constructor must be public or internal.");
}
FunctionDefinition const* fallbackFunction = nullptr;
@@ -95,21 +97,19 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
{
if (fallbackFunction)
{
- auto err = make_shared<Error>(Error::Type::DeclarationError);
- *err << errinfo_comment("Only one fallback function is allowed.");
- m_errors.push_back(err);
+ m_errorReporter.declarationError(function->location(), "Only one fallback function is allowed.");
}
else
{
fallbackFunction = function;
if (_contract.isLibrary())
- typeError(fallbackFunction->location(), "Libraries cannot have fallback functions.");
+ m_errorReporter.typeError(fallbackFunction->location(), "Libraries cannot have fallback functions.");
if (fallbackFunction->isDeclaredConst())
- typeError(fallbackFunction->location(), "Fallback function cannot be declared constant.");
+ m_errorReporter.typeError(fallbackFunction->location(), "Fallback function cannot be declared constant.");
if (!fallbackFunction->parameters().empty())
- typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
+ m_errorReporter.typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
if (!fallbackFunction->returnParameters().empty())
- typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
+ m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values.");
}
}
if (!function->isImplemented())
@@ -127,7 +127,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
{
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
- typeError(
+ m_errorReporter.typeError(
_contract.location(),
string("Function signature hash collision for ") + it.second->externalSignature()
);
@@ -156,12 +156,11 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
for (; it != functions[_contract.name()].end(); ++it)
ssl.append("Another declaration is here:", (*it)->location());
- auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
- *err <<
- errinfo_sourceLocation(functions[_contract.name()].front()->location()) <<
- errinfo_comment("More than one constructor defined.") <<
- errinfo_secondarySourceLocation(ssl);
- m_errors.push_back(err);
+ m_errorReporter.declarationError(
+ functions[_contract.name()].front()->location(),
+ ssl,
+ "More than one constructor defined."
+ );
}
for (auto const& it: functions)
{
@@ -170,13 +169,14 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
for (size_t j = i + 1; j < overloads.size(); ++j)
if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j])))
{
- auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
- *err <<
- errinfo_sourceLocation(overloads[j]->location()) <<
- errinfo_comment("Function with same name and arguments defined twice.") <<
- errinfo_secondarySourceLocation(SecondarySourceLocation().append(
- "Other declaration is here:", overloads[i]->location()));
- m_errors.push_back(err);
+ m_errorReporter.declarationError(
+ overloads[j]->location(),
+ SecondarySourceLocation().append(
+ "Other declaration is here:",
+ overloads[i]->location()
+ ),
+ "Function with same name and arguments defined twice."
+ );
}
}
}
@@ -213,7 +213,7 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
else if (it->second)
{
if (!function->isImplemented())
- typeError(function->location(), "Redeclaring an already implemented function as abstract");
+ m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract");
}
else if (function->isImplemented())
it->second = true;
@@ -285,7 +285,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
continue; // constructors can neither be overridden nor override anything
string const& name = function->name();
if (modifiers.count(name))
- typeError(modifiers[name]->location(), "Override changes function to modifier.");
+ m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
FunctionType functionType(*function);
// function should not change the return type
for (FunctionDefinition const* overriding: functions[name])
@@ -299,7 +299,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
overriding->isPayable() != function->isPayable() ||
overridingType != functionType
)
- typeError(overriding->location(), "Override changes extended function signature.");
+ m_errorReporter.typeError(overriding->location(), "Override changes extended function signature.");
}
functions[name].push_back(function);
}
@@ -310,9 +310,9 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
if (!override)
override = modifier;
else if (ModifierType(*override) != ModifierType(*modifier))
- typeError(override->location(), "Override changes modifier signature.");
+ m_errorReporter.typeError(override->location(), "Override changes modifier signature.");
if (!functions[name].empty())
- typeError(override->location(), "Override changes modifier to function.");
+ m_errorReporter.typeError(override->location(), "Override changes modifier to function.");
}
}
}
@@ -347,7 +347,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
for (size_t i = 0; i < it.second.size(); ++i)
for (size_t j = i + 1; j < it.second.size(); ++j)
if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second))
- typeError(
+ m_errorReporter.typeError(
it.second[j].first->location(),
"Function overload clash during conversion to external types for arguments."
);
@@ -357,11 +357,40 @@ void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
{
solAssert(_contract.isLibrary(), "");
if (!_contract.baseContracts().empty())
- typeError(_contract.location(), "Library is not allowed to inherit.");
+ m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
for (auto const& var: _contract.stateVariables())
if (!var->isConstant())
- typeError(var->location(), "Library cannot have non-constant state variables");
+ m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
+}
+
+void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
+{
+ TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide()));
+ TupleType const& rhs = dynamic_cast<TupleType const&>(*type(_assignment.rightHandSide()));
+
+ bool fillRight = !lhs.components().empty() && (!lhs.components().back() || lhs.components().front());
+ size_t storageToStorageCopies = 0;
+ size_t toStorageCopies = 0;
+ for (size_t i = 0; i < lhs.components().size(); ++i)
+ {
+ ReferenceType const* ref = dynamic_cast<ReferenceType const*>(lhs.components()[i].get());
+ if (!ref || !ref->dataStoredIn(DataLocation::Storage) || ref->isPointer())
+ continue;
+ size_t rhsPos = fillRight ? i : rhs.components().size() - (lhs.components().size() - i);
+ solAssert(rhsPos < rhs.components().size(), "");
+ toStorageCopies++;
+ if (rhs.components()[rhsPos]->dataStoredIn(DataLocation::Storage))
+ storageToStorageCopies++;
+ }
+ if (storageToStorageCopies >= 1 && toStorageCopies >= 2)
+ m_errorReporter.warning(
+ _assignment.location(),
+ "This assignment performs two copies to storage. Since storage copies do not first "
+ "copy to a temporary location, one of them might be overwritten before the second "
+ "is executed and thus may have unexpected effects. It is safer to perform the copies "
+ "separately or assign to storage pointers first."
+ );
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
@@ -370,16 +399,16 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
solAssert(base, "Base contract not available.");
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- typeError(_inheritance.location(), "Interfaces cannot inherit.");
+ m_errorReporter.typeError(_inheritance.location(), "Interfaces cannot inherit.");
if (base->isLibrary())
- typeError(_inheritance.location(), "Libraries cannot be inherited from.");
+ m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from.");
auto const& arguments = _inheritance.arguments();
TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
if (!arguments.empty() && parameterTypes.size() != arguments.size())
{
- typeError(
+ m_errorReporter.typeError(
_inheritance.location(),
"Wrong argument count for constructor call: " +
toString(arguments.size()) +
@@ -392,7 +421,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
for (size_t i = 0; i < arguments.size(); ++i)
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
- typeError(
+ m_errorReporter.typeError(
arguments[i]->location(),
"Invalid type for argument in constructor call. "
"Invalid implicit conversion from " +
@@ -409,17 +438,17 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
_usingFor.libraryName().annotation().referencedDeclaration
);
if (!library || !library->isLibrary())
- typeError(_usingFor.libraryName().location(), "Library name expected.");
+ m_errorReporter.typeError(_usingFor.libraryName().location(), "Library name expected.");
}
bool TypeChecker::visit(StructDefinition const& _struct)
{
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- typeError(_struct.location(), "Structs cannot be defined in interfaces.");
+ m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces.");
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
- typeError(member->location(), "Type cannot be used in struct.");
+ m_errorReporter.typeError(member->location(), "Type cannot be used in struct.");
// Check recursion, fatal error if detected.
using StructPointer = StructDefinition const*;
@@ -427,7 +456,7 @@ bool TypeChecker::visit(StructDefinition const& _struct)
function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
{
if (_parents.count(_struct))
- fatalTypeError(_struct->location(), "Recursive struct definition.");
+ m_errorReporter.fatalTypeError(_struct->location(), "Recursive struct definition.");
StructPointersSet parents = _parents;
parents.insert(_struct);
for (ASTPointer<VariableDeclaration> const& member: _struct->members())
@@ -452,34 +481,49 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isPayable())
{
if (isLibraryFunction)
- typeError(_function.location(), "Library functions cannot be payable.");
+ m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
- typeError(_function.location(), "Internal functions cannot be payable.");
+ m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
if (_function.isDeclaredConst())
- typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
+ m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (!type(*var)->canLiveOutsideStorage())
- typeError(var->location(), "Type is required to live outside storage.");
+ m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
- fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
+ m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
+
+ var->accept(*this);
}
+ set<Declaration const*> modifiers;
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
+ {
visitManually(
*modifier,
_function.isConstructor() ?
dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
vector<ContractDefinition const*>()
);
+ Declaration const* decl = &dereference(*modifier->name());
+ if (modifiers.count(decl))
+ {
+ if (dynamic_cast<ContractDefinition const*>(decl))
+ m_errorReporter.declarationError(modifier->location(), "Base constructor already provided.");
+ else
+ m_errorReporter.declarationError(modifier->location(), "Modifier already used for this function.");
+ }
+ else
+ modifiers.insert(decl);
+ }
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
{
if (_function.isImplemented())
- typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
+ m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
if (_function.visibility() < FunctionDefinition::Visibility::Public)
- typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
+ m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
if (_function.isConstructor())
- typeError(_function.location(), "Constructor cannot be defined in interfaces.");
+ m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces.");
}
if (_function.isImplemented())
_function.body().accept(*this);
@@ -488,8 +532,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
- if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- typeError(_variable.location(), "Variables cannot be declared in interfaces.");
+ // Forbid any variable declarations inside interfaces unless they are part of
+ // a function's input/output parameters.
+ if (
+ m_scope->contractKind() == ContractDefinition::ContractKind::Interface
+ && !_variable.isCallableParameter()
+ )
+ m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
// Variables can be declared without type (with "var"), in which case the first assignment
// sets the type.
@@ -505,19 +554,19 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (_variable.isConstant())
{
if (!_variable.isStateVariable())
- typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
+ m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
if (!_variable.type()->isValueType())
{
bool allowed = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(_variable.type().get()))
allowed = arrayType->isString();
if (!allowed)
- typeError(_variable.location(), "Constants of non-value type not yet implemented.");
+ m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented.");
}
if (!_variable.value())
- typeError(_variable.location(), "Uninitialized \"constant\" variable.");
+ m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable.");
else if (!_variable.value()->annotation().isPure)
- warning(
+ m_errorReporter.warning(
_variable.value()->location(),
"Initial value for constant variable has to be compile-time constant. "
"This will fail to compile with the next breaking version change."
@@ -527,20 +576,20 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
{
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
if (!varType->canLiveOutsideStorage())
- typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage.");
+ m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage.");
}
else if (
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
!FunctionType(_variable).interfaceFunctionType()
)
- typeError(_variable.location(), "Internal type is not allowed for public state variables.");
+ m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables.");
return false;
}
bool TypeChecker::visit(EnumDefinition const& _enum)
{
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- typeError(_enum.location(), "Enumerable cannot be declared in interfaces.");
+ m_errorReporter.typeError(_enum.location(), "Enumerable cannot be declared in interfaces.");
return false;
}
@@ -572,12 +621,12 @@ void TypeChecker::visitManually(
}
if (!parameters)
{
- typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class.");
+ m_errorReporter.typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class.");
return;
}
if (parameters->size() != arguments.size())
{
- typeError(
+ m_errorReporter.typeError(
_modifier.location(),
"Wrong argument count for modifier invocation: " +
toString(arguments.size()) +
@@ -589,7 +638,7 @@ void TypeChecker::visitManually(
}
for (size_t i = 0; i < _modifier.arguments().size(); ++i)
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])))
- typeError(
+ m_errorReporter.typeError(
arguments[i]->location(),
"Invalid type for argument in modifier invocation. "
"Invalid implicit conversion from " +
@@ -608,13 +657,13 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
if (var->isIndexed())
numIndexed++;
if (_eventDef.isAnonymous() && numIndexed > 4)
- typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
+ m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
else if (!_eventDef.isAnonymous() && numIndexed > 3)
- typeError(_eventDef.location(), "More than 3 indexed arguments for event.");
+ m_errorReporter.typeError(_eventDef.location(), "More than 3 indexed arguments for event.");
if (!type(*var)->canLiveOutsideStorage())
- typeError(var->location(), "Type is required to live outside storage.");
+ m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (!type(*var)->interfaceType(false))
- typeError(var->location(), "Internal type is not allowed as event parameter type.");
+ m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type.");
}
return false;
}
@@ -624,16 +673,17 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
if (fun.kind() == FunctionType::Kind::External)
if (!fun.canBeUsedExternally(false))
- typeError(_funType.location(), "External function type uses internal types.");
+ m_errorReporter.typeError(_funType.location(), "External function type uses internal types.");
}
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,
+ bool
)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
@@ -647,43 +697,43 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
{
- typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
+ m_errorReporter.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.");
+ m_errorReporter.typeError(_identifier.location, "Storage variables cannot be assigned to.");
return size_t(-1);
}
}
else if (var->isConstant())
{
- typeError(_identifier.location, "Constant variables not supported by inline assembly.");
+ m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly.");
return size_t(-1);
}
else if (!var->isLocalVariable())
{
- typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
+ m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
return size_t(-1);
}
else if (var->type()->dataStoredIn(DataLocation::Storage))
{
- typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables.");
+ m_errorReporter.typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables.");
return size_t(-1);
}
else if (var->type()->sizeOnStack() != 1)
{
- typeError(_identifier.location, "Only types that use one stack slot are supported.");
+ m_errorReporter.typeError(_identifier.location, "Only types that use one stack slot are supported.");
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.");
+ m_errorReporter.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))
@@ -696,7 +746,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
if (!contract->isLibrary())
{
- typeError(_identifier.location, "Expected a library.");
+ m_errorReporter.typeError(_identifier.location, "Expected a library.");
return size_t(-1);
}
}
@@ -710,7 +760,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
- m_errors,
+ m_errorReporter,
+ false,
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@@ -753,7 +804,7 @@ void TypeChecker::endVisit(Return const& _return)
ParameterList const* params = _return.annotation().functionReturnParameters;
if (!params)
{
- typeError(_return.location(), "Return arguments not allowed.");
+ m_errorReporter.typeError(_return.location(), "Return arguments not allowed.");
return;
}
TypePointers returnTypes;
@@ -762,9 +813,9 @@ void TypeChecker::endVisit(Return const& _return)
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get()))
{
if (tupleType->components().size() != params->parameters().size())
- typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
+ m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)))
- typeError(
+ m_errorReporter.typeError(
_return.expression()->location(),
"Return argument type " +
type(*_return.expression())->toString() +
@@ -774,12 +825,12 @@ void TypeChecker::endVisit(Return const& _return)
);
}
else if (params->parameters().size() != 1)
- typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
+ m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
else
{
TypePointer const& expected = type(*params->parameters().front());
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
- typeError(
+ m_errorReporter.typeError(
_return.expression()->location(),
"Return argument type " +
type(*_return.expression())->toString() +
@@ -796,20 +847,20 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
// No initial value is only permitted for single variables with specified type.
if (_statement.declarations().size() != 1 || !_statement.declarations().front())
- fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
+ m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
VariableDeclaration const& varDecl = *_statement.declarations().front();
if (!varDecl.annotation().type)
- fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
+ m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
{
if (ref->dataStoredIn(DataLocation::Storage))
- warning(
+ m_errorReporter.warning(
varDecl.location(),
"Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"
);
}
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
- typeError(
+ m_errorReporter.typeError(
varDecl.location(),
"Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable."
);
@@ -835,7 +886,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (variables.empty())
{
if (!valueTypes.empty())
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_statement.location(),
"Too many components (" +
toString(valueTypes.size()) +
@@ -843,7 +894,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
);
}
else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_statement.location(),
"Wildcard both at beginning and end of variable declaration list is only allowed "
"if the number of components is equal."
@@ -852,7 +903,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (!variables.empty() && (!variables.back() || !variables.front()))
--minNumValues;
if (valueTypes.size() < minNumValues)
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_statement.location(),
"Not enough components (" +
toString(valueTypes.size()) +
@@ -860,7 +911,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
toString(minNumValues) + ")."
);
if (valueTypes.size() > variables.size() && variables.front() && variables.back())
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_statement.location(),
"Too many components (" +
toString(valueTypes.size()) +
@@ -891,7 +942,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (!var.annotation().type)
{
if (valueComponentType->category() == Type::Category::RationalNumber)
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_statement.initialValue()->location(),
"Invalid rational " +
valueComponentType->toString() +
@@ -901,10 +952,42 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
solAssert(false, "");
}
else if (*var.annotation().type == TupleType())
- typeError(
+ m_errorReporter.typeError(
var.location(),
"Cannot declare variable with void (empty tuple) type."
);
+ else if (valueComponentType->category() == Type::Category::RationalNumber)
+ {
+ string typeName = var.annotation().type->toString(true);
+ string extension;
+ if (auto type = dynamic_cast<IntegerType const*>(var.annotation().type.get()))
+ {
+ int numBits = type->numBits();
+ bool isSigned = type->isSigned();
+ string minValue;
+ string maxValue;
+ if (isSigned)
+ {
+ numBits--;
+ minValue = "-" + bigint(bigint(1) << numBits).str();
+ }
+ else
+ minValue = "0";
+ maxValue = bigint((bigint(1) << numBits) - 1).str();
+ extension = ", which can hold values between " + minValue + " and " + maxValue;
+ }
+ else
+ solAssert(dynamic_cast<FixedPointType const*>(var.annotation().type.get()), "Unknown type.");
+
+ m_errorReporter.warning(
+ _statement.location(),
+ "The type of this variable was inferred as " +
+ typeName +
+ extension +
+ ". This is probably not desired. Use an explicit type to silence this warning."
+ );
+ }
+
var.accept(*this);
}
else
@@ -917,7 +1000,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
valueComponentType->mobileType()
)
- typeError(
+ m_errorReporter.typeError(
_statement.location(),
"Type " +
valueComponentType->toString() +
@@ -928,7 +1011,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
" or use an explicit conversion."
);
else
- typeError(
+ m_errorReporter.typeError(
_statement.location(),
"Type " +
valueComponentType->toString() +
@@ -946,7 +1029,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
{
if (type(_statement.expression())->category() == Type::Category::RationalNumber)
if (!dynamic_cast<RationalNumberType const&>(*type(_statement.expression())).mobileType())
- typeError(_statement.expression().location(), "Invalid rational number.");
+ m_errorReporter.typeError(_statement.expression().location(), "Invalid rational number.");
if (auto call = dynamic_cast<FunctionCall const*>(&_statement.expression()))
{
@@ -958,9 +1041,9 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
kind == FunctionType::Kind::BareCallCode ||
kind == FunctionType::Kind::BareDelegateCall
)
- warning(_statement.location(), "Return value of low-level calls not used.");
+ m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
else if (kind == FunctionType::Kind::Send)
- warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead.");
+ m_errorReporter.warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead.");
}
}
}
@@ -975,14 +1058,14 @@ bool TypeChecker::visit(Conditional const& _conditional)
TypePointer trueType = type(_conditional.trueExpression())->mobileType();
TypePointer falseType = type(_conditional.falseExpression())->mobileType();
if (!trueType)
- fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type.");
+ m_errorReporter.fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type.");
if (!falseType)
- fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type.");
+ m_errorReporter.fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type.");
TypePointer commonType = Type::commonType(trueType, falseType);
if (!commonType)
{
- typeError(
+ m_errorReporter.typeError(
_conditional.location(),
"True expression's type " +
trueType->toString() +
@@ -1002,7 +1085,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
_conditional.falseExpression().annotation().isPure;
if (_conditional.annotation().lValueRequested)
- typeError(
+ m_errorReporter.typeError(
_conditional.location(),
"Conditional expression as left value is not supported yet."
);
@@ -1018,17 +1101,19 @@ bool TypeChecker::visit(Assignment const& _assignment)
if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get()))
{
if (_assignment.assignmentOperator() != Token::Assign)
- typeError(
+ m_errorReporter.typeError(
_assignment.location(),
"Compound assignment is not allowed for tuple types."
);
// Sequenced assignments of tuples is not valid, make the result a "void" type.
_assignment.annotation().type = make_shared<TupleType>();
expectType(_assignment.rightHandSide(), *tupleType);
+
+ checkDoubleStorageAssignment(_assignment);
}
else if (t->category() == Type::Category::Mapping)
{
- typeError(_assignment.location(), "Mappings cannot be assigned to.");
+ m_errorReporter.typeError(_assignment.location(), "Mappings cannot be assigned to.");
_assignment.rightHandSide().accept(*this);
}
else if (_assignment.assignmentOperator() == Token::Assign)
@@ -1042,7 +1127,7 @@ bool TypeChecker::visit(Assignment const& _assignment)
type(_assignment.rightHandSide())
);
if (!resultType || *resultType != *t)
- typeError(
+ m_errorReporter.typeError(
_assignment.location(),
"Operator " +
string(Token::toString(_assignment.assignmentOperator())) +
@@ -1063,7 +1148,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (_tuple.annotation().lValueRequested)
{
if (_tuple.isInlineArray())
- fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
+ m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
for (auto const& component: components)
if (component)
{
@@ -1087,7 +1172,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
// Outside of an lvalue-context, the only situation where a component can be empty is (x,).
if (!components[i] && !(i == 1 && components.size() == 2))
- fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
+ m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
else if (components[i])
{
components[i]->accept(*this);
@@ -1097,7 +1182,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
{
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
- fatalTypeError(components[i]->location(), "Invalid mobile type.");
+ m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type.");
if (i == 0)
inlineArrayType = types[i]->mobileType();
@@ -1114,7 +1199,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
{
if (!inlineArrayType)
- fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
+ m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
}
else
@@ -1146,7 +1231,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op);
if (!t)
{
- typeError(
+ m_errorReporter.typeError(
_operation.location(),
"Unary operator " +
string(Token::toString(op)) +
@@ -1167,7 +1252,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
if (!commonType)
{
- typeError(
+ m_errorReporter.typeError(
_operation.location(),
"Operator " +
string(Token::toString(_operation.getOperator())) +
@@ -1200,7 +1285,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
commonType->category() == Type::Category::FixedPoint &&
dynamic_cast<FixedPointType const&>(*commonType).numBits() != 256
))
- warning(
+ m_errorReporter.warning(
_operation.location(),
"Result of exponentiation has type " + commonType->toString() + " and thus "
"might overflow. Silence this warning by converting the literal to the "
@@ -1238,20 +1323,24 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get()))
{
- _functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct);
- _functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall;
+ if (typeType->actualType()->category() == Type::Category::Struct)
+ _functionCall.annotation().kind = FunctionCallKind::StructConstructorCall;
+ else
+ _functionCall.annotation().kind = FunctionCallKind::TypeConversion;
+
}
else
- _functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false;
+ _functionCall.annotation().kind = FunctionCallKind::FunctionCall;
+ solAssert(_functionCall.annotation().kind != FunctionCallKind::Unset, "");
- if (_functionCall.annotation().isTypeConversion)
+ if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
{
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
TypePointer resultType = t.actualType();
if (arguments.size() != 1)
- typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion.");
+ m_errorReporter.typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion.");
else if (!isPositionalCall)
- typeError(_functionCall.location(), "Type conversion cannot allow named arguments.");
+ m_errorReporter.typeError(_functionCall.location(), "Type conversion cannot allow named arguments.");
else
{
TypePointer const& argType = type(*arguments.front());
@@ -1260,7 +1349,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
// (data location cannot yet be specified for type conversions)
resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType))
- typeError(_functionCall.location(), "Explicit type conversion not allowed.");
+ m_errorReporter.typeError(_functionCall.location(), "Explicit type conversion not allowed.");
}
_functionCall.annotation().type = resultType;
_functionCall.annotation().isPure = isPure;
@@ -1274,7 +1363,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
/// For error message: Struct members that were removed during conversion to memory.
set<string> membersRemovedForStructConstructor;
- if (_functionCall.annotation().isStructConstructorCall)
+ if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
@@ -1282,18 +1371,15 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
membersRemovedForStructConstructor = structType.membersMissingInMemory();
_functionCall.annotation().isPure = isPure;
}
- else
- {
- functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
+ else if ((functionType = dynamic_pointer_cast<FunctionType const>(expressionType)))
_functionCall.annotation().isPure =
isPure &&
_functionCall.expression().annotation().isPure &&
functionType->isPure();
- }
if (!functionType)
{
- typeError(_functionCall.location(), "Type is not callable");
+ m_errorReporter.typeError(_functionCall.location(), "Type is not callable");
_functionCall.annotation().type = make_shared<TupleType>();
return false;
}
@@ -1312,13 +1398,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
toString(parameterTypes.size()) +
".";
// Extend error message in case we try to construct a struct with mapping member.
- if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty())
+ if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall && !membersRemovedForStructConstructor.empty())
{
msg += " Members that have to be skipped in memory:";
for (auto const& member: membersRemovedForStructConstructor)
msg += " " + member;
}
- typeError(_functionCall.location(), msg);
+ m_errorReporter.typeError(_functionCall.location(), msg);
}
else if (isPositionalCall)
{
@@ -1330,10 +1416,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
{
if (auto t = dynamic_cast<RationalNumberType const*>(argType.get()))
if (!t->mobileType())
- typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
+ m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
- typeError(
+ m_errorReporter.typeError(
arguments[i]->location(),
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
@@ -1349,14 +1435,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
// call by named arguments
auto const& parameterNames = functionType->parameterNames();
if (functionType->takesArbitraryParameters())
- typeError(
+ m_errorReporter.typeError(
_functionCall.location(),
"Named arguments cannnot be used for functions that take arbitrary parameters."
);
else if (parameterNames.size() > argumentNames.size())
- typeError(_functionCall.location(), "Some argument names are missing.");
+ m_errorReporter.typeError(_functionCall.location(), "Some argument names are missing.");
else if (parameterNames.size() < argumentNames.size())
- typeError(_functionCall.location(), "Too many arguments.");
+ m_errorReporter.typeError(_functionCall.location(), "Too many arguments.");
else
{
// check duplicate names
@@ -1366,7 +1452,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (*argumentNames[i] == *argumentNames[j])
{
duplication = true;
- typeError(arguments[i]->location(), "Duplicate named argument.");
+ m_errorReporter.typeError(arguments[i]->location(), "Duplicate named argument.");
}
// check actual types
@@ -1380,7 +1466,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
found = true;
// check type convertible
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j]))
- typeError(
+ m_errorReporter.typeError(
arguments[i]->location(),
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
@@ -1393,7 +1479,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
if (!found)
- typeError(
+ m_errorReporter.typeError(
_functionCall.location(),
"Named argument does not match function declaration."
);
@@ -1414,11 +1500,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
auto contract = dynamic_cast<ContractDefinition const*>(&dereference(*contractName));
if (!contract)
- fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
+ m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (!contract->annotation().isFullyImplemented)
- typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
+ m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
if (!contract->constructorIsPublic())
- typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
+ m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly.");
solAssert(!!m_scope, "");
m_scope->annotation().contractDependencies.insert(contract);
@@ -1427,7 +1513,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
"Linearized base contracts not yet available."
);
if (contractDependenciesAreCyclic(*m_scope))
- typeError(
+ m_errorReporter.typeError(
_newExpression.location(),
"Circular reference for contract creation (cannot create instance of derived or same contract)."
);
@@ -1437,12 +1523,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
else if (type->category() == Type::Category::Array)
{
if (!type->canLiveOutsideStorage())
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_newExpression.typeName().location(),
"Type cannot live outside storage."
);
if (!type->isDynamicallySized())
- typeError(
+ m_errorReporter.typeError(
_newExpression.typeName().location(),
"Length has to be placed in parentheses after the array type for new expression."
);
@@ -1457,7 +1543,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
_newExpression.annotation().isPure = true;
}
else
- fatalTypeError(_newExpression.location(), "Contract or array type expected.");
+ m_errorReporter.fatalTypeError(_newExpression.location(), "Contract or array type expected.");
}
bool TypeChecker::visit(MemberAccess const& _memberAccess)
@@ -1488,13 +1574,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
exprType
);
if (!storageType->members(m_scope).membersByName(memberName).empty())
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" is not available in " +
exprType->toString() +
" outside of storage."
);
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not found or not visible "
"after argument-dependent lookup in " + exprType->toString() +
@@ -1502,7 +1588,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
);
}
else if (possibleMembers.size() > 1)
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_memberAccess.location(),
"Member \"" + memberName + "\" not unique "
"after argument-dependent lookup in " + exprType->toString() +
@@ -1515,7 +1601,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get()))
if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType()))
- typeError(
+ m_errorReporter.typeError(
_memberAccess.location(),
"Function \"" + memberName + "\" cannot be called on an object of type " +
exprType->toString() + " (expected " + funType->selfType()->toString() + ")"
@@ -1563,10 +1649,10 @@ bool TypeChecker::visit(IndexAccess const& _access)
{
ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
if (!index)
- typeError(_access.location(), "Index expression cannot be omitted.");
+ m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted.");
else if (actualType.isString())
{
- typeError(_access.location(), "Index access for string is not possible.");
+ m_errorReporter.typeError(_access.location(), "Index access for string is not possible.");
index->accept(*this);
}
else
@@ -1576,7 +1662,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
{
if (!numberType->isFractional()) // error is reported above
if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr))
- typeError(_access.location(), "Out of bounds array access.");
+ m_errorReporter.typeError(_access.location(), "Out of bounds array access.");
}
}
resultType = actualType.baseType();
@@ -1587,7 +1673,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
{
MappingType const& actualType = dynamic_cast<MappingType const&>(*baseType);
if (!index)
- typeError(_access.location(), "Index expression cannot be omitted.");
+ m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted.");
else
expectType(*index, *actualType.keyType());
resultType = actualType.valueType();
@@ -1609,7 +1695,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
length->literalValue(nullptr)
));
else
- fatalTypeError(index->location(), "Integer constant expected.");
+ m_errorReporter.fatalTypeError(index->location(), "Integer constant expected.");
}
break;
}
@@ -1617,20 +1703,20 @@ bool TypeChecker::visit(IndexAccess const& _access)
{
FixedBytesType const& bytesType = dynamic_cast<FixedBytesType const&>(*baseType);
if (!index)
- typeError(_access.location(), "Index expression cannot be omitted.");
+ m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted.");
else
{
expectType(*index, IntegerType(256));
if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
if (bytesType.numBytes() <= integerType->literalValue(nullptr))
- typeError(_access.location(), "Out of bounds array access.");
+ m_errorReporter.typeError(_access.location(), "Out of bounds array access.");
}
resultType = make_shared<FixedBytesType>(1);
isLValue = false; // @todo this heavily depends on how it is embedded
break;
}
default:
- fatalTypeError(
+ m_errorReporter.fatalTypeError(
_access.baseExpression().location(),
"Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")"
);
@@ -1660,14 +1746,14 @@ bool TypeChecker::visit(Identifier const& _identifier)
candidates.push_back(declaration);
}
if (candidates.empty())
- fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup.");
else if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front();
else
- fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup.");
}
else if (annotation.overloadedDeclarations.empty())
- fatalTypeError(_identifier.location(), "No candidates for overload resolution found.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "No candidates for overload resolution found.");
else if (annotation.overloadedDeclarations.size() == 1)
annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin();
else
@@ -1683,11 +1769,11 @@ bool TypeChecker::visit(Identifier const& _identifier)
candidates.push_back(declaration);
}
if (candidates.empty())
- fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
else if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front();
else
- fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
}
}
solAssert(
@@ -1697,7 +1783,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
annotation.isLValue = annotation.referencedDeclaration->isLValue();
annotation.type = annotation.referencedDeclaration->type();
if (!annotation.type)
- fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
+ m_errorReporter.fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration))
annotation.isPure = annotation.isConstant = variableDeclaration->isConstant();
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
@@ -1717,21 +1803,21 @@ void TypeChecker::endVisit(Literal const& _literal)
if (_literal.looksLikeAddress())
{
if (_literal.passesAddressChecksum())
- {
_literal.annotation().type = make_shared<IntegerType>(0, IntegerType::Modifier::Address);
- return;
- }
else
- warning(
+ m_errorReporter.warning(
_literal.location(),
"This looks like an address but has an invalid checksum. "
"If this is not used as an address, please prepend '00'."
);
}
- _literal.annotation().type = Type::forLiteral(_literal);
- _literal.annotation().isPure = true;
if (!_literal.annotation().type)
- fatalTypeError(_literal.location(), "Invalid literal value.");
+ _literal.annotation().type = Type::forLiteral(_literal);
+
+ if (!_literal.annotation().type)
+ m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
+
+ _literal.annotation().isPure = true;
}
bool TypeChecker::contractDependenciesAreCyclic(
@@ -1772,7 +1858,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
type(_expression)->mobileType()
)
- typeError(
+ m_errorReporter.typeError(
_expression.location(),
"Type " +
type(_expression)->toString() +
@@ -1783,7 +1869,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
" or use an explicit conversion."
);
else
- typeError(
+ m_errorReporter.typeError(
_expression.location(),
"Type " +
type(_expression)->toString() +
@@ -1791,7 +1877,23 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
_expectedType.toString() +
"."
);
- }
+ }
+
+ if (
+ type(_expression)->category() == Type::Category::RationalNumber &&
+ _expectedType.category() == Type::Category::FixedBytes
+ )
+ {
+ auto literal = dynamic_cast<Literal const*>(&_expression);
+
+ if (literal && !literal->isHexNumber())
+ m_errorReporter.warning(
+ _expression.location(),
+ "Decimal literal assigned to bytesXX variable will be left-aligned. "
+ "Use an explicit conversion to silence this warning."
+ );
+ }
+
}
void TypeChecker::requireLValue(Expression const& _expression)
@@ -1800,33 +1902,8 @@ void TypeChecker::requireLValue(Expression const& _expression)
_expression.accept(*this);
if (_expression.annotation().isConstant)
- typeError(_expression.location(), "Cannot assign to a constant variable.");
+ m_errorReporter.typeError(_expression.location(), "Cannot assign to a constant variable.");
else if (!_expression.annotation().isLValue)
- typeError(_expression.location(), "Expression has to be an lvalue.");
+ m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue.");
}
-void TypeChecker::typeError(SourceLocation const& _location, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::TypeError);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
-}
-
-void TypeChecker::warning(SourceLocation const& _location, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::Warning);
- *err <<
- errinfo_sourceLocation(_location) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
-}
-
-void TypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
-{
- typeError(_location, _description);
- BOOST_THROW_EXCEPTION(FatalError());
-}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 88559f44..ee43d13a 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -22,7 +22,6 @@
#pragma once
-#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
@@ -33,6 +32,7 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
/**
* The module that performs type analysis on the AST, checks the applicability of operations on
@@ -43,7 +43,7 @@ class TypeChecker: private ASTConstVisitor
{
public:
/// @param _errors the reference to the list of errors and warnings to add them found during type checking.
- TypeChecker(ErrorList& _errors): m_errors(_errors) {}
+ TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
/// Performs type checking on the given contract and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
@@ -56,14 +56,6 @@ public:
TypePointer const& type(VariableDeclaration const& _variable) const;
private:
- /// Adds a new error to the list of errors.
- void typeError(SourceLocation const& _location, std::string const& _description);
-
- /// Adds a new warning to the list of errors.
- void warning(SourceLocation const& _location, std::string const& _description);
-
- /// Adds a new error to the list of errors and throws to abort type checking.
- void fatalTypeError(SourceLocation const& _location, std::string const& _description);
virtual bool visit(ContractDefinition const& _contract) override;
/// Checks that two functions defined in this contract with the same name have different
@@ -77,6 +69,9 @@ private:
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
/// Checks that all requirements for a library are fulfilled if this is a library.
void checkLibraryRequirements(ContractDefinition const& _contract);
+ /// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage.
+ /// Should only be called if the left hand side is tuple-typed.
+ void checkDoubleStorageAssignment(Assignment const& _assignment);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
@@ -127,7 +122,7 @@ private:
ContractDefinition const* m_scope = nullptr;
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
};
}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 03112d2d..403f4b79 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -20,10 +20,8 @@
* Solidity abstract syntax tree.
*/
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
-#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/AST_accept.h>
#include <libdevcore/SHA3.h>
@@ -532,18 +530,26 @@ IdentifierAnnotation& Identifier::annotation() const
return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
}
+bool Literal::isHexNumber() const
+{
+ if (token() != Token::Number)
+ return false;
+ return boost::starts_with(value(), "0x");
+}
+
bool Literal::looksLikeAddress() const
{
if (subDenomination() != SubDenomination::None)
return false;
- string lit = value();
- return lit.substr(0, 2) == "0x" && abs(int(lit.length()) - 42) <= 1;
+ if (!isHexNumber())
+ return false;
+
+ return abs(int(value().length()) - 42) <= 1;
}
bool Literal::passesAddressChecksum() const
{
- string lit = value();
- solAssert(lit.substr(0, 2) == "0x", "Expected hex prefix");
- return dev::passesAddressChecksum(lit, true);
+ solAssert(isHexNumber(), "Expected hex number");
+ return dev::passesAddressChecksum(value(), true);
}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 02234ffc..e8831dc0 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -29,7 +29,6 @@
#include <boost/noncopyable.hpp>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/Types.h>
@@ -583,8 +582,7 @@ public:
bool isPayable() const { return m_isPayable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
- Block const& body() const { return *m_body; }
-
+ Block const& body() const { solAssert(m_body, ""); return *m_body; }
virtual bool isVisibleInContract() const override
{
return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
@@ -874,6 +872,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
{
@@ -1314,7 +1314,7 @@ private:
/**
* Tuple, parenthesized expression, or bracketed expression.
- * Examples: (1, 2), (x,), (x), (), [1, 2],
+ * Examples: (1, 2), (x,), (x), (), [1, 2],
* Individual components might be empty shared pointers (as in the second example).
* The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple
* Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple.
@@ -1327,8 +1327,8 @@ public:
std::vector<ASTPointer<Expression>> const& _components,
bool _isArray
):
- Expression(_location),
- m_components(_components),
+ Expression(_location),
+ m_components(_components),
m_isArray(_isArray) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@@ -1590,6 +1590,9 @@ public:
SubDenomination subDenomination() const { return m_subDenomination; }
+ /// @returns true if this is a number with a hex prefix.
+ bool isHexNumber() const;
+
/// @returns true if this looks like a checksummed address.
bool looksLikeAddress() const;
/// @returns true if it passes the address checksum test.
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index a7d89248..45a6dd1a 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -200,12 +200,17 @@ struct BinaryOperationAnnotation: ExpressionAnnotation
TypePointer commonType;
};
+enum class FunctionCallKind
+{
+ Unset,
+ FunctionCall,
+ TypeConversion,
+ StructConstructorCall
+};
+
struct FunctionCallAnnotation: ExpressionAnnotation
{
- /// Whether this is an explicit type conversion.
- bool isTypeConversion = false;
- /// Whether this is a struct constructor call.
- bool isStructConstructorCall = false;
+ FunctionCallKind kind = FunctionCallKind::Unset;
};
}
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 9ea23687..a90debb2 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -24,7 +24,8 @@
#include <boost/algorithm/string/join.hpp>
#include <libdevcore/UTF8.h>
#include <libsolidity/ast/AST.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmData.h>
+#include <libsolidity/inlineasm/AsmPrinter.h>
using namespace std;
@@ -33,49 +34,86 @@ namespace dev
namespace solidity
{
-void ASTJsonConverter::addJsonNode(
+ASTJsonConverter::ASTJsonConverter(bool _legacy, map<string, unsigned> _sourceIndices):
+ m_legacy(_legacy),
+ m_sourceIndices(_sourceIndices)
+{
+}
+
+
+void ASTJsonConverter::setJsonNode(
ASTNode const& _node,
string const& _nodeName,
- initializer_list<pair<string const, Json::Value const>> _attributes,
- bool _hasChildren = false
+ initializer_list<pair<string, Json::Value>>&& _attributes
)
{
- ASTJsonConverter::addJsonNode(
+ ASTJsonConverter::setJsonNode(
_node,
_nodeName,
- std::vector<pair<string const, Json::Value const>>(_attributes),
- _hasChildren
+ std::vector<pair<string, Json::Value>>(std::move(_attributes))
);
}
-
-void ASTJsonConverter::addJsonNode(
+
+void ASTJsonConverter::setJsonNode(
ASTNode const& _node,
- string const& _nodeName,
- std::vector<pair<string const, Json::Value const>> const& _attributes,
- bool _hasChildren = false
+ string const& _nodeType,
+ std::vector<pair<string, Json::Value>>&& _attributes
)
{
- Json::Value node;
-
- node["id"] = Json::UInt64(_node.id());
- node["src"] = sourceLocationToString(_node.location());
- node["name"] = _nodeName;
- if (_attributes.size() != 0)
+ m_currentValue = Json::objectValue;
+ m_currentValue["id"] = nodeId(_node);
+ m_currentValue["src"] = sourceLocationToString(_node.location());
+ if (!m_legacy)
{
- Json::Value attrs;
+ m_currentValue["nodeType"] = _nodeType;
for (auto& e: _attributes)
- attrs[e.first] = e.second;
- node["attributes"] = attrs;
+ m_currentValue[e.first] = std::move(e.second);
}
-
- m_jsonNodePtrs.top()->append(node);
-
- if (_hasChildren)
+ else
{
- Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
- Json::Value children(Json::arrayValue);
- addedNode["children"] = children;
- m_jsonNodePtrs.push(&addedNode["children"]);
+ m_currentValue["name"] = _nodeType;
+ Json::Value attrs(Json::objectValue);
+ if (
+ //these nodeTypes need to have a children-node even if it is empty
+ (_nodeType == "VariableDeclaration") ||
+ (_nodeType == "ParameterList") ||
+ (_nodeType == "Block") ||
+ (_nodeType == "InlineAssembly") ||
+ (_nodeType == "Throw")
+ )
+ {
+ Json::Value children(Json::arrayValue);
+ m_currentValue["children"] = children;
+ }
+
+ for (auto& e: _attributes)
+ {
+ if (
+ (!e.second.isNull()) &&
+ (
+ (e.second.isObject() && e.second.isMember("name")) ||
+ (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) ||
+ (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0]
+ )
+ )
+ {
+ if (e.second.isObject())
+ m_currentValue["children"].append(std::move(e.second));
+ if (e.second.isArray())
+ for (auto& child: e.second)
+ if (!child.isNull())
+ m_currentValue["children"].append(std::move(child));
+ }
+ else
+ {
+ if (e.first == "typeDescriptions")
+ attrs["type"] = Json::Value(e.second["typeString"]);
+ else
+ attrs[e.first] = std::move(e.second);
+ }
+ }
+ if (!attrs.empty())
+ m_currentValue["attributes"] = std::move(attrs);
}
}
@@ -90,34 +128,89 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location)
return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex);
}
-ASTJsonConverter::ASTJsonConverter(
- ASTNode const& _ast,
- map<string, unsigned> _sourceIndices
-): m_ast(&_ast), m_sourceIndices(_sourceIndices)
+string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath) const
+{
+ return boost::algorithm::join(_namePath, ".");
+}
+
+Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp)
{
+ Json::Value typeDescriptions(Json::objectValue);
+ typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString()) : Json::nullValue;
+ typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue;
+ return typeDescriptions;
+
+}
+Json::Value ASTJsonConverter::typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps)
+{
+ if (_tps)
+ {
+ Json::Value arguments(Json::arrayValue);
+ for (auto const& tp: *_tps)
+ arguments.append(typePointerToJson(tp));
+ return arguments;
+ }
+ else
+ return Json::nullValue;
}
-void ASTJsonConverter::print(ostream& _stream)
+void ASTJsonConverter::appendExpressionAttributes(
+ std::vector<pair<string, Json::Value>>& _attributes,
+ ExpressionAnnotation const& _annotation
+)
{
- process();
- _stream << m_astJson;
+ std::vector<pair<string, Json::Value>> exprAttributes = {
+ make_pair("typeDescriptions", typePointerToJson(_annotation.type)),
+ make_pair("isConstant", _annotation.isConstant),
+ make_pair("isPure", _annotation.isPure),
+ make_pair("isLValue", _annotation.isLValue),
+ make_pair("lValueRequested", _annotation.lValueRequested),
+ make_pair("argumentTypes", typePointerToJson(_annotation.argumentTypes))
+ };
+ _attributes += exprAttributes;
}
-Json::Value const& ASTJsonConverter::json()
+Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<assembly::Identifier const* ,InlineAssemblyAnnotation::ExternalIdentifierInfo> _info)
{
- process();
- return m_astJson;
+ Json::Value tuple(Json::objectValue);
+ tuple["src"] = sourceLocationToString(_info.first->location);
+ tuple["declaration"] = idOrNull(_info.second.declaration);
+ tuple["isSlot"] = Json::Value(_info.second.isSlot);
+ tuple["isOffset"] = Json::Value(_info.second.isOffset);
+ tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize));
+ return tuple;
}
-bool ASTJsonConverter::visit(SourceUnit const&)
+void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node)
{
- Json::Value children(Json::arrayValue);
+ _stream << toJson(_node);
+}
- m_astJson["name"] = "SourceUnit";
- m_astJson["children"] = children;
- m_jsonNodePtrs.push(&m_astJson["children"]);
+Json::Value ASTJsonConverter::toJson(ASTNode const& _node)
+{
+ _node.accept(*this);
+ return std::move(m_currentValue);
+}
- return true;
+bool ASTJsonConverter::visit(SourceUnit const& _node)
+{
+ Json::Value exportedSymbols = Json::objectValue;
+ for (auto const& sym: _node.annotation().exportedSymbols)
+ {
+ exportedSymbols[sym.first] = Json::arrayValue;
+ for (Declaration const* overload: sym.second)
+ exportedSymbols[sym.first].append(nodeId(*overload));
+ }
+ setJsonNode(
+ _node,
+ "SourceUnit",
+ {
+ make_pair("absolutePath", _node.annotation().path),
+ make_pair("exportedSymbols", move(exportedSymbols)),
+ make_pair("nodes", toJson(_node.nodes()))
+ }
+ );
+ return false;
}
bool ASTJsonConverter::visit(PragmaDirective const& _node)
@@ -125,556 +218,516 @@ bool ASTJsonConverter::visit(PragmaDirective const& _node)
Json::Value literals(Json::arrayValue);
for (auto const& literal: _node.literals())
literals.append(literal);
- addJsonNode(_node, "PragmaDirective", { make_pair("literals", literals) });
- return true;
+ setJsonNode( _node, "PragmaDirective", {
+ make_pair("literals", std::move(literals))
+ });
+ return false;
}
bool ASTJsonConverter::visit(ImportDirective const& _node)
{
- addJsonNode(_node, "ImportDirective", { make_pair("file", _node.path())});
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("file", _node.path()),
+ make_pair("absolutePath", _node.annotation().absolutePath),
+ make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
+ make_pair("scope", idOrNull(_node.scope()))
+ };
+ attributes.push_back(make_pair("unitAlias", _node.name()));
+ Json::Value symbolAliases(Json::arrayValue);
+ for (auto const& symbolAlias: _node.symbolAliases())
+ {
+ Json::Value tuple(Json::objectValue);
+ solAssert(symbolAlias.first, "");
+ tuple["foreign"] = nodeId(*symbolAlias.first);
+ tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue;
+ symbolAliases.append(tuple);
+ }
+ attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases)));
+ setJsonNode(_node, "ImportDirective", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
- Json::Value linearizedBaseContracts(Json::arrayValue);
- for (auto const& baseContract: _node.annotation().linearizedBaseContracts)
- linearizedBaseContracts.append(Json::UInt64(baseContract->id()));
- addJsonNode(_node, "ContractDefinition", {
+ setJsonNode(_node, "ContractDefinition", {
make_pair("name", _node.name()),
- make_pair("isLibrary", _node.isLibrary()),
+ make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
+ make_pair("contractKind", contractKind(_node.contractKind())),
make_pair("fullyImplemented", _node.annotation().isFullyImplemented),
- make_pair("linearizedBaseContracts", linearizedBaseContracts),
- }, true);
- return true;
+ make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)),
+ make_pair("baseContracts", toJson(_node.baseContracts())),
+ make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)),
+ make_pair("nodes", toJson(_node.subNodes())),
+ make_pair("scope", idOrNull(_node.scope()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
{
- addJsonNode(_node, "InheritanceSpecifier", {}, true);
- return true;
+ setJsonNode(_node, "InheritanceSpecifier", {
+ make_pair("baseName", toJson(_node.name())),
+ make_pair("arguments", toJson(_node.arguments()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(UsingForDirective const& _node)
{
- addJsonNode(_node, "UsingForDirective", {}, true);
- return true;
+ setJsonNode(_node, "UsingForDirective", {
+ make_pair("libraryName", toJson(_node.libraryName())),
+ make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
+ });
+ return false;
}
bool ASTJsonConverter::visit(StructDefinition const& _node)
{
- addJsonNode(_node, "StructDefinition", { make_pair("name", _node.name()) }, true);
- return true;
+ setJsonNode(_node, "StructDefinition", {
+ make_pair("name", _node.name()),
+ make_pair("visibility", visibility(_node.visibility())),
+ make_pair("canonicalName", _node.annotation().canonicalName),
+ make_pair("members", toJson(_node.members())),
+ make_pair("scope", idOrNull(_node.scope()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(EnumDefinition const& _node)
{
- addJsonNode(_node, "EnumDefinition", { make_pair("name", _node.name()) }, true);
- return true;
+ setJsonNode(_node, "EnumDefinition", {
+ make_pair("name", _node.name()),
+ make_pair("canonicalName", _node.annotation().canonicalName),
+ make_pair("members", toJson(_node.members()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(EnumValue const& _node)
{
- addJsonNode(_node, "EnumValue", { make_pair("name", _node.name()) });
- return true;
+ setJsonNode(_node, "EnumValue", {
+ make_pair("name", _node.name())
+ });
+ return false;
}
bool ASTJsonConverter::visit(ParameterList const& _node)
{
- addJsonNode(_node, "ParameterList", {}, true);
- return true;
+ setJsonNode(_node, "ParameterList", {
+ make_pair("parameters", toJson(_node.parameters()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
- addJsonNode(_node, "FunctionDefinition", {
+ std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
- make_pair("constant", _node.isDeclaredConst()),
+ make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
make_pair("payable", _node.isPayable()),
- make_pair("visibility", visibility(_node.visibility()))
- }, true);
- return true;
+ make_pair("visibility", visibility(_node.visibility())),
+ make_pair("parameters", toJson(_node.parameterList())),
+ make_pair("isConstructor", _node.isConstructor()),
+ make_pair("returnParameters", toJson(*_node.returnParameterList())),
+ make_pair("modifiers", toJson(_node.modifiers())),
+ make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue),
+ make_pair("implemented", _node.isImplemented()),
+ make_pair("scope", idOrNull(_node.scope()))
+ };
+ setJsonNode(_node, "FunctionDefinition", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
- std::vector<pair<string const, Json::Value const>> attributes = {
+ std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
- make_pair("type", type(_node)),
+ make_pair("typeName", toJsonOrNull(_node.typeName())),
make_pair("constant", _node.isConstant()),
+ make_pair("stateVariable", _node.isStateVariable()),
make_pair("storageLocation", location(_node.referenceLocation())),
- make_pair("visibility", visibility(_node.visibility()))
- };
+ make_pair("visibility", visibility(_node.visibility())),
+ make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue),
+ make_pair("scope", idOrNull(_node.scope())),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ };
if (m_inEvent)
attributes.push_back(make_pair("indexed", _node.isIndexed()));
- addJsonNode(_node, "VariableDeclaration", attributes, true);
- return true;
-
+ setJsonNode(_node, "VariableDeclaration", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(ModifierDefinition const& _node)
{
- addJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()) }, true);
- return true;
+ setJsonNode(_node, "ModifierDefinition", {
+ make_pair("name", _node.name()),
+ make_pair("visibility", visibility(_node.visibility())),
+ make_pair("parameters", toJson(_node.parameterList())),
+ make_pair("body", toJson(_node.body()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(ModifierInvocation const& _node)
{
- addJsonNode(_node, "ModifierInvocation", {}, true);
- return true;
+ setJsonNode(_node, "ModifierInvocation", {
+ make_pair("modifierName", toJson(*_node.name())),
+ make_pair("arguments", toJson(_node.arguments()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(TypeName const&)
{
- return true;
+ solAssert(false, "AST node of abstract type used.");
+ return false;
}
bool ASTJsonConverter::visit(EventDefinition const& _node)
{
m_inEvent = true;
- addJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()) }, true);
- return true;
+ setJsonNode(_node, "EventDefinition", {
+ make_pair("name", _node.name()),
+ make_pair("parameters", toJson(_node.parameterList())),
+ make_pair("anonymous", _node.isAnonymous())
+ });
+ return false;
}
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
- addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) });
- return true;
+ setJsonNode(_node, "ElementaryTypeName", {
+ make_pair("name", _node.typeName().toString()),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ });
+ return false;
}
bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
{
- addJsonNode(_node, "UserDefinedTypeName", {
- make_pair("name", boost::algorithm::join(_node.namePath(), "."))
+ setJsonNode(_node, "UserDefinedTypeName", {
+ make_pair("name", namePathToString(_node.namePath())),
+ make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
+ make_pair("contractScope", idOrNull(_node.annotation().contractScope)),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
});
- return true;
+ return false;
}
bool ASTJsonConverter::visit(FunctionTypeName const& _node)
{
- addJsonNode(_node, "FunctionTypeName", {
+ setJsonNode(_node, "FunctionTypeName", {
make_pair("payable", _node.isPayable()),
make_pair("visibility", visibility(_node.visibility())),
- make_pair("constant", _node.isDeclaredConst())
- }, true);
- return true;
+ make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.isDeclaredConst()),
+ make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
+ make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ });
+ return false;
}
bool ASTJsonConverter::visit(Mapping const& _node)
{
- addJsonNode(_node, "Mapping", {}, true);
- return true;
+ setJsonNode(_node, "Mapping", {
+ make_pair("keyType", toJson(_node.keyType())),
+ make_pair("valueType", toJson(_node.valueType())),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ });
+ return false;
}
bool ASTJsonConverter::visit(ArrayTypeName const& _node)
{
- addJsonNode(_node, "ArrayTypeName", {}, true);
- return true;
+ setJsonNode(_node, "ArrayTypeName", {
+ make_pair("baseType", toJson(_node.baseType())),
+ make_pair("length", toJsonOrNull(_node.length())),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ });
+ return false;
}
bool ASTJsonConverter::visit(InlineAssembly const& _node)
{
- addJsonNode(_node, "InlineAssembly", {}, true);
- return true;
+ Json::Value externalReferences(Json::arrayValue);
+ for (auto const& it : _node.annotation().externalReferences)
+ {
+ if (it.first)
+ {
+ Json::Value tuple(Json::objectValue);
+ tuple[it.first->name] = inlineAssemblyIdentifierToJson(it);
+ externalReferences.append(tuple);
+ }
+ }
+ setJsonNode(_node, "InlineAssembly", {
+ make_pair("operations", Json::Value(assembly::AsmPrinter()(_node.operations()))),
+ make_pair("externalReferences", std::move(externalReferences))
+ });
+ return false;
}
bool ASTJsonConverter::visit(Block const& _node)
{
- addJsonNode(_node, "Block", {}, true);
- return true;
+ setJsonNode(_node, "Block", {
+ make_pair("statements", toJson(_node.statements()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(PlaceholderStatement const& _node)
{
- addJsonNode(_node, "PlaceholderStatement", {});
- return true;
+ setJsonNode(_node, "PlaceholderStatement", {});
+ return false;
}
bool ASTJsonConverter::visit(IfStatement const& _node)
{
- addJsonNode(_node, "IfStatement", {}, true);
- return true;
+ setJsonNode(_node, "IfStatement", {
+ make_pair("condition", toJson(_node.condition())),
+ make_pair("trueBody", toJson(_node.trueStatement())),
+ make_pair("falseBody", toJsonOrNull(_node.falseStatement()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(WhileStatement const& _node)
{
- addJsonNode(
+ setJsonNode(
_node,
_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement",
- {},
- true);
- return true;
+ {
+ make_pair("condition", toJson(_node.condition())),
+ make_pair("body", toJson(_node.body()))
+ }
+ );
+ return false;
}
bool ASTJsonConverter::visit(ForStatement const& _node)
{
- addJsonNode(_node, "ForStatement", {}, true);
- return true;
+ setJsonNode(_node, "ForStatement", {
+ make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())),
+ make_pair("condition", toJsonOrNull(_node.condition())),
+ make_pair("loopExpression", toJsonOrNull(_node.loopExpression())),
+ make_pair("body", toJson(_node.body()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(Continue const& _node)
{
- addJsonNode(_node, "Continue", {});
- return true;
+ setJsonNode(_node, "Continue", {});
+ return false;
}
bool ASTJsonConverter::visit(Break const& _node)
{
- addJsonNode(_node, "Break", {});
- return true;
+ setJsonNode(_node, "Break", {});
+ return false;
}
bool ASTJsonConverter::visit(Return const& _node)
{
- addJsonNode(_node, "Return", {}, true);;
- return true;
+ setJsonNode(_node, "Return", {
+ make_pair("expression", toJsonOrNull(_node.expression())),
+ make_pair("functionReturnParameters", idOrNull(_node.annotation().functionReturnParameters))
+ });
+ return false;
}
bool ASTJsonConverter::visit(Throw const& _node)
{
- addJsonNode(_node, "Throw", {}, true);;
- return true;
+ setJsonNode(_node, "Throw", {});;
+ return false;
}
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
- addJsonNode(_node, "VariableDeclarationStatement", {}, true);
- return true;
+ Json::Value varDecs(Json::arrayValue);
+ for (auto const& v: _node.annotation().assignments)
+ varDecs.append(idOrNull(v));
+ setJsonNode(_node, "VariableDeclarationStatement", {
+ make_pair("assignments", std::move(varDecs)),
+ make_pair("declarations", toJson(_node.declarations())),
+ make_pair("initialValue", toJsonOrNull(_node.initialValue()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(ExpressionStatement const& _node)
{
- addJsonNode(_node, "ExpressionStatement", {}, true);
- return true;
+ setJsonNode(_node, "ExpressionStatement", {
+ make_pair("expression", toJson(_node.expression()))
+ });
+ return false;
}
bool ASTJsonConverter::visit(Conditional const& _node)
{
- addJsonNode(_node, "Conditional", {}, true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("condition", toJson(_node.condition())),
+ make_pair("trueExpression", toJson(_node.trueExpression())),
+ make_pair("falseExpression", toJson(_node.falseExpression()))
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "Conditional", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(Assignment const& _node)
{
- addJsonNode(_node, "Assignment",
- { make_pair("operator", Token::toString(_node.assignmentOperator())),
- make_pair("type", type(_node)) },
- true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("operator", Token::toString(_node.assignmentOperator())),
+ make_pair("leftHandSide", toJson(_node.leftHandSide())),
+ make_pair("rightHandSide", toJson(_node.rightHandSide()))
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode( _node, "Assignment", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(TupleExpression const& _node)
{
- addJsonNode(_node, "TupleExpression",{}, true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("isInlineArray", Json::Value(_node.isInlineArray())),
+ make_pair("components", toJson(_node.components())),
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "TupleExpression", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
- addJsonNode(_node, "UnaryOperation",
- { make_pair("prefix", _node.isPrefixOperation()),
- make_pair("operator", Token::toString(_node.getOperator())),
- make_pair("type", type(_node)) },
- true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("prefix", _node.isPrefixOperation()),
+ make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("subExpression", toJson(_node.subExpression()))
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "UnaryOperation", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
- addJsonNode(_node, "BinaryOperation", {
+ std::vector<pair<string, Json::Value>> attributes = {
make_pair("operator", Token::toString(_node.getOperator())),
- make_pair("type", type(_node))
- }, true);
- return true;
+ make_pair("leftExpression", toJson(_node.leftExpression())),
+ make_pair("rightExpression", toJson(_node.rightExpression())),
+ make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "BinaryOperation", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(FunctionCall const& _node)
{
- addJsonNode(_node, "FunctionCall", {
- make_pair("type_conversion", _node.annotation().isTypeConversion),
- make_pair("type", type(_node))
- }, true);
- return true;
+ Json::Value names(Json::arrayValue);
+ for (auto const& name: _node.names())
+ names.append(Json::Value(*name));
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("expression", toJson(_node.expression())),
+ make_pair("names", std::move(names)),
+ make_pair("arguments", toJson(_node.arguments()))
+ };
+ if (m_legacy)
+ {
+ attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall));
+ attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion));
+ }
+ else
+ attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind)));
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "FunctionCall", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(NewExpression const& _node)
{
- addJsonNode(_node, "NewExpression", { make_pair("type", type(_node)) }, true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("typeName", toJson(_node.typeName()))
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "NewExpression", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(MemberAccess const& _node)
{
- addJsonNode(_node, "MemberAccess", {
- make_pair("member_name", _node.memberName()),
- make_pair("type", type(_node))
- }, true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair(m_legacy ? "member_name" : "memberName", _node.memberName()),
+ make_pair("expression", toJson(_node.expression())),
+ make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "MemberAccess", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(IndexAccess const& _node)
{
- addJsonNode(_node, "IndexAccess", { make_pair("type", type(_node)) }, true);
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair("baseExpression", toJson(_node.baseExpression())),
+ make_pair("indexExpression", toJsonOrNull(_node.indexExpression())),
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "IndexAccess", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(Identifier const& _node)
{
- addJsonNode(_node, "Identifier",
- { make_pair("value", _node.name()), make_pair("type", type(_node)) });
- return true;
+ Json::Value overloads(Json::arrayValue);
+ for (auto const& dec: _node.annotation().overloadedDeclarations)
+ overloads.append(nodeId(*dec));
+ setJsonNode(_node, "Identifier", {
+ make_pair(m_legacy ? "value" : "name", _node.name()),
+ make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
+ make_pair("overloadedDeclarations", overloads),
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)),
+ make_pair("argumentTypes", typePointerToJson(_node.annotation().argumentTypes))
+ });
+ return false;
}
bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
{
- addJsonNode(_node, "ElementaryTypeNameExpression", {
- make_pair("value", _node.typeName().toString()),
- make_pair("type", type(_node))
- });
- return true;
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair(m_legacy ? "value" : "typeName", _node.typeName().toString())
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "ElementaryTypeNameExpression", std::move(attributes));
+ return false;
}
bool ASTJsonConverter::visit(Literal const& _node)
{
- char const* tokenString = Token::toString(_node.token());
Json::Value value{_node.value()};
if (!dev::validateUTF8(_node.value()))
value = Json::nullValue;
Token::Value subdenomination = Token::Value(_node.subDenomination());
- addJsonNode(_node, "Literal", {
- make_pair("token", tokenString ? tokenString : Json::Value()),
+ std::vector<pair<string, Json::Value>> attributes = {
+ make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
make_pair("value", value),
- make_pair("hexvalue", toHex(_node.value())),
+ make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())),
make_pair(
"subdenomination",
subdenomination == Token::Illegal ?
Json::nullValue :
Json::Value{Token::toString(subdenomination)}
- ),
- make_pair("type", type(_node))
- });
- return true;
+ )
+ };
+ appendExpressionAttributes(attributes, _node.annotation());
+ setJsonNode(_node, "Literal", std::move(attributes));
+ return false;
}
-void ASTJsonConverter::endVisit(SourceUnit const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(PragmaDirective const&)
-{
-}
-
-void ASTJsonConverter::endVisit(ImportDirective const&)
-{
-}
-
-void ASTJsonConverter::endVisit(ContractDefinition const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(InheritanceSpecifier const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(UsingForDirective const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(StructDefinition const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(EnumDefinition const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(EnumValue const&)
-{
-}
-
-void ASTJsonConverter::endVisit(ParameterList const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(FunctionDefinition const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(VariableDeclaration const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(ModifierDefinition const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(ModifierInvocation const&)
-{
- goUp();
-}
void ASTJsonConverter::endVisit(EventDefinition const&)
{
m_inEvent = false;
- goUp();
-}
-
-void ASTJsonConverter::endVisit(TypeName const&)
-{
-}
-
-void ASTJsonConverter::endVisit(ElementaryTypeName const&)
-{
-}
-
-void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
-{
-}
-
-void ASTJsonConverter::endVisit(FunctionTypeName const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Mapping const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(ArrayTypeName const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(InlineAssembly const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Block const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(PlaceholderStatement const&)
-{
-}
-
-void ASTJsonConverter::endVisit(IfStatement const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(WhileStatement const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(ForStatement const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Continue const&)
-{
-}
-
-void ASTJsonConverter::endVisit(Break const&)
-{
-}
-
-void ASTJsonConverter::endVisit(Return const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Throw const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(VariableDeclarationStatement const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(ExpressionStatement const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Conditional const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Assignment const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(TupleExpression const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(UnaryOperation const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(BinaryOperation const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(FunctionCall const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(NewExpression const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(MemberAccess const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(IndexAccess const&)
-{
- goUp();
-}
-
-void ASTJsonConverter::endVisit(Identifier const&)
-{
-}
-
-void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
-{
-}
-
-void ASTJsonConverter::endVisit(Literal const&)
-{
-}
-
-void ASTJsonConverter::process()
-{
- if (!processed)
- m_ast->accept(*this);
- processed = true;
}
string ASTJsonConverter::visibility(Declaration::Visibility const& _visibility)
@@ -709,6 +762,52 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
}
}
+string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
+{
+ switch (_kind)
+ {
+ case ContractDefinition::ContractKind::Interface:
+ return "interface";
+ case ContractDefinition::ContractKind::Contract:
+ return "contract";
+ case ContractDefinition::ContractKind::Library:
+ return "library";
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of contract."));
+ }
+}
+
+string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
+{
+ switch (_kind)
+ {
+ case FunctionCallKind::FunctionCall:
+ return "functionCall";
+ case FunctionCallKind::TypeConversion:
+ return "typeConversion";
+ case FunctionCallKind::StructConstructorCall:
+ return "structConstructorCall";
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of function call ."));
+ }
+}
+
+string ASTJsonConverter::literalTokenKind(Token::Value _token)
+{
+ switch (_token)
+ {
+ case dev::solidity::Token::Number:
+ return "number";
+ case dev::solidity::Token::StringLiteral:
+ return "string";
+ case dev::solidity::Token::TrueLiteral:
+ case dev::solidity::Token::FalseLiteral:
+ return "bool";
+ default:
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of literal token."));
+ }
+}
+
string ASTJsonConverter::type(Expression const& _expression)
{
return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index bd5e6560..27114c2a 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -26,7 +26,6 @@
#include <stack>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <json/json.h>
@@ -42,15 +41,23 @@ class ASTJsonConverter: public ASTConstVisitor
{
public:
/// Create a converter to JSON for the given abstract syntax tree.
+ /// @a _legacy if true, use legacy format
/// @a _sourceIndices is used to abbreviate source names in source locations.
explicit ASTJsonConverter(
- ASTNode const& _ast,
+ bool _legacy,
std::map<std::string, unsigned> _sourceIndices = std::map<std::string, unsigned>()
);
/// Output the json representation of the AST to _stream.
- void print(std::ostream& _stream);
- Json::Value const& json();
-
+ void print(std::ostream& _stream, ASTNode const& _node);
+ Json::Value toJson(ASTNode const& _node);
+ template <class T>
+ Json::Value toJson(std::vector<ASTPointer<T>> const& _nodes)
+ {
+ Json::Value ret(Json::arrayValue);
+ for (auto const& n: _nodes)
+ ret.append(n ? toJson(*n) : Json::nullValue);
+ return ret;
+ }
bool visit(SourceUnit const& _node) override;
bool visit(PragmaDirective const& _node) override;
bool visit(ImportDirective const& _node) override;
@@ -97,82 +104,61 @@ public:
bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
- void endVisit(SourceUnit const&) override;
- void endVisit(PragmaDirective const&) override;
- void endVisit(ImportDirective const&) override;
- void endVisit(ContractDefinition const&) override;
- void endVisit(InheritanceSpecifier const&) override;
- void endVisit(UsingForDirective const&) override;
- void endVisit(StructDefinition const&) override;
- void endVisit(EnumDefinition const&) override;
- void endVisit(EnumValue const&) override;
- void endVisit(ParameterList const&) override;
- void endVisit(FunctionDefinition const&) override;
- void endVisit(VariableDeclaration const&) override;
- void endVisit(ModifierDefinition const&) override;
- void endVisit(ModifierInvocation const&) override;
void endVisit(EventDefinition const&) override;
- void endVisit(TypeName const&) override;
- void endVisit(ElementaryTypeName const&) override;
- void endVisit(UserDefinedTypeName const&) override;
- void endVisit(FunctionTypeName const&) override;
- void endVisit(Mapping const&) override;
- void endVisit(ArrayTypeName const&) override;
- void endVisit(InlineAssembly const&) override;
- void endVisit(Block const&) override;
- void endVisit(PlaceholderStatement const&) override;
- void endVisit(IfStatement const&) override;
- void endVisit(WhileStatement const&) override;
- void endVisit(ForStatement const&) override;
- void endVisit(Continue const&) override;
- void endVisit(Break const&) override;
- void endVisit(Return const&) override;
- void endVisit(Throw const&) override;
- void endVisit(VariableDeclarationStatement const&) override;
- void endVisit(ExpressionStatement const&) override;
- void endVisit(Conditional const&) override;
- void endVisit(Assignment const&) override;
- void endVisit(TupleExpression const&) override;
- void endVisit(UnaryOperation const&) override;
- void endVisit(BinaryOperation const&) override;
- void endVisit(FunctionCall const&) override;
- void endVisit(NewExpression const&) override;
- void endVisit(MemberAccess const&) override;
- void endVisit(IndexAccess const&) override;
- void endVisit(Identifier const&) override;
- void endVisit(ElementaryTypeNameExpression const&) override;
- void endVisit(Literal const&) override;
private:
- void process();
- void addJsonNode(
+ void setJsonNode(
ASTNode const& _node,
std::string const& _nodeName,
- std::initializer_list<std::pair<std::string const, Json::Value const>> _attributes,
- bool _hasChildren
+ std::initializer_list<std::pair<std::string, Json::Value>>&& _attributes
);
- void addJsonNode(
+ void setJsonNode(
ASTNode const& _node,
std::string const& _nodeName,
- std::vector<std::pair<std::string const, Json::Value const>> const& _attributes,
- bool _hasChildren
+ std::vector<std::pair<std::string, Json::Value>>&& _attributes
);
std::string sourceLocationToString(SourceLocation const& _location) const;
+ std::string namePathToString(std::vector<ASTString> const& _namePath) const;
+ Json::Value idOrNull(ASTNode const* _pt)
+ {
+ return _pt ? Json::Value(nodeId(*_pt)) : Json::nullValue;
+ }
+ Json::Value toJsonOrNull(ASTNode const* _node)
+ {
+ return _node ? toJson(*_node) : Json::nullValue;
+ }
+ Json::Value inlineAssemblyIdentifierToJson(std::pair<assembly::Identifier const* , InlineAssemblyAnnotation::ExternalIdentifierInfo> _info);
std::string visibility(Declaration::Visibility const& _visibility);
std::string location(VariableDeclaration::Location _location);
+ std::string contractKind(ContractDefinition::ContractKind _kind);
+ std::string functionCallKind(FunctionCallKind _kind);
+ std::string literalTokenKind(Token::Value _token);
std::string type(Expression const& _expression);
std::string type(VariableDeclaration const& _varDecl);
- inline void goUp()
+ int nodeId(ASTNode const& _node)
{
- solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error.");
- m_jsonNodePtrs.pop();
+ return _node.id();
}
-
+ template<class Container>
+ Json::Value getContainerIds(Container const& container)
+ {
+ Json::Value tmp(Json::arrayValue);
+ for (auto const& element: container)
+ {
+ solAssert(element, "");
+ tmp.append(nodeId(*element));
+ }
+ return tmp;
+ }
+ Json::Value typePointerToJson(TypePointer _tp);
+ Json::Value typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps);
+ void appendExpressionAttributes(
+ std::vector<std::pair<std::string, Json::Value>> &_attributes,
+ ExpressionAnnotation const& _annotation
+ );
+ bool m_legacy = false; ///< if true, use legacy format
bool m_inEvent = false; ///< whether we are currently inside an event or not
- bool processed = false;
- Json::Value m_astJson;
- std::stack<Json::Value*> m_jsonNodePtrs;
- ASTNode const* m_ast;
+ Json::Value m_currentValue;
std::map<std::string, unsigned> m_sourceIndices;
};
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 41ee6498..7dc6c4a6 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -22,7 +22,6 @@
#include <libsolidity/ast/Types.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/ast/AST.h>
#include <libdevcore/CommonIO.h>
@@ -2183,6 +2182,8 @@ string FunctionType::identifier() const
case Kind::ArrayPush: id += "arraypush"; break;
case Kind::ByteArrayPush: id += "bytearraypush"; break;
case Kind::ObjectCreation: id += "objectcreation"; break;
+ case Kind::Assert: id += "assert"; break;
+ case Kind::Require: id += "require";break;
default: solAssert(false, "Unknown function location."); break;
}
if (isConstant())
@@ -2247,6 +2248,16 @@ TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
return TypePointer();
}
+TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
+ return TypePointer();
+ FunctionType const& other = dynamic_cast<FunctionType const&>(*_other);
+ if (kind() == Kind::Internal && other.kind() == Kind::Internal && sizeOnStack() == 1 && other.sizeOnStack() == 1)
+ return commonType(shared_from_this(), _other);
+ return TypePointer();
+}
+
string FunctionType::canonicalName(bool) const
{
solAssert(m_kind == Kind::External, "");
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index c4ffc44c..f7a73ab5 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -933,6 +933,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
virtual std::string canonicalName(bool /*_addDataLocation*/) const override;
virtual std::string toString(bool _short) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
@@ -1038,6 +1039,7 @@ public:
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(256);
@@ -1116,11 +1118,7 @@ public:
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
- {
- return TypePointer();
- }
-
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual std::string identifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
@@ -1178,6 +1176,7 @@ public:
virtual std::string identifier() const override { return "t_inaccessible"; }
virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index bdd29abd..67ca22f1 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -25,7 +25,7 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/ast/Types.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/codegen/LValue.h>
using namespace std;
@@ -449,7 +449,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
m_context << Instruction::DUP3 << Instruction::ADD << Instruction::SWAP2;
if (_sourceType.isDynamicallySized())
{
- // actual array data is stored at SHA3(storage_offset)
+ // actual array data is stored at KECCAK256(storage_offset)
m_context << Instruction::SWAP1;
utils.computeHashStatic();
m_context << Instruction::SWAP1;
@@ -731,7 +731,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
_context << Instruction::POP;
}
- // Change of length for a regular array (i.e. length at location, data at sha3(location)).
+ // Change of length for a regular array (i.e. length at location, data at KECCAK256(location)).
// stack: ref new_length old_length
// store new length
_context << Instruction::DUP2;
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 51dd9fd2..6875bda1 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -25,8 +25,12 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/Version.h>
-#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/inlineasm/AsmStack.h>
+#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/inlineasm/AsmParser.h>
+#include <libsolidity/inlineasm/AsmCodeGen.h>
+#include <libsolidity/inlineasm/AsmAnalysis.h>
+#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <boost/algorithm/string/replace.hpp>
@@ -120,6 +124,7 @@ void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
+ solAssert(m_localVariables.count(&_declaration) == 0, "Variable already present");
m_localVariables[&_declaration] = unsigned(m_asm->deposit()) - _offsetToCurrent;
}
@@ -240,6 +245,20 @@ CompilerContext& CompilerContext::appendConditionalInvalid()
return *this;
}
+CompilerContext& CompilerContext::appendRevert()
+{
+ return *this << u256(0) << u256(0) << Instruction::REVERT;
+}
+
+CompilerContext& CompilerContext::appendConditionalRevert()
+{
+ *this << Instruction::ISZERO;
+ eth::AssemblyItem afterTag = appendConditionalJump();
+ appendRevert();
+ *this << afterTag;
+ return *this;
+}
+
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
{
stack<ASTNode const*> newStack;
@@ -264,12 +283,13 @@ void CompilerContext::appendInlineAssembly(
assembly = &replacedAssembly;
}
- unsigned startStackHeight = stackHeight();
+ int startStackHeight = stackHeight();
- assembly::ExternalIdentifierAccess identifierAccess;
+ julia::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&](
assembly::Identifier const& _identifier,
- assembly::IdentifierContext
+ julia::IdentifierContext,
+ bool
)
{
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
@@ -277,31 +297,42 @@ void CompilerContext::appendInlineAssembly(
};
identifierAccess.generateCode = [&](
assembly::Identifier const& _identifier,
- assembly::IdentifierContext _context,
- eth::Assembly& _assembly
+ julia::IdentifierContext _context,
+ julia::AbstractAssembly& _assembly
)
{
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
solAssert(it != _localVariables.end(), "");
- unsigned stackDepth = _localVariables.end() - it;
- int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
- if (_context == assembly::IdentifierContext::LValue)
+ int stackDepth = _localVariables.end() - it;
+ int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth;
+ if (_context == julia::IdentifierContext::LValue)
stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
- errinfo_comment("Stack too deep, try removing local variables.")
+ errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
);
- if (_context == assembly::IdentifierContext::RValue)
- _assembly.append(dupInstruction(stackDiff));
+ if (_context == julia::IdentifierContext::RValue)
+ _assembly.appendInstruction(dupInstruction(stackDiff));
else
{
- _assembly.append(swapInstruction(stackDiff));
- _assembly.append(Instruction::POP);
+ _assembly.appendInstruction(swapInstruction(stackDiff));
+ _assembly.appendInstruction(Instruction::POP);
}
};
- solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block.");
+ ErrorList errors;
+ ErrorReporter errorReporter(errors);
+ auto scanner = make_shared<Scanner>(CharStream(*assembly), "--CODEGEN--");
+ auto parserResult = assembly::Parser(errorReporter).parse(scanner);
+ solAssert(parserResult, "Failed to parse inline assembly block.");
+ solAssert(errorReporter.errors().empty(), "Failed to parse inline assembly block.");
+
+ assembly::AsmAnalysisInfo analysisInfo;
+ assembly::AsmAnalyzer analyzer(analysisInfo, errorReporter, false, identifierAccess.resolve);
+ solAssert(analyzer.analyze(*parserResult), "Failed to analyze inline assembly block.");
+ solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
+ assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess);
}
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index c37142c9..1968c1e1 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -136,13 +136,15 @@ public:
/// Appends a JUMP to a new tag and @returns the tag
eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
/// Appends a JUMP to a tag already on the stack
- CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
+ CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
/// Appends an INVALID instruction
- CompilerContext& appendInvalid();
+ CompilerContext& appendInvalid();
/// Appends a conditional INVALID instruction
- CompilerContext& appendConditionalInvalid();
- /// Returns an "ErrorTag"
- eth::AssemblyItem errorTag() { return m_asm->errorTag(); }
+ CompilerContext& appendConditionalInvalid();
+ /// Appends a REVERT(0, 0) call
+ CompilerContext& appendRevert();
+ /// Appends a conditional REVERT(0, 0) call
+ CompilerContext& appendConditionalRevert();
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
@@ -151,10 +153,10 @@ public:
eth::AssemblyItem newTag() { return m_asm->newTag(); }
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
- eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { auto sub = m_asm->newSub(_assembly); m_asm->append(m_asm->newPushSubSize(size_t(sub.data()))); return sub; }
- void pushSubroutineSize(size_t _subRoutine) { m_asm->append(m_asm->newPushSubSize(_subRoutine)); }
+ eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); }
+ void pushSubroutineSize(size_t _subRoutine) { m_asm->pushSubroutineSize(_subRoutine); }
/// Pushes the offset of the subroutine.
- void pushSubroutineOffset(size_t _subRoutine) { m_asm->append(eth::AssemblyItem(eth::PushSub, _subRoutine)); }
+ void pushSubroutineOffset(size_t _subRoutine) { m_asm->pushSubroutineOffset(_subRoutine); }
/// Pushes the size of the final program
void appendProgramSize() { m_asm->appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index dc0b340c..7067ddd5 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -128,7 +128,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << Instruction::DUP1;
storeStringData(bytesConstRef(str->value()));
if (_padToWordBoundaries)
- m_context << u256(((str->value().size() + 31) / 32) * 32);
+ m_context << u256(max<size_t>(32, ((str->value().size() + 31) / 32) * 32));
else
m_context << u256(str->value().size());
m_context << Instruction::ADD;
@@ -180,6 +180,9 @@ void CompilerUtils::encodeToMemory(
t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
}
+ if (_givenTypes.empty())
+ return;
+
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
// The values dyn_head_i are added during the first loop and they point to the head part
@@ -299,40 +302,15 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
m_context << Instruction::SWAP1 << Instruction::POP;
}
-void CompilerUtils::memoryCopyPrecompile()
-{
- // Stack here: size target source
-
- m_context.appendInlineAssembly(R"(
- {
- let words := div(add(len, 31), 32)
- let cost := add(15, mul(3, words))
- jumpi(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len)))
- }
- )",
- { "len", "dst", "src" },
- map<string, string> {
- { "$identityContractAddress", toString(identityContractAddress) }
- }
- );
- m_context << Instruction::POP << Instruction::POP << Instruction::POP;
-}
-
void CompilerUtils::memoryCopy32()
{
// Stack here: size target source
m_context.appendInlineAssembly(R"(
{
- jumpi(end, eq(len, 0))
- start:
- mstore(dst, mload(src))
- jumpi(end, iszero(gt(len, 32)))
- dst := add(dst, 32)
- src := add(src, 32)
- len := sub(len, 32)
- jump(start)
- end:
+ for { let i := 0 } lt(i, len) { i := add(i, 32) } {
+ mstore(add(dst, i), mload(add(src, i)))
+ }
}
)",
{ "len", "dst", "src" }
@@ -346,21 +324,22 @@ void CompilerUtils::memoryCopy()
m_context.appendInlineAssembly(R"(
{
- // copy 32 bytes at once
- start32:
- jumpi(end32, lt(len, 32))
- mstore(dst, mload(src))
- dst := add(dst, 32)
- src := add(src, 32)
- len := sub(len, 32)
- jump(start32)
- end32:
+ // copy 32 bytes at once
+ for
+ {}
+ iszero(lt(len, 32))
+ {
+ dst := add(dst, 32)
+ src := add(src, 32)
+ len := sub(len, 32)
+ }
+ { mstore(dst, mload(src)) }
- // copy the remainder (0 < len < 32)
- let mask := sub(exp(256, sub(32, len)), 1)
- let srcpart := and(mload(src), not(mask))
- let dstpart := and(mload(dst), mask)
- mstore(dst, or(srcpart, dstpart))
+ // copy the remainder (0 < len < 32)
+ let mask := sub(exp(256, sub(32, len)), 1)
+ let srcpart := and(mload(src), not(mask))
+ let dstpart := and(mload(dst), mask)
+ mstore(dst, or(srcpart, dstpart))
}
)",
{ "len", "dst", "src" }
@@ -374,13 +353,16 @@ void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
// address (right aligned), function identifier (right aligned)
if (_leftAligned)
{
- m_context << Instruction::DUP1 << (u256(1) << (64 + 32)) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::DUP1;
+ rightShiftNumberOnStack(64 + 32, false);
// <input> <address>
- m_context << Instruction::SWAP1 << (u256(1) << 64) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::SWAP1;
+ rightShiftNumberOnStack(64, false);
}
else
{
- m_context << Instruction::DUP1 << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::DUP1;
+ rightShiftNumberOnStack(32, false);
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
}
m_context << u256(0xffffffffUL) << Instruction::AND;
@@ -392,10 +374,10 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
m_context << u256(0xffffffffUL) << Instruction::AND << Instruction::SWAP1;
if (!_leftAligned)
m_context << ((u256(1) << 160) - 1) << Instruction::AND;
- m_context << (u256(1) << 32) << Instruction::MUL;
+ leftShiftNumberOnStack(32);
m_context << Instruction::OR;
if (_leftAligned)
- m_context << (u256(1) << 64) << Instruction::MUL;
+ leftShiftNumberOnStack(64);
}
void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
@@ -404,14 +386,21 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
+ {
+ leftShiftNumberOnStack(32);
m_context <<
- (u256(1) << 32) <<
- Instruction::MUL <<
rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
Instruction::OR;
+ }
}
-void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded, bool _chopSignBits)
+void CompilerUtils::convertType(
+ Type const& _typeOnStack,
+ Type const& _targetType,
+ bool _cleanupNeeded,
+ bool _chopSignBits,
+ bool _asPartOfArgumentDecoding
+)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
@@ -440,7 +429,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// conversion from bytes to integer. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
- m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << Instruction::SWAP1 << Instruction::DIV;
+ rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8, false);
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
@@ -469,7 +458,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
- m_context.appendConditionalInvalid();
+ if (_asPartOfArgumentDecoding)
+ m_context.appendConditionalRevert();
+ else
+ m_context.appendConditionalInvalid();
enumOverflowCheckPending = false;
}
break;
@@ -488,7 +480,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
- m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL;
+ leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
}
else if (targetTypeCategory == Type::Category::Enum)
{
@@ -953,7 +945,7 @@ unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _varia
void CompilerUtils::computeHashStatic()
{
storeInMemory(0);
- m_context << u256(32) << u256(0) << Instruction::SHA3;
+ m_context << u256(32) << u256(0) << Instruction::KECCAK256;
}
void CompilerUtils::storeStringData(bytesConstRef _data)
@@ -998,13 +990,13 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
{
bool leftAligned = _type.category() == Type::Category::FixedBytes;
// add leading or trailing zeros by dividing/multiplying depending on alignment
- u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
- m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
+ int shiftFactor = (32 - numBytes) * 8;
+ rightShiftNumberOnStack(shiftFactor, false);
if (leftAligned)
- m_context << shiftFactor << Instruction::MUL;
+ leftShiftNumberOnStack(shiftFactor);
}
if (_fromCalldata)
- convertType(_type, _type, true);
+ convertType(_type, _type, true, false, true);
return numBytes;
}
@@ -1019,6 +1011,18 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
}
+void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
+{
+ solAssert(_bits < 256, "");
+ m_context << (u256(1) << _bits) << Instruction::MUL;
+}
+
+void CompilerUtils::rightShiftNumberOnStack(unsigned _bits, bool _isSigned)
+{
+ solAssert(_bits < 256, "");
+ m_context << (u256(1) << _bits) << Instruction::SWAP1 << (_isSigned ? Instruction::SDIV : Instruction::DIV);
+}
+
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
{
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
@@ -1031,7 +1035,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
convertType(_type, _type, true);
if (numBytes != 32 && !leftAligned && !_padToWords)
// shift the value accordingly before storing
- m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL;
+ leftShiftNumberOnStack((32 - numBytes) * 8);
}
return numBytes;
}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index b9ed6757..fb169463 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -109,15 +109,13 @@ public:
/// Stack post: <updated_memptr>
void zeroInitialiseMemoryArray(ArrayType const& _type);
- /// Uses a CALL to the identity contract to perform a memory-to-memory copy.
- /// Stack pre: <size> <target> <source>
- /// Stack post:
- void memoryCopyPrecompile();
/// Copies full 32 byte words in memory (regions cannot overlap), i.e. may copy more than length.
+ /// Length can be zero, in this case, it copies nothing.
/// Stack pre: <size> <target> <source>
/// Stack post:
void memoryCopy32();
/// Copies data in memory (regions cannot overlap).
+ /// Length can be zero, in this case, it copies nothing.
/// Stack pre: <size> <target> <source>
/// Stack post:
void memoryCopy();
@@ -139,7 +137,15 @@ public:
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary.
/// If @a _chopSignBits, the function resets the signed bits out of the width of the signed integer.
- void convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false, bool _chopSignBits = false);
+ /// If @a _asPartOfArgumentDecoding is true, failed conversions are flagged via REVERT,
+ /// otherwise they are flagged with INVALID.
+ void convertType(
+ Type const& _typeOnStack,
+ Type const& _targetType,
+ bool _cleanupNeeded = false,
+ bool _chopSignBits = false,
+ bool _asPartOfArgumentDecoding = false
+ );
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references.
@@ -170,7 +176,13 @@ public:
static unsigned sizeOnStack(std::vector<T> const& _variables);
static unsigned sizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
- /// Appends code that computes tha SHA3 hash of the topmost stack element of 32 byte type.
+ /// Helper function to shift top value on the stack to the left.
+ void leftShiftNumberOnStack(unsigned _bits);
+
+ /// Helper function to shift top value on the stack to the right.
+ void rightShiftNumberOnStack(unsigned _bits, bool _isSigned = false);
+
+ /// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
/// Bytes we need to the start of call data.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 34ef13c0..c358a519 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -21,15 +21,20 @@
*/
#include <libsolidity/codegen/ContractCompiler.h>
-#include <algorithm>
-#include <boost/range/adaptor/reversed.hpp>
-#include <libevmasm/Instruction.h>
-#include <libevmasm/Assembly.h>
-#include <libevmasm/GasMeter.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/codegen/ExpressionCompiler.h>
#include <libsolidity/codegen/CompilerUtils.h>
+
+#include <libevmasm/Instruction.h>
+#include <libevmasm/Assembly.h>
+#include <libevmasm/GasMeter.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+#include <algorithm>
+
using namespace std;
using namespace dev;
using namespace dev::solidity;
@@ -106,7 +111,7 @@ void ContractCompiler::appendCallValueCheck()
{
// Throw if function is not payable but call contained ether.
m_context << Instruction::CALLVALUE;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
@@ -262,16 +267,22 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
+ m_context.setStackOffset(0);
if (!fallback->isPayable())
appendCallValueCheck();
+ // Return tag is used to jump out of the function.
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
m_context << returnTag;
- appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
+ solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
+ solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
+ // Return tag gets consumed.
+ m_context.adjustStackOffset(-1);
+ m_context << Instruction::STOP;
}
else
- m_context.appendInvalid();
+ m_context.appendRevert();
for (auto const& it: interfaceFunctions)
{
@@ -280,16 +291,29 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
m_context << callDataUnpackerEntryPoints.at(it.first);
+ m_context.setStackOffset(0);
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
if (!functionType->isPayable() && !_contract.isLibrary())
appendCallValueCheck();
+ // Return tag is used to jump out of the function.
eth::AssemblyItem returnTag = m_context.pushNewTag();
- m_context << CompilerUtils::dataStartOffset;
- appendCalldataUnpacker(functionType->parameterTypes());
+ if (!functionType->parameterTypes().empty())
+ {
+ // Parameter for calldataUnpacker
+ m_context << CompilerUtils::dataStartOffset;
+ appendCalldataUnpacker(functionType->parameterTypes());
+ }
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
m_context << returnTag;
+ // Return tag and input parameters get consumed.
+ m_context.adjustStackOffset(
+ CompilerUtils(m_context).sizeOnStack(functionType->returnParameterTypes()) -
+ CompilerUtils(m_context).sizeOnStack(functionType->parameterTypes()) -
+ 1
+ );
+ // Consumes the return parameters.
appendReturnValuePacker(functionType->returnParameterTypes(), _contract.isLibrary());
}
}
@@ -363,7 +387,7 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
// copy to memory
// move calldata type up again
CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
- CompilerUtils(m_context).convertType(*calldataType, arrayType);
+ CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true);
// fetch next pointer again
CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
}
@@ -519,40 +543,42 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
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, bool)
{
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, eth::Assembly& _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.deposit();
+ int const depositBefore = _assembly.stackHeight();
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
{
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
functionDef = &m_context.resolveVirtualFunction(*functionDef);
- _assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
+ auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag();
+ solAssert(functionEntryLabel.data() <= std::numeric_limits<size_t>::max(), "");
+ _assembly.appendLabelReference(size_t(functionEntryLabel.data()));
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
{
- _assembly.append(u256(1) << 32);
- _assembly.append(Instruction::MUL);
- _assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
- _assembly.append(Instruction::OR);
+ _assembly.appendConstant(u256(1) << 32);
+ _assembly.appendInstruction(Instruction::MUL);
+ auto runtimeEntryLabel = rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub());
+ solAssert(runtimeEntryLabel.data() <= std::numeric_limits<size_t>::max(), "");
+ _assembly.appendLabelReference(size_t(runtimeEntryLabel.data()));
+ _assembly.appendInstruction(Instruction::OR);
}
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
@@ -570,7 +596,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}
else if (m_context.isLocalVariable(decl))
{
- int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable);
+ int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable);
if (ref->second.isSlot || ref->second.isOffset)
{
solAssert(variable->type()->dataStoredIn(DataLocation::Storage), "");
@@ -587,7 +613,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
// only slot, offset is zero
if (ref->second.isOffset)
{
- _assembly.append(u256(0));
+ _assembly.appendConstant(u256(0));
return;
}
}
@@ -601,7 +627,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
errinfo_comment("Stack too deep, try removing local variables.")
);
solAssert(variable->type()->sizeOnStack() == 1, "");
- _assembly.append(dupInstruction(stackDiff));
+ _assembly.appendInstruction(dupInstruction(stackDiff));
}
else
solAssert(false, "");
@@ -610,11 +636,11 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
solAssert(contract->isLibrary(), "");
- _assembly.appendLibraryAddress(contract->fullyQualifiedName());
+ _assembly.appendLinkerSymbol(contract->fullyQualifiedName());
}
else
solAssert(false, "Invalid declaration type.");
- solAssert(_assembly.deposit() - depositBefore == int(ref->second.valueSize), "");
+ solAssert(_assembly.stackHeight() - depositBefore == int(ref->second.valueSize), "");
}
else
{
@@ -626,25 +652,24 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
"Can only assign to stack variables in inline assembly."
);
solAssert(variable->type()->sizeOnStack() == 1, "");
- int stackDiff = _assembly.deposit() - m_context.baseStackOffsetOfVariable(*variable) - 1;
+ int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
- errinfo_comment("Stack too deep, try removing local variables.")
+ errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
);
- _assembly.append(swapInstruction(stackDiff));
- _assembly.append(Instruction::POP);
+ _assembly.appendInstruction(swapInstruction(stackDiff));
+ _assembly.appendInstruction(Instruction::POP);
}
};
solAssert(_inlineAssembly.annotation().analysisInfo, "");
- codeGen.assemble(
+ assembly::CodeGenerator::assemble(
_inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo,
m_context.nonConstAssembly(),
identifierAccess
);
- solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested.");
m_context.setStackOffset(startStackHeight);
return false;
}
@@ -799,8 +824,7 @@ bool ContractCompiler::visit(Throw const& _throw)
{
CompilerContext::LocationSetter locationSetter(m_context, _throw);
// Do not send back an error detail.
- m_context << u256(0) << u256(0);
- m_context << Instruction::REVERT;
+ m_context.appendRevert();
return false;
}
@@ -873,6 +897,7 @@ void ContractCompiler::appendModifierOrFunctionCode()
solAssert(m_currentFunction, "");
unsigned stackSurplus = 0;
Block const* codeBlock = nullptr;
+ vector<VariableDeclaration const*> addedVariables;
m_modifierDepth++;
@@ -896,6 +921,7 @@ void ContractCompiler::appendModifierOrFunctionCode()
for (unsigned i = 0; i < modifier.parameters().size(); ++i)
{
m_context.addVariable(*modifier.parameters()[i]);
+ addedVariables.push_back(modifier.parameters()[i].get());
compileExpression(
*modifierInvocation->arguments()[i],
modifier.parameters()[i]->annotation().type
@@ -922,6 +948,8 @@ void ContractCompiler::appendModifierOrFunctionCode()
m_returnTags.pop_back();
CompilerUtils(m_context).popStackSlots(stackSurplus);
+ for (auto var: addedVariables)
+ m_context.removeVariable(*var);
}
m_modifierDepth--;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index f018b311..82518e8c 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -32,6 +32,7 @@
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h>
#include <libevmasm/GasMeter.h>
+
using namespace std;
namespace dev
@@ -87,6 +88,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
FunctionType accessorType(_varDecl);
TypePointers paramTypes = accessorType.parameterTypes();
+ m_context.adjustStackOffset(1 + CompilerUtils::sizeOnStack(paramTypes));
// retrieve the position of the variable
auto const& location = m_context.storageLocationOfVariable(_varDecl);
@@ -110,7 +112,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
// move key to memory.
utils().copyToStackTop(paramTypes.size() - i, 1);
utils().storeInMemory(0);
- m_context << u256(64) << u256(0) << Instruction::SHA3;
+ m_context << u256(64) << u256(0) << Instruction::KECCAK256;
// push offset
m_context << u256(0);
returnType = mappingType->valueType();
@@ -434,7 +436,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
- if (_functionCall.annotation().isTypeConversion)
+ if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
{
solAssert(_functionCall.arguments().size() == 1, "");
solAssert(_functionCall.names().empty(), "");
@@ -445,7 +447,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
FunctionTypePointer functionType;
- if (_functionCall.annotation().isStructConstructorCall)
+ if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
@@ -476,7 +478,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
solAssert(found, "");
}
- if (_functionCall.annotation().isStructConstructorCall)
+ if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
@@ -524,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (m_context.runtimeContext())
// We have a runtime context, so we need the creation part.
- m_context << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ utils().rightShiftNumberOnStack(32, false);
else
// Extract the runtime part.
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
@@ -586,7 +588,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::CREATE;
// Check if zero (out of stack or not enough balance).
m_context << Instruction::DUP1 << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
break;
@@ -650,7 +652,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
// Check if zero (out of stack or not enough balance).
m_context << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
break;
case FunctionType::Kind::Selfdestruct:
@@ -659,9 +661,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::SELFDESTRUCT;
break;
case FunctionType::Kind::Revert:
- // memory offset returned - zero length
- m_context << u256(0) << u256(0);
- m_context << Instruction::REVERT;
+ m_context.appendRevert();
break;
case FunctionType::Kind::SHA3:
{
@@ -674,7 +674,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().fetchFreeMemoryPointer();
utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
utils().toSizeAfterFreeMemoryPointer();
- m_context << Instruction::SHA3;
+ m_context << Instruction::KECCAK256;
break;
}
case FunctionType::Kind::Log0:
@@ -721,7 +721,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
true
);
utils().toSizeAfterFreeMemoryPointer();
- m_context << Instruction::SHA3;
+ m_context << Instruction::KECCAK256;
}
else
utils().convertType(
@@ -887,9 +887,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
auto success = m_context.appendConditionalJump();
if (function.kind() == FunctionType::Kind::Assert)
// condition was not met, flag an error
- m_context << Instruction::INVALID;
+ m_context.appendInvalid();
else
- m_context << u256(0) << u256(0) << Instruction::REVERT;
+ m_context.appendRevert();
// the success branch
m_context << success;
break;
@@ -1214,7 +1214,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
utils().storeInMemoryDynamic(IntegerType(256));
m_context << u256(0);
}
- m_context << Instruction::SHA3;
+ m_context << Instruction::KECCAK256;
m_context << u256(0);
setLValueToStorageItem(_indexAccess);
}
@@ -1269,7 +1269,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
m_context.appendConditionalInvalid();
m_context << Instruction::BYTE;
- m_context << (u256(1) << (256 - 8)) << Instruction::MUL;
+ utils().leftShiftNumberOnStack(256 - 8);
}
else if (baseType.category() == Type::Category::TypeType)
{
@@ -1367,6 +1367,7 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
+ solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types.");
if (_operator == Token::Equal || _operator == Token::NotEqual)
{
if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
@@ -1694,7 +1695,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
existenceChecked = true;
}
@@ -1730,7 +1731,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
//Propagate error condition (if CALL pushes 0 on stack).
m_context << Instruction::ISZERO;
- m_context.appendConditionalInvalid();
+ m_context.appendConditionalRevert();
}
utils().popStackSlots(remainsSize);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index d0a8ac15..3b8cf1c6 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -28,7 +28,7 @@
#include <libevmasm/SourceLocation.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/LValue.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
namespace dev {
namespace eth
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index a74a3d74..e19cf41e 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -186,7 +186,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
solUnimplemented("Not yet implemented - FixedPointType.");
if (m_dataType->category() == Type::Category::FixedBytes)
{
- m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL;
+ CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * m_dataType->storageBytes());
cleaned = true;
}
else if (
@@ -267,9 +267,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
else if (m_dataType->category() == Type::Category::FixedBytes)
{
solAssert(_sourceType.category() == Type::Category::FixedBytes, "source not fixed bytes");
- m_context
- << (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
- << Instruction::SWAP1 << Instruction::DIV;
+ CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes(), false);
}
else
{
diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp
deleted file mode 100644
index b6f17907..00000000
--- a/libsolidity/formal/Why3Translator.cpp
+++ /dev/null
@@ -1,902 +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 2015
- * Component that translates Solidity code into the why3 programming language.
- */
-
-#include <libsolidity/formal/Why3Translator.h>
-#include <boost/algorithm/string/predicate.hpp>
-
-using namespace std;
-using namespace dev;
-using namespace dev::solidity;
-
-bool Why3Translator::process(SourceUnit const& _source)
-{
- try
- {
- if (m_lines.size() != 1 || !m_lines.back().contents.empty())
- fatalError(_source, "Multiple source units not yet supported");
- appendPreface();
- _source.accept(*this);
- }
- catch (NoFormalType&)
- {
- solAssert(false, "There is a call to toFormalType() that does not catch NoFormalType exceptions.");
- }
- catch (FatalError& /*_e*/)
- {
- solAssert(m_errorOccured, "");
- }
- return !m_errorOccured;
-}
-
-string Why3Translator::translation() const
-{
- string result;
- for (auto const& line: m_lines)
- result += string(line.indentation, '\t') + line.contents + "\n";
- return result;
-}
-
-void Why3Translator::error(ASTNode const& _node, string const& _description)
-{
- auto err = make_shared<Error>(Error::Type::Why3TranslatorError);
- *err <<
- errinfo_sourceLocation(_node.location()) <<
- errinfo_comment(_description);
- m_errors.push_back(err);
- m_errorOccured = true;
-}
-
-void Why3Translator::fatalError(ASTNode const& _node, string const& _description)
-{
- error(_node, _description);
- BOOST_THROW_EXCEPTION(FatalError());
-}
-
-string Why3Translator::toFormalType(Type const& _type) const
-{
- if (_type.category() == Type::Category::Bool)
- return "bool";
- else if (auto type = dynamic_cast<IntegerType const*>(&_type))
- {
- if (!type->isAddress() && !type->isSigned() && type->numBits() == 256)
- return "uint256";
- }
- else if (auto type = dynamic_cast<ArrayType const*>(&_type))
- {
- if (!type->isByteArray() && type->isDynamicallySized() && type->dataStoredIn(DataLocation::Memory))
- {
- // Not catching NoFormalType exception. Let the caller deal with it.
- string base = toFormalType(*type->baseType());
- return "array " + base;
- }
- }
- else if (auto mappingType = dynamic_cast<MappingType const*>(&_type))
- {
- solAssert(mappingType->keyType(), "A mappingType misses a keyType.");
- if (dynamic_cast<IntegerType const*>(&*mappingType->keyType()))
- {
- //@TODO Use the information from the key type and specify the length of the array as an invariant.
- // Also the constructor need to specify the length of the array.
- solAssert(mappingType->valueType(), "A mappingType misses a valueType.");
- // Not catching NoFormalType exception. Let the caller deal with it.
- string valueTypeFormal = toFormalType(*mappingType->valueType());
- return "array " + valueTypeFormal;
- }
- }
-
- BOOST_THROW_EXCEPTION(NoFormalType()
- << errinfo_noFormalTypeFrom(_type.toString(true)));
-}
-
-void Why3Translator::addLine(string const& _line)
-{
- newLine();
- add(_line);
- newLine();
-}
-
-void Why3Translator::add(string const& _str)
-{
- m_lines.back().contents += _str;
-}
-
-void Why3Translator::newLine()
-{
- if (!m_lines.back().contents.empty())
- m_lines.push_back({"", m_lines.back().indentation});
-}
-
-void Why3Translator::unindent()
-{
- newLine();
- solAssert(m_lines.back().indentation > 0, "");
- m_lines.back().indentation--;
-}
-
-bool Why3Translator::visit(ContractDefinition const& _contract)
-{
- if (m_seenContract)
- error(_contract, "More than one contract not supported.");
- m_seenContract = true;
- m_currentContract.contract = &_contract;
- if (_contract.isLibrary())
- error(_contract, "Libraries not supported.");
-
- addLine("module Contract_" + _contract.name());
- indent();
- addLine("use import int.Int");
- addLine("use import ref.Ref");
- addLine("use import map.Map");
- addLine("use import array.Array");
- addLine("use import int.ComputerDivision");
- addLine("use import mach.int.Unsigned");
- addLine("use import UInt256");
- addLine("exception Revert");
- addLine("exception Return");
-
- if (_contract.stateVariables().empty())
- addLine("type state = ()");
- else
- {
- addLine("type state = {");
- indent();
- m_currentContract.stateVariables = _contract.stateVariables();
- for (VariableDeclaration const* variable: m_currentContract.stateVariables)
- {
- string varType;
- try
- {
- varType = toFormalType(*variable->annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- string typeName = typeNamePtr ? " \"" + *typeNamePtr + "\"" : "";
- fatalError(*variable, "Type" + typeName + " not supported for state variable.");
- }
- addLine("mutable _" + variable->name() + ": " + varType);
- }
- unindent();
- addLine("}");
- }
-
- addLine("type account = {");
- indent();
- addLine("mutable balance: uint256;");
- addLine("storage: state");
- unindent();
- addLine("}");
-
- addLine("val external_call (this: account): bool");
- indent();
- addLine("ensures { result = false -> this = (old this) }");
- addLine("writes { this }");
- addSourceFromDocStrings(m_currentContract.contract->annotation());
- unindent();
-
- if (!_contract.baseContracts().empty())
- error(*_contract.baseContracts().front(), "Inheritance not supported.");
- if (!_contract.definedStructs().empty())
- error(*_contract.definedStructs().front(), "User-defined types not supported.");
- if (!_contract.definedEnums().empty())
- error(*_contract.definedEnums().front(), "User-defined types not supported.");
- if (!_contract.events().empty())
- error(*_contract.events().front(), "Events not supported.");
- if (!_contract.functionModifiers().empty())
- error(*_contract.functionModifiers().front(), "Modifiers not supported.");
-
- ASTNode::listAccept(_contract.definedFunctions(), *this);
-
- return false;
-}
-
-void Why3Translator::endVisit(ContractDefinition const&)
-{
- m_currentContract.reset();
- unindent();
- addLine("end");
-}
-
-bool Why3Translator::visit(FunctionDefinition const& _function)
-{
- if (!_function.isImplemented())
- {
- error(_function, "Unimplemented functions not supported.");
- return false;
- }
- if (_function.name().empty())
- {
- error(_function, "Fallback functions not supported.");
- return false;
- }
- if (!_function.modifiers().empty())
- {
- error(_function, "Modifiers not supported.");
- return false;
- }
-
- m_localVariables.clear();
- for (auto const& var: _function.parameters())
- m_localVariables[var->name()] = var.get();
- for (auto const& var: _function.returnParameters())
- m_localVariables[var->name()] = var.get();
- for (auto const& var: _function.localVariables())
- m_localVariables[var->name()] = var;
-
- add("let rec _" + _function.name());
- add(" (this: account)");
- for (auto const& param: _function.parameters())
- {
- string paramType;
- try
- {
- paramType = toFormalType(*param->annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- error(*param, "Parameter type \"" + (typeName ? *typeName : "") + "\" not supported.");
- }
- if (param->name().empty())
- error(*param, "Anonymous function parameters not supported.");
- add(" (arg_" + param->name() + ": " + paramType + ")");
- }
- add(":");
-
- indent();
- indent();
- string retString = "(";
- for (auto const& retParam: _function.returnParameters())
- {
- string paramType;
- try
- {
- paramType = toFormalType(*retParam->annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- error(*retParam, "Parameter type " + (typeName ? *typeName : "") + " not supported.");
- }
- if (retString.size() != 1)
- retString += ", ";
- retString += paramType;
- }
- add(retString + ")");
- unindent();
-
- addSourceFromDocStrings(_function.annotation());
- if (!m_currentContract.contract)
- error(_function, "Only functions inside contracts allowed.");
- addSourceFromDocStrings(m_currentContract.contract->annotation());
-
- if (_function.isDeclaredConst())
- addLine("ensures { (old this) = this }");
- else
- addLine("writes { this }");
-
- addLine("=");
-
- // store the prestate in the case we need to revert
- addLine("let prestate = {balance = this.balance; storage = " + copyOfStorage() + "} in ");
-
- // initialise local variables
- for (auto const& variable: _function.parameters())
- addLine("let _" + variable->name() + " = ref arg_" + variable->name() + " in");
- for (auto const& variable: _function.returnParameters())
- {
- if (variable->name().empty())
- error(*variable, "Unnamed return variables not yet supported.");
- string varType;
- try
- {
- varType = toFormalType(*variable->annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in return parameter not yet supported.");
- }
- addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
- }
- for (VariableDeclaration const* variable: _function.localVariables())
- {
- if (variable->name().empty())
- error(*variable, "Unnamed variables not yet supported.");
- string varType;
- try
- {
- varType = toFormalType(*variable->annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in variable declaration not yet supported.");
- }
- addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
- }
- addLine("try");
-
- _function.body().accept(*this);
- add(";");
- addLine("raise Return");
-
- string retVals;
- for (auto const& variable: _function.returnParameters())
- {
- if (!retVals.empty())
- retVals += ", ";
- retVals += "!_" + variable->name();
- }
- addLine("with Return -> (" + retVals + ") |");
- string reversion = " Revert -> this.balance <- prestate.balance; ";
- for (auto const* variable: m_currentContract.stateVariables)
- reversion += "this.storage._" + variable->name() + " <- prestate.storage._" + variable->name() + "; ";
- //@TODO in case of reversion the return values are wrong - we need to change the
- // return type to include a bool to signify if an exception was thrown.
- reversion += "(" + retVals + ")";
- addLine(reversion);
- unindent();
- addLine("end");
- addLine("");
- return false;
-}
-
-void Why3Translator::endVisit(FunctionDefinition const&)
-{
- m_localVariables.clear();
-}
-
-bool Why3Translator::visit(Block const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
- add("begin");
- indent();
- for (size_t i = 0; i < _node.statements().size(); ++i)
- {
- _node.statements()[i]->accept(*this);
- if (i != _node.statements().size() - 1)
- {
- auto it = m_lines.end() - 1;
- while (it != m_lines.begin() && it->contents.empty())
- --it;
- if (!boost::algorithm::ends_with(it->contents, "begin"))
- it->contents += ";";
- }
- newLine();
- }
- unindent();
- add("end");
- return false;
-}
-
-bool Why3Translator::visit(IfStatement const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
-
- add("if ");
- _node.condition().accept(*this);
- add(" then");
- visitIndentedUnlessBlock(_node.trueStatement());
- if (_node.falseStatement())
- {
- newLine();
- add("else");
- visitIndentedUnlessBlock(*_node.falseStatement());
- }
- return false;
-}
-
-bool Why3Translator::visit(WhileStatement const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
-
- // Why3 does not appear to support do-while loops,
- // so we will simulate them by performing a while
- // loop with the body prepended once.
-
- if (_node.isDoWhile())
- {
- visitIndentedUnlessBlock(_node.body());
- newLine();
- }
-
- add("while ");
- _node.condition().accept(*this);
- newLine();
- add("do");
- visitIndentedUnlessBlock(_node.body());
- add("done");
- return false;
-}
-
-bool Why3Translator::visit(Return const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
-
- if (_node.expression())
- {
- solAssert(!!_node.annotation().functionReturnParameters, "");
- auto const& params = _node.annotation().functionReturnParameters->parameters();
- if (params.size() != 1)
- {
- error(_node, "Directly returning tuples not supported. Rather assign to return variable.");
- return false;
- }
- add("begin _" + params.front()->name() + " := ");
- _node.expression()->accept(*this);
- add("; raise Return end");
- }
- else
- add("raise Return");
- return false;
-}
-
-bool Why3Translator::visit(Throw const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
- add("raise Revert");
- return false;
-}
-
-bool Why3Translator::visit(VariableDeclarationStatement const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
-
- if (_node.declarations().size() != 1)
- {
- error(_node, "Multiple variables not supported.");
- return false;
- }
- if (_node.initialValue())
- {
- add("_" + _node.declarations().front()->name() + " := ");
- _node.initialValue()->accept(*this);
- }
- return false;
-}
-
-bool Why3Translator::visit(ExpressionStatement const& _node)
-{
- addSourceFromDocStrings(_node.annotation());
- return true;
-}
-
-bool Why3Translator::visit(Assignment const& _node)
-{
- if (_node.assignmentOperator() != Token::Assign)
- error(_node, "Compound assignment not supported.");
-
- _node.leftHandSide().accept(*this);
-
- add(m_currentLValueIsRef ? " := " : " <- ");
- _node.rightHandSide().accept(*this);
-
- return false;
-}
-
-bool Why3Translator::visit(TupleExpression const& _node)
-{
- if (_node.components().size() != 1)
- error(_node, "Only tuples with exactly one component supported.");
- add("(");
- return true;
-}
-
-bool Why3Translator::visit(UnaryOperation const& _unaryOperation)
-{
- try
- {
- toFormalType(*_unaryOperation.annotation().type);
- }
- catch (NoFormalType &err)
- {
- string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
- error(_unaryOperation, "Type \"" + (typeNamePtr ? *typeNamePtr : "") + "\" supported in unary operation.");
- }
-
- switch (_unaryOperation.getOperator())
- {
- case Token::Not: // !
- add("(not ");
- break;
- default:
- error(_unaryOperation, "Operator not supported.");
- break;
- }
-
- _unaryOperation.subExpression().accept(*this);
- add(")");
-
- return false;
-}
-
-bool Why3Translator::visit(BinaryOperation const& _binaryOperation)
-{
- Expression const& leftExpression = _binaryOperation.leftExpression();
- Expression const& rightExpression = _binaryOperation.rightExpression();
- solAssert(!!_binaryOperation.annotation().commonType, "");
- Type const& commonType = *_binaryOperation.annotation().commonType;
- Token::Value const c_op = _binaryOperation.getOperator();
-
- if (commonType.category() == Type::Category::RationalNumber)
- {
- auto const& constantNumber = dynamic_cast<RationalNumberType const&>(commonType);
- if (constantNumber.isFractional())
- error(_binaryOperation, "Fractional numbers not supported.");
- else
- add("(of_int " + toString(commonType.literalValue(nullptr)) + ")");
- return false;
- }
- static const map<Token::Value, char const*> optrans({
- {Token::And, " && "},
- {Token::Or, " || "},
- {Token::BitOr, " lor "},
- {Token::BitXor, " lxor "},
- {Token::BitAnd, " land "},
- {Token::Add, " + "},
- {Token::Sub, " - "},
- {Token::Mul, " * "},
- {Token::Div, " / "},
- {Token::Mod, " mod "},
- {Token::Equal, " = "},
- {Token::NotEqual, " <> "},
- {Token::LessThan, " < "},
- {Token::GreaterThan, " > "},
- {Token::LessThanOrEqual, " <= "},
- {Token::GreaterThanOrEqual, " >= "}
- });
- if (!optrans.count(c_op))
- {
- error(_binaryOperation, "Operator not supported.");
- return true;
- }
-
- add("(");
- leftExpression.accept(*this);
- add(optrans.at(c_op));
- rightExpression.accept(*this);
- add(")");
-
- return false;
-}
-
-bool Why3Translator::visit(FunctionCall const& _node)
-{
- if (_node.annotation().isTypeConversion || _node.annotation().isStructConstructorCall)
- {
- error(_node, "Only ordinary function calls supported.");
- return true;
- }
- FunctionType const& function = dynamic_cast<FunctionType const&>(*_node.expression().annotation().type);
- switch (function.kind())
- {
- case FunctionType::Kind::AddMod:
- case FunctionType::Kind::MulMod:
- {
- //@todo require that third parameter is not zero
- add("(of_int (mod (Int.(");
- add(function.kind() == FunctionType::Kind::AddMod ? "+" : "*");
- add(") (to_int ");
- _node.arguments().at(0)->accept(*this);
- add(") (to_int ");
- _node.arguments().at(1)->accept(*this);
- add(")) (to_int ");
- _node.arguments().at(2)->accept(*this);
- add(")))");
- return false;
- }
- case FunctionType::Kind::Internal:
- {
- if (!_node.names().empty())
- {
- error(_node, "Function calls with named arguments not supported.");
- return true;
- }
-
- //@TODO check type conversions
-
- add("(");
- _node.expression().accept(*this);
- add(" state");
- for (auto const& arg: _node.arguments())
- {
- add(" ");
- arg->accept(*this);
- }
- add(")");
- return false;
- }
- case FunctionType::Kind::Bare:
- {
- if (!_node.arguments().empty())
- {
- error(_node, "Function calls with named arguments not supported.");
- return true;
- }
-
- add("(");
- indent();
- add("let amount = 0 in ");
- _node.expression().accept(*this);
- addLine("if amount <= this.balance then");
- indent();
- addLine("let balance_precall = this.balance in");
- addLine("begin");
- indent();
- addLine("this.balance <- this.balance - amount;");
- addLine("if not (external_call this) then begin this.balance = balance_precall; false end else true");
- unindent();
- addLine("end");
- unindent();
- addLine("else false");
-
- unindent();
- add(")");
- return false;
- }
- case FunctionType::Kind::SetValue:
- {
- add("let amount = ");
- solAssert(_node.arguments().size() == 1, "");
- _node.arguments()[0]->accept(*this);
- add(" in ");
- return false;
- }
- default:
- error(_node, "Only internal function calls supported.");
- return true;
- }
-}
-
-bool Why3Translator::visit(MemberAccess const& _node)
-{
- if (
- _node.expression().annotation().type->category() == Type::Category::Array &&
- _node.memberName() == "length" &&
- !_node.annotation().lValueRequested
- )
- {
- add("(of_int ");
- _node.expression().accept(*this);
- add(".length");
- add(")");
- }
- else if (
- _node.memberName() == "call" &&
- *_node.expression().annotation().type == IntegerType(160, IntegerType::Modifier::Address)
- )
- {
- // Do nothing, do not even visit the address because this will be an external call
- //@TODO ensure that the expression itself does not have side-effects
- return false;
- }
- else
- error(_node, "Member access: Only call and array length supported.");
- return false;
-}
-
-bool Why3Translator::visit(IndexAccess const& _node)
-{
- auto baseType = dynamic_cast<ArrayType const*>(_node.baseExpression().annotation().type.get());
- if (!baseType)
- {
- error(_node, "Index access only supported for arrays.");
- return true;
- }
- if (_node.annotation().lValueRequested)
- {
- error(_node, "Assignment to array elements not supported.");
- return true;
- }
- add("(");
- _node.baseExpression().accept(*this);
- add("[to_int ");
- _node.indexExpression()->accept(*this);
- add("]");
- add(")");
-
- return false;
-}
-
-bool Why3Translator::visit(Identifier const& _identifier)
-{
- Declaration const* declaration = _identifier.annotation().referencedDeclaration;
- if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
- add("_" + functionDef->name());
- else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
- {
- bool isStateVar = isStateVariable(variable);
- bool lvalue = _identifier.annotation().lValueRequested;
- if (isStateVar)
- add("this.storage.");
- else if (!lvalue)
- add("!(");
- add("_" + variable->name());
- if (!isStateVar && !lvalue)
- add(")");
- m_currentLValueIsRef = !isStateVar;
- }
- else
- error(_identifier, "Not supported.");
- return false;
-}
-
-bool Why3Translator::visit(Literal const& _literal)
-{
- TypePointer type = _literal.annotation().type;
- switch (type->category())
- {
- case Type::Category::Bool:
- if (type->literalValue(&_literal) == 0)
- add("false");
- else
- add("true");
- break;
- case Type::Category::RationalNumber:
- {
- auto const& constantNumber = dynamic_cast<RationalNumberType const&>(*type);
- if (constantNumber.isFractional())
- error(_literal, "Fractional numbers not supported.");
- else
- add("(of_int " + toString(type->literalValue(&_literal)) + ")");
- break;
- }
- default:
- error(_literal, "Not supported.");
- }
- return false;
-}
-
-bool Why3Translator::visit(PragmaDirective const& _pragma)
-{
- if (_pragma.tokens().empty())
- error(_pragma, "Not supported");
- else if (_pragma.literals().empty())
- error(_pragma, "Not supported");
- else if (_pragma.literals()[0] != "solidity")
- error(_pragma, "Not supported");
- else if (_pragma.tokens()[0] != Token::Identifier)
- error(_pragma, "A literal 'solidity' is not an identifier. Strange");
-
- return false;
-}
-
-bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const
-{
- return contains(m_currentContract.stateVariables, _var);
-}
-
-bool Why3Translator::isStateVariable(string const& _name) const
-{
- for (auto const& var: m_currentContract.stateVariables)
- if (var->name() == _name)
- return true;
- return false;
-}
-
-bool Why3Translator::isLocalVariable(VariableDeclaration const* _var) const
-{
- for (auto const& var: m_localVariables)
- if (var.second == _var)
- return true;
- return false;
-}
-
-bool Why3Translator::isLocalVariable(string const& _name) const
-{
- return m_localVariables.count(_name);
-}
-
-string Why3Translator::copyOfStorage() const
-{
- if (m_currentContract.stateVariables.empty())
- return "()";
- string ret = "{";
- bool first = true;
- for (auto const* variable: m_currentContract.stateVariables)
- {
- if (first)
- first = false;
- else
- ret += "; ";
- ret += "_" + variable->name() + " = this.storage._" + variable->name();
- }
- return ret + "}";
-}
-
-void Why3Translator::visitIndentedUnlessBlock(Statement const& _statement)
-{
- bool isBlock = !!dynamic_cast<Block const*>(&_statement);
- if (isBlock)
- newLine();
- else
- indent();
- _statement.accept(*this);
- if (isBlock)
- newLine();
- else
- unindent();
-}
-
-void Why3Translator::addSourceFromDocStrings(DocumentedAnnotation const& _annotation)
-{
- auto why3Range = _annotation.docTags.equal_range("why3");
- for (auto i = why3Range.first; i != why3Range.second; ++i)
- addLine(transformVariableReferences(i->second.content));
-}
-
-string Why3Translator::transformVariableReferences(string const& _annotation)
-{
- string ret;
- auto pos = _annotation.begin();
- while (true)
- {
- auto hash = find(pos, _annotation.end(), '#');
- ret.append(pos, hash);
- if (hash == _annotation.end())
- break;
-
- auto hashEnd = find_if(hash + 1, _annotation.end(), [](char _c)
- {
- return
- (_c != '_' && _c != '$') &&
- !('a' <= _c && _c <= 'z') &&
- !('A' <= _c && _c <= 'Z') &&
- !('0' <= _c && _c <= '9');
- });
- string varName(hash + 1, hashEnd);
- if (isLocalVariable(varName))
- ret += "(!_" + varName + ")";
- else if (isStateVariable(varName))
- ret += "(this.storage._" + varName + ")";
- //@todo return variables
- else
- ret.append(hash, hashEnd);
-
- pos = hashEnd;
- }
- return ret;
-}
-
-void Why3Translator::appendPreface()
-{
- m_lines.push_back(Line{R"(
-module UInt256
- use import mach.int.Unsigned
- type uint256
- constant max_uint256: int = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
- clone export mach.int.Unsigned with
- type t = uint256,
- constant max = max_uint256
-end
-
-module Address
- use import mach.int.Unsigned
- type address
- constant max_address: int = 0xffffffffffffffffffffffffffffffffffffffff (* 160 bit = 40 f's *)
- clone export mach.int.Unsigned with
- type t = address,
- constant max = max_address
-end
- )", 0});
-}
diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h
deleted file mode 100644
index 03f3bf9c..00000000
--- a/libsolidity/formal/Why3Translator.h
+++ /dev/null
@@ -1,149 +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 2015
- * Component that translates Solidity code into the why3 programming language.
- */
-
-#pragma once
-
-#include <libsolidity/ast/ASTVisitor.h>
-#include <libsolidity/interface/Exceptions.h>
-#include <string>
-
-namespace dev
-{
-namespace solidity
-{
-
-class SourceUnit;
-
-/**
- * Simple translator from Solidity to Why3.
- *
- * @todo detect side effects in sub-expressions and limit them to one per statement. #1043
- * @todo `x = y = z`
- * @todo implicit and explicit type conversion
- */
-class Why3Translator: private ASTConstVisitor
-{
-public:
- Why3Translator(ErrorList& _errors): m_lines(std::vector<Line>{{std::string(), 0}}), m_errors(_errors) {}
-
- /// Appends formalisation of the given source unit to the output.
- /// @returns false on error.
- bool process(SourceUnit const& _source);
-
- std::string translation() const;
-
-private:
- /// Creates an error and adds it to errors list.
- void error(ASTNode const& _node, std::string const& _description);
- /// Reports a fatal error and throws.
- void fatalError(ASTNode const& _node, std::string const& _description);
-
- /// Appends imports and constants use throughout the formal code.
- void appendPreface();
-
- /// @returns a string representation of the corresponding formal type or throws NoFormalType exception.
- std::string toFormalType(Type const& _type) const;
- using errinfo_noFormalTypeFrom = boost::error_info<struct tag_noFormalTypeFrom, std::string /* name of the type that cannot be translated */ >;
- struct NoFormalType: virtual Exception {};
-
- void indent() { newLine(); m_lines.back().indentation++; }
- void unindent();
- void addLine(std::string const& _line);
- void add(std::string const& _str);
- void newLine();
- void appendSemicolon();
-
- virtual bool visit(SourceUnit const&) override { return true; }
- virtual bool visit(ContractDefinition const& _contract) override;
- virtual void endVisit(ContractDefinition const& _contract) override;
- virtual bool visit(FunctionDefinition const& _function) override;
- virtual void endVisit(FunctionDefinition const& _function) override;
- virtual bool visit(Block const&) override;
- virtual bool visit(IfStatement const& _node) override;
- virtual bool visit(WhileStatement const& _node) override;
- virtual bool visit(Return const& _node) override;
- virtual bool visit(Throw const& _node) override;
- virtual bool visit(VariableDeclarationStatement const& _node) override;
- virtual bool visit(ExpressionStatement const&) override;
- virtual bool visit(Assignment const& _node) override;
- virtual bool visit(TupleExpression const& _node) override;
- virtual void endVisit(TupleExpression const&) override { add(")"); }
- virtual bool visit(UnaryOperation const& _node) override;
- virtual bool visit(BinaryOperation const& _node) override;
- virtual bool visit(FunctionCall const& _node) override;
- virtual bool visit(MemberAccess const& _node) override;
- virtual bool visit(IndexAccess const& _node) override;
- virtual bool visit(Identifier const& _node) override;
- virtual bool visit(Literal const& _node) override;
- virtual bool visit(PragmaDirective const& _node) override;
-
- virtual bool visitNode(ASTNode const& _node) override
- {
- error(_node, "Code not supported for formal verification.");
- return false;
- }
-
- bool isStateVariable(VariableDeclaration const* _var) const;
- bool isStateVariable(std::string const& _name) const;
- bool isLocalVariable(VariableDeclaration const* _var) const;
- bool isLocalVariable(std::string const& _name) const;
-
- /// @returns a string representing an expression that is a copy of this.storage
- std::string copyOfStorage() const;
-
- /// Visits the given statement and indents it unless it is a block
- /// (which does its own indentation).
- void visitIndentedUnlessBlock(Statement const& _statement);
-
- void addSourceFromDocStrings(DocumentedAnnotation const& _annotation);
- /// Transforms substring like `#varName` and `#stateVarName` to code that evaluates to their value.
- std::string transformVariableReferences(std::string const& _annotation);
-
- /// True if we have already seen a contract. For now, only a single contract
- /// is supported.
- bool m_seenContract = false;
- bool m_errorOccured = false;
-
- /// Metadata relating to the current contract
- struct ContractMetadata
- {
- ContractDefinition const* contract = nullptr;
- std::vector<VariableDeclaration const*> stateVariables;
-
- void reset() { contract = nullptr; stateVariables.clear(); }
- };
-
- ContractMetadata m_currentContract;
- bool m_currentLValueIsRef = false;
- std::map<std::string, VariableDeclaration const*> m_localVariables;
-
- struct Line
- {
- std::string contents;
- unsigned indentation;
- };
- std::vector<Line> m_lines;
- ErrorList& m_errors;
-};
-
-}
-}
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/interface/Utils.h b/libsolidity/inlineasm/AsmDataForward.h
index 0027759c..4ead7ff5 100644
--- a/libsolidity/interface/Utils.h
+++ b/libsolidity/inlineasm/AsmDataForward.h
@@ -16,30 +16,37 @@
*/
/**
* @author Christian <c@ethdev.com>
- * @date 2014
- * Solidity Utilities.
+ * @date 2016
+ * Forward declaration of classes for inline assembly / JULIA AST
*/
#pragma once
-#include <libdevcore/Assertions.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <boost/variant.hpp>
namespace dev
{
namespace solidity
{
-struct InternalCompilerError;
-struct UnimplementedFeatureError;
-}
-}
-
-/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
-#define solAssert(CONDITION, DESCRIPTION) \
- assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
+namespace assembly
+{
-#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
- assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
+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>;
-#define solUnimplemented(DESCRIPTION) \
- solUnimplementedAssert(false, DESCRIPTION)
+}
+}
+}
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;
-};
-
-}
-}
-}
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp
new file mode 100644
index 00000000..12f958fc
--- /dev/null
+++ b/libsolidity/interface/ABI.cpp
@@ -0,0 +1,116 @@
+/*
+ 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/>.
+*/
+/**
+ * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
+ */
+
+#include <libsolidity/interface/ABI.h>
+#include <boost/range/irange.hpp>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+Json::Value ABI::generate(ContractDefinition const& _contractDef)
+{
+ Json::Value abi(Json::arrayValue);
+
+ for (auto it: _contractDef.interfaceFunctions())
+ {
+ auto externalFunctionType = it.second->interfaceFunctionType();
+ Json::Value method;
+ method["type"] = "function";
+ method["name"] = it.second->declaration().name();
+ method["constant"] = it.second->isConstant();
+ method["payable"] = it.second->isPayable();
+ method["inputs"] = formatTypeList(
+ externalFunctionType->parameterNames(),
+ externalFunctionType->parameterTypes(),
+ _contractDef.isLibrary()
+ );
+ method["outputs"] = formatTypeList(
+ externalFunctionType->returnParameterNames(),
+ externalFunctionType->returnParameterTypes(),
+ _contractDef.isLibrary()
+ );
+ abi.append(method);
+ }
+ if (_contractDef.constructor())
+ {
+ Json::Value method;
+ method["type"] = "constructor";
+ auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
+ solAssert(!!externalFunction, "");
+ method["payable"] = externalFunction->isPayable();
+ method["inputs"] = formatTypeList(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypes(),
+ _contractDef.isLibrary()
+ );
+ abi.append(method);
+ }
+ if (_contractDef.fallbackFunction())
+ {
+ auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
+ solAssert(!!externalFunctionType, "");
+ Json::Value method;
+ method["type"] = "fallback";
+ method["payable"] = externalFunctionType->isPayable();
+ abi.append(method);
+ }
+ for (auto const& it: _contractDef.interfaceEvents())
+ {
+ Json::Value event;
+ event["type"] = "event";
+ event["name"] = it->name();
+ event["anonymous"] = it->isAnonymous();
+ Json::Value params(Json::arrayValue);
+ for (auto const& p: it->parameters())
+ {
+ solAssert(!!p->annotation().type->interfaceType(false), "");
+ Json::Value input;
+ input["name"] = p->name();
+ input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false);
+ input["indexed"] = p->isIndexed();
+ params.append(input);
+ }
+ event["inputs"] = params;
+ abi.append(event);
+ }
+
+ return abi;
+}
+
+Json::Value ABI::formatTypeList(
+ vector<string> const& _names,
+ vector<TypePointer> const& _types,
+ bool _forLibrary
+)
+{
+ Json::Value params(Json::arrayValue);
+ solAssert(_names.size() == _types.size(), "Names and types vector size does not match");
+ for (unsigned i = 0; i < _names.size(); ++i)
+ {
+ solAssert(_types[i], "");
+ Json::Value param;
+ param["name"] = _names[i];
+ param["type"] = _types[i]->canonicalName(_forLibrary);
+ params.append(param);
+ }
+ return params;
+}
diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h
new file mode 100644
index 00000000..95b162a9
--- /dev/null
+++ b/libsolidity/interface/ABI.h
@@ -0,0 +1,56 @@
+/*
+ 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/>.
+*/
+/**
+ * Utilities to handle the Contract ABI (https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
+ */
+
+#pragma once
+
+#include <string>
+#include <memory>
+#include <json/json.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+// Forward declarations
+class ContractDefinition;
+class Type;
+using TypePointer = std::shared_ptr<Type const>;
+
+class ABI
+{
+public:
+ /// Get the ABI Interface of the contract
+ /// @param _contractDef The contract definition
+ /// @return A JSONrepresentation of the contract's ABI Interface
+ static Json::Value generate(ContractDefinition const& _contractDef);
+private:
+ /// @returns a json value suitable for a list of types in function input or output
+ /// parameters or other places. If @a _forLibrary is true, complex types are referenced
+ /// by name, otherwise they are anonymously expanded.
+ static Json::Value formatTypeList(
+ std::vector<std::string> const& _names,
+ std::vector<TypePointer> const& _types,
+ bool _forLibrary
+ );
+};
+
+}
+}
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
new file mode 100644
index 00000000..23524bb3
--- /dev/null
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -0,0 +1,119 @@
+/*
+ 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/AsmAnalysisInfo.h>
+#include <libsolidity/inlineasm/AsmCodeGen.h>
+
+#include <libevmasm/Assembly.h>
+
+#include <libjulia/backends/evm/EVMCodeTransform.h>
+#include <libjulia/backends/evm/EVMAssembly.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_errors.clear();
+ m_analysisSuccessful = false;
+ m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
+ m_parserResult = assembly::Parser(m_errorReporter, m_language == Language::JULIA).parse(m_scanner);
+ if (!m_errorReporter.errors().empty())
+ return false;
+ solAssert(m_parserResult, "");
+
+ return analyzeParsed();
+}
+
+bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scanner)
+{
+ m_errors.clear();
+ m_analysisSuccessful = false;
+ if (_scanner)
+ m_scanner = make_shared<Scanner>(*_scanner);
+ m_parserResult = make_shared<assembly::Block>(_block);
+
+ return analyzeParsed();
+}
+
+bool AssemblyStack::analyzeParsed()
+{
+ m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
+ assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_language == Language::JULIA);
+ m_analysisSuccessful = analyzer.analyze(*m_parserResult);
+ return m_analysisSuccessful;
+}
+
+MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
+{
+ solAssert(m_analysisSuccessful, "");
+ solAssert(m_parserResult, "");
+ solAssert(m_analysisInfo, "");
+
+ switch (_machine)
+ {
+ case Machine::EVM:
+ {
+ MachineAssemblyObject object;
+ eth::Assembly assembly;
+ assembly::CodeGenerator::assemble(*m_parserResult, *m_analysisInfo, assembly);
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
+ ostringstream tmp;
+ assembly.stream(tmp);
+ object.assembly = tmp.str();
+ return object;
+ }
+ case Machine::EVM15:
+ {
+ MachineAssemblyObject object;
+ julia::EVMAssembly assembly(true);
+ julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::JULIA, true)(*m_parserResult);
+ object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
+ /// TOOD: fill out text representation
+ return object;
+ }
+ case Machine::eWasm:
+ solUnimplemented("eWasm backend is not yet implemented.");
+ }
+ // unreachable
+ return MachineAssemblyObject();
+}
+
+string AssemblyStack::print() const
+{
+ 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..2ae596ed
--- /dev/null
+++ b/libsolidity/interface/AssemblyStack.h
@@ -0,0 +1,96 @@
+/*
+ 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/ErrorReporter.h>
+#include <libevmasm/LinkerObject.h>
+
+#include <string>
+#include <memory>
+
+namespace dev
+{
+namespace solidity
+{
+class Scanner;
+namespace assembly
+{
+struct AsmAnalysisInfo;
+struct Block;
+}
+
+struct MachineAssemblyObject
+{
+ std::shared_ptr<eth::LinkerObject> bytecode;
+ std::string assembly;
+};
+
+/*
+ * 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), m_errorReporter(m_errors)
+ {}
+
+ /// @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);
+
+ /// Runs analysis step on the supplied block, returns false if input cannot be assembled.
+ /// Multiple calls overwrite the previous state.
+ bool analyze(assembly::Block const& _block, Scanner const* _scanner = nullptr);
+
+ /// Run the assembly step (should only be called after parseAndAnalyze).
+ MachineAssemblyObject assemble(Machine _machine) const;
+
+ /// @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() const;
+
+private:
+ bool analyzeParsed();
+
+ 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;
+ ErrorReporter m_errorReporter;
+};
+
+}
+}
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 9c9c9614..e2507821 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -37,9 +37,9 @@
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h>
-#include <libsolidity/interface/InterfaceHandler.h>
+#include <libsolidity/interface/ABI.h>
+#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
-#include <libsolidity/formal/Why3Translator.h>
#include <libevmasm/Exceptions.h>
@@ -56,9 +56,6 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-CompilerStack::CompilerStack(ReadFile::Callback const& _readFile):
- m_readFile(_readFile) {}
-
void CompilerStack::setRemappings(vector<string> const& _remappings)
{
vector<Remapping> remappings;
@@ -87,6 +84,7 @@ void CompilerStack::reset(bool _keepSources)
}
else
{
+ m_stackState = Empty;
m_sources.clear();
}
m_optimize = false;
@@ -95,8 +93,7 @@ void CompilerStack::reset(bool _keepSources)
m_scopes.clear();
m_sourceOrder.clear();
m_contracts.clear();
- m_errors.clear();
- m_stackState = Empty;
+ m_errorReporter.clear();
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
@@ -120,15 +117,11 @@ bool CompilerStack::parse()
//reset
if(m_stackState != SourcesSet)
return false;
- m_errors.clear();
+ m_errorReporter.clear();
ASTNode::resetID();
if (SemVerVersion{string(VersionString)}.isPrerelease())
- {
- auto err = make_shared<Error>(Error::Type::Warning);
- *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production.");
- m_errors.push_back(err);
- }
+ m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
vector<string> sourcesToParse;
for (auto const& s: m_sources)
@@ -138,9 +131,9 @@ bool CompilerStack::parse()
string const& path = sourcesToParse[i];
Source& source = m_sources[path];
source.scanner->reset();
- source.ast = Parser(m_errors).parse(source.scanner);
+ source.ast = Parser(m_errorReporter).parse(source.scanner);
if (!source.ast)
- solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
+ solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error.");
else
{
source.ast->annotation().path = path;
@@ -153,7 +146,7 @@ bool CompilerStack::parse()
}
}
}
- if (Error::containsOnlyWarnings(m_errors))
+ if (Error::containsOnlyWarnings(m_errorReporter.errors()))
{
m_stackState = ParsingSuccessful;
return true;
@@ -169,18 +162,18 @@ bool CompilerStack::analyze()
resolveImports();
bool noErrors = true;
- SyntaxChecker syntaxChecker(m_errors);
+ SyntaxChecker syntaxChecker(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!syntaxChecker.checkSyntax(*source->ast))
noErrors = false;
- DocStringAnalyser docStringAnalyser(m_errors);
+ DocStringAnalyser docStringAnalyser(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!docStringAnalyser.analyseDocStrings(*source->ast))
noErrors = false;
m_globalContext = make_shared<GlobalContext>();
- NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errors);
+ NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!resolver.registerDeclarations(*source->ast))
return false;
@@ -216,11 +209,11 @@ bool CompilerStack::analyze()
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->currentThis());
- TypeChecker typeChecker(m_errors);
+ TypeChecker typeChecker(m_errorReporter);
if (typeChecker.checkTypeRequirements(*contract))
{
- contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract));
- contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract));
+ contract->setDevDocumentation(Natspec::devDocumentation(*contract));
+ contract->setUserDocumentation(Natspec::userDocumentation(*contract));
}
else
noErrors = false;
@@ -236,7 +229,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
- PostTypeChecker postTypeChecker(m_errors);
+ PostTypeChecker postTypeChecker(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!postTypeChecker.check(*source->ast))
noErrors = false;
@@ -244,7 +237,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
- StaticAnalyzer staticAnalyzer(m_errors);
+ StaticAnalyzer staticAnalyzer(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!staticAnalyzer.analyze(*source->ast))
noErrors = false;
@@ -322,20 +315,6 @@ void CompilerStack::link()
}
}
-bool CompilerStack::prepareFormalAnalysis(ErrorList* _errors)
-{
- if (!_errors)
- _errors = &m_errors;
- Why3Translator translator(*_errors);
- for (Source const* source: m_sourceOrder)
- if (!translator.process(*source->ast))
- return false;
-
- m_formalTranslation = translator.translation();
-
- return true;
-}
-
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
{
Contract const& currentContract = contract(_contractName);
@@ -406,15 +385,6 @@ eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName)
return contract(_contractName).cloneObject;
}
-dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
-{
- auto const& obj = runtimeObject(_contractName);
- if (obj.bytecode.empty() || !obj.linkReferences.empty())
- return dev::h256();
- else
- return dev::keccak256(obj.bytecode);
-}
-
Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
{
Contract const& currentContract = contract(_contractName);
@@ -444,20 +414,31 @@ map<string, unsigned> CompilerStack::sourceIndices() const
return indices;
}
-Json::Value const& CompilerStack::interface(string const& _contractName) const
+Json::Value const& CompilerStack::contractABI(string const& _contractName) const
{
- return metadata(_contractName, DocumentationType::ABIInterface);
+ return contractABI(contract(_contractName));
}
-Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
+Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
- return metadata(contract(_contractName), _type);
+ solAssert(_contract.contract, "");
+
+ // caches the result
+ if (!_contract.abi)
+ _contract.abi.reset(new Json::Value(ABI::generate(*_contract.contract)));
+
+ return *_contract.abi;
}
-Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const
+Json::Value const& CompilerStack::natspec(string const& _contractName, DocumentationType _type) const
+{
+ return natspec(contract(_contractName), _type);
+}
+
+Json::Value const& CompilerStack::natspec(Contract const& _contract, DocumentationType _type) const
{
if (m_stackState < AnalysisSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
@@ -474,40 +455,54 @@ Json::Value const& CompilerStack::metadata(Contract const& _contract, Documentat
case DocumentationType::NatspecDev:
doc = &_contract.devDocumentation;
break;
- case DocumentationType::ABIInterface:
- doc = &_contract.interface;
- break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
// caches the result
if (!*doc)
- doc->reset(new Json::Value(InterfaceHandler::documentation(*_contract.contract, _type)));
+ doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type)));
return *(*doc);
}
+Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
+{
+ Json::Value methodIdentifiers(Json::objectValue);
+ for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
+ methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ return methodIdentifiers;
+}
+
string const& CompilerStack::onChainMetadata(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
return contract(_contractName).onChainMetadata;
}
Scanner const& CompilerStack::scanner(string const& _sourceName) const
{
+ if (m_stackState < SourcesSet)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set."));
+
return *source(_sourceName).scanner;
}
SourceUnit const& CompilerStack::ast(string const& _sourceName) const
{
+ if (m_stackState < ParsingSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+
return *source(_sourceName).ast;
}
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
return *contract(_contractName).contract;
}
@@ -564,11 +559,10 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
newSources[importPath] = result.contentsOrErrorMessage;
else
{
- auto err = make_shared<Error>(Error::Type::ParserError);
- *err <<
- errinfo_sourceLocation(import->location()) <<
- errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage);
- m_errors.push_back(std::move(err));
+ m_errorReporter.parserError(
+ import->location(),
+ string("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage)
+ );
continue;
}
}
@@ -734,11 +728,6 @@ void CompilerStack::compileContract(
}
}
-std::string CompilerStack::defaultContractName() const
-{
- return contract("").contract->name();
-}
-
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
{
if (m_contracts.empty())
@@ -821,9 +810,9 @@ string CompilerStack::createOnChainMetadata(Contract const& _contract) const
for (auto const& library: m_libraries)
meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes());
- meta["output"]["abi"] = metadata(_contract, DocumentationType::ABIInterface);
- meta["output"]["userdoc"] = metadata(_contract, DocumentationType::NatspecUser);
- meta["output"]["devdoc"] = metadata(_contract, DocumentationType::NatspecDev);
+ meta["output"]["abi"] = contractABI(_contract);
+ meta["output"]["userdoc"] = natspec(_contract, DocumentationType::NatspecUser);
+ meta["output"]["devdoc"] = natspec(_contract, DocumentationType::NatspecDev);
return jsonCompactPrint(meta);
}
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index c1d344ca..03a1b806 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -35,7 +35,7 @@
#include <libdevcore/FixedHash.h>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/LinkerObject.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/ReadFile.h>
namespace dev
@@ -59,15 +59,14 @@ class FunctionDefinition;
class SourceUnit;
class Compiler;
class GlobalContext;
-class InterfaceHandler;
+class Natspec;
class Error;
class DeclarationContainer;
enum class DocumentationType: uint8_t
{
NatspecUser = 1,
- NatspecDev,
- ABIInterface
+ NatspecDev
};
/**
@@ -78,10 +77,21 @@ enum class DocumentationType: uint8_t
class CompilerStack: boost::noncopyable
{
public:
+ enum State {
+ Empty,
+ SourcesSet,
+ ParsingSuccessful,
+ AnalysisSuccessful,
+ CompilationSuccessful
+ };
+
/// Creates a new compiler stack.
/// @param _readFile callback to used to read files for import statements. Must return
/// and must not emit exceptions.
- explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback());
+ explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()):
+ m_readFile(_readFile),
+ m_errorList(),
+ m_errorReporter(m_errorList) {}
/// Sets path remappings in the format "context:prefix=target"
void setRemappings(std::vector<std::string> const& _remappings);
@@ -115,7 +125,6 @@ public:
bool parseAndAnalyze(std::string const& _sourceCode);
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
- std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed.
/// @returns false on error.
@@ -128,12 +137,6 @@ public:
/// @returns false on error.
bool compile(std::string const& _sourceCode, bool _optimize = false, unsigned _runs = 200);
- /// Tries to translate all source files into a language suitable for formal analysis.
- /// @param _errors list to store errors - defaults to the internal error list.
- /// @returns false on error.
- bool prepareFormalAnalysis(ErrorList* _errors = nullptr);
- std::string const& formalTranslation() const { return m_formalTranslation; }
-
/// @returns the assembled object for a contract.
eth::LinkerObject const& object(std::string const& _contractName = "") const;
/// @returns the runtime object for the contract.
@@ -157,11 +160,6 @@ public:
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
std::string const filesystemFriendlyName(std::string const& _contractName) const;
- /// @returns hash of the runtime bytecode for the contract, i.e. the code that is
- /// returned by the constructor or the zero-h256 if the contract still needs to be linked or
- /// does not have runtime code.
- dev::h256 contractCodeHash(std::string const& _contractName = "") const;
-
/// Streams a verbose version of the assembly to @a _outStream.
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
@@ -173,14 +171,18 @@ public:
/// @returns a mapping assigning each source name its index inside the vector returned
/// by sourceNames().
std::map<std::string, unsigned> sourceIndices() const;
- /// @returns a JSON representing the contract interface.
+ /// @returns a JSON representing the contract ABI.
/// Prerequisite: Successful call to parse or compile.
- Json::Value const& interface(std::string const& _contractName = "") const;
+ Json::Value const& contractABI(std::string const& _contractName = "") const;
/// @returns a JSON representing the contract's documentation.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
/// Can be one of 4 types defined at @c DocumentationType
- Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const;
+ Json::Value const& natspec(std::string const& _contractName, DocumentationType _type) const;
+
+ /// @returns a JSON representing a map of method identifiers (hashes) to function names.
+ Json::Value methodIdentifiers(std::string const& _contractName) const;
+
std::string const& onChainMetadata(std::string const& _contractName) const;
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
@@ -191,16 +193,6 @@ public:
Scanner const& scanner(std::string const& _sourceName = "") const;
/// @returns the parsed source unit with the supplied name.
SourceUnit const& ast(std::string const& _sourceName = "") const;
- /// @returns the parsed contract with the supplied name. Throws an exception if the contract
- /// does not exist.
- ContractDefinition const& contractDefinition(std::string const& _contractName) const;
-
- /// @returns the offset of the entry point of the given function into the list of assembly items
- /// or zero if it is not found or does not exist.
- size_t functionEntryPoint(
- std::string const& _contractName,
- FunctionDefinition const& _function
- ) const;
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
@@ -208,7 +200,9 @@ public:
std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
/// @returns the list of errors that occured during parsing and type checking.
- ErrorList const& errors() const { return m_errors; }
+ ErrorList const& errors() { return m_errorReporter.errors(); }
+
+ State state() const { return m_stackState; }
private:
/**
@@ -230,20 +224,12 @@ private:
eth::LinkerObject runtimeObject;
eth::LinkerObject cloneObject;
std::string onChainMetadata; ///< The metadata json that will be hashed into the chain.
- mutable std::unique_ptr<Json::Value const> interface;
+ mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> userDocumentation;
mutable std::unique_ptr<Json::Value const> devDocumentation;
mutable std::unique_ptr<std::string const> sourceMapping;
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
};
- enum State {
- Empty,
- SourcesSet,
- ParsingSuccessful,
- AnalysisSuccessful,
- CompilationSuccessful
- };
-
/// Loads the missing sources from @a _ast (named @a _path) using the callback
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
/// @returns the newly loaded sources.
@@ -265,9 +251,21 @@ private:
Contract const& contract(std::string const& _contractName = "") const;
Source const& source(std::string const& _sourceName = "") const;
+ /// @returns the parsed contract with the supplied name. Throws an exception if the contract
+ /// does not exist.
+ ContractDefinition const& contractDefinition(std::string const& _contractName) const;
+
std::string createOnChainMetadata(Contract const& _contract) const;
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
- Json::Value const& metadata(Contract const&, DocumentationType _type) const;
+ Json::Value const& contractABI(Contract const&) const;
+ Json::Value const& natspec(Contract const&, DocumentationType _type) const;
+
+ /// @returns the offset of the entry point of the given function into the list of assembly items
+ /// or zero if it is not found or does not exist.
+ size_t functionEntryPoint(
+ std::string const& _contractName,
+ FunctionDefinition const& _function
+ ) const;
struct Remapping
{
@@ -288,8 +286,8 @@ private:
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
std::vector<Source const*> m_sourceOrder;
std::map<std::string const, Contract> m_contracts;
- std::string m_formalTranslation;
- ErrorList m_errors;
+ ErrorList m_errorList;
+ ErrorReporter m_errorReporter;
bool m_metadataLiteralSources = false;
State m_stackState = Empty;
};
diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp
new file mode 100644
index 00000000..6e2667a5
--- /dev/null
+++ b/libsolidity/interface/ErrorReporter.cpp
@@ -0,0 +1,167 @@
+/*
+ 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 Rhett <roadriverrail@gmail.com>
+ * @date 2017
+ * Error helper class.
+ */
+
+#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/ast/AST.h>
+#include <memory>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
+{
+ if (&_errorReporter == this)
+ return *this;
+ m_errorList = _errorReporter.m_errorList;
+ return *this;
+}
+
+
+void ErrorReporter::warning(string const& _description)
+{
+ error(Error::Type::Warning, SourceLocation(), _description);
+}
+
+void ErrorReporter::warning(SourceLocation const& _location, string const& _description)
+{
+ error(Error::Type::Warning, _location, _description);
+}
+
+void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
+{
+ auto err = make_shared<Error>(_type);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_comment(_description);
+
+ m_errorList.push_back(err);
+}
+
+void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
+{
+ auto err = make_shared<Error>(_type);
+ *err <<
+ errinfo_sourceLocation(_location) <<
+ errinfo_secondarySourceLocation(_secondaryLocation) <<
+ errinfo_comment(_description);
+
+ m_errorList.push_back(err);
+}
+
+
+void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description)
+{
+ error(_type, _location, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+ErrorList const& ErrorReporter::errors() const
+{
+ return m_errorList;
+}
+
+void ErrorReporter::clear()
+{
+ m_errorList.clear();
+}
+
+void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const&_secondaryLocation, string const& _description)
+{
+ error(
+ Error::Type::DeclarationError,
+ _location,
+ _secondaryLocation,
+ _description
+ );
+}
+
+void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description)
+{
+ error(
+ Error::Type::DeclarationError,
+ _location,
+ _description
+ );
+}
+
+void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description)
+{
+ fatalError(
+ Error::Type::DeclarationError,
+ _location,
+ _description);
+}
+
+void ErrorReporter::parserError(SourceLocation const& _location, string const& _description)
+{
+ error(
+ Error::Type::ParserError,
+ _location,
+ _description
+ );
+}
+
+void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description)
+{
+ fatalError(
+ Error::Type::ParserError,
+ _location,
+ _description
+ );
+}
+
+void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description)
+{
+ error(
+ Error::Type::SyntaxError,
+ _location,
+ _description
+ );
+}
+
+void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
+{
+ error(
+ Error::Type::TypeError,
+ _location,
+ _description
+ );
+}
+
+
+void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description)
+{
+ fatalError(Error::Type::TypeError,
+ _location,
+ _description
+ );
+}
+
+void ErrorReporter::docstringParsingError(string const& _description)
+{
+ error(
+ Error::Type::DocstringParsingError,
+ SourceLocation(),
+ _description
+ );
+}
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
new file mode 100644
index 00000000..e5605d24
--- /dev/null
+++ b/libsolidity/interface/ErrorReporter.h
@@ -0,0 +1,102 @@
+/*
+ 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 Rhett <roadriverrail@gmail.com>
+ * @date 2017
+ * Error reporting helper class.
+ */
+
+#pragma once
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libevmasm/SourceLocation.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ASTNode;
+
+class ErrorReporter
+{
+public:
+
+ ErrorReporter(ErrorList& _errors):
+ m_errorList(_errors) { }
+
+ ErrorReporter& operator=(ErrorReporter const& _errorReporter);
+
+ void warning(std::string const& _description = std::string());
+
+ void warning(
+ SourceLocation const& _location = SourceLocation(),
+ std::string const& _description = std::string()
+ );
+
+ void error(
+ Error::Type _type,
+ SourceLocation const& _location = SourceLocation(),
+ std::string const& _description = std::string()
+ );
+
+ void declarationError(
+ SourceLocation const& _location,
+ SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
+ std::string const& _description = std::string()
+ );
+
+ void declarationError(
+ SourceLocation const& _location,
+ std::string const& _description = std::string()
+ );
+
+ void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
+
+ void parserError(SourceLocation const& _location, std::string const& _description);
+
+ void fatalParserError(SourceLocation const& _location, std::string const& _description);
+
+ void syntaxError(SourceLocation const& _location, std::string const& _description);
+
+ void typeError(SourceLocation const& _location, std::string const& _description);
+
+ void fatalTypeError(SourceLocation const& _location, std::string const& _description);
+
+ void docstringParsingError(std::string const& _location);
+
+ ErrorList const& errors() const;
+
+ void clear();
+
+private:
+ void error(Error::Type _type,
+ SourceLocation const& _location,
+ SecondarySourceLocation const& _secondaryLocation,
+ std::string const& _description = std::string());
+
+ void fatalError(Error::Type _type,
+ SourceLocation const& _location = SourceLocation(),
+ std::string const& _description = std::string());
+
+ ErrorList& m_errorList;
+};
+
+
+}
+}
+
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index c09180de..9f2a2d06 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -21,7 +21,6 @@
*/
#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/Utils.h>
using namespace std;
using namespace dev;
@@ -47,9 +46,6 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
case Type::TypeError:
m_typeName = "TypeError";
break;
- case Type::Why3TranslatorError:
- m_typeName = "Why3TranslatorError";
- break;
case Type::Warning:
m_typeName = "Warning";
break;
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
index 0803d8cc..09301b10 100644
--- a/libsolidity/interface/Exceptions.h
+++ b/libsolidity/interface/Exceptions.h
@@ -25,6 +25,7 @@
#include <string>
#include <utility>
#include <libdevcore/Exceptions.h>
+#include <libdevcore/Assertions.h>
#include <libevmasm/SourceLocation.h>
namespace dev
@@ -39,6 +40,16 @@ struct InternalCompilerError: virtual Exception {};
struct FatalError: virtual Exception {};
struct UnimplementedFeatureError: virtual Exception{};
+/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
+#define solAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
+
+#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
+ assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
+
+#define solUnimplemented(DESCRIPTION) \
+ solUnimplementedAssert(false, DESCRIPTION)
+
class Error: virtual public Exception
{
public:
@@ -49,7 +60,6 @@ public:
ParserError,
TypeError,
SyntaxError,
- Why3TranslatorError,
Warning
};
diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp
deleted file mode 100644
index 6c1bb0c4..00000000
--- a/libsolidity/interface/InterfaceHandler.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-
-#include <libsolidity/interface/InterfaceHandler.h>
-#include <boost/range/irange.hpp>
-#include <libsolidity/ast/AST.h>
-#include <libsolidity/interface/CompilerStack.h>
-
-using namespace std;
-using namespace dev;
-using namespace dev::solidity;
-
-Json::Value InterfaceHandler::documentation(
- ContractDefinition const& _contractDef,
- DocumentationType _type
-)
-{
- switch(_type)
- {
- case DocumentationType::NatspecUser:
- return userDocumentation(_contractDef);
- case DocumentationType::NatspecDev:
- return devDocumentation(_contractDef);
- case DocumentationType::ABIInterface:
- return abiInterface(_contractDef);
- }
-
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
-}
-
-Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
-{
- Json::Value abi(Json::arrayValue);
-
- for (auto it: _contractDef.interfaceFunctions())
- {
- auto externalFunctionType = it.second->interfaceFunctionType();
- Json::Value method;
- method["type"] = "function";
- method["name"] = it.second->declaration().name();
- method["constant"] = it.second->isConstant();
- method["payable"] = it.second->isPayable();
- method["inputs"] = formatTypeList(
- externalFunctionType->parameterNames(),
- externalFunctionType->parameterTypes(),
- _contractDef.isLibrary()
- );
- method["outputs"] = formatTypeList(
- externalFunctionType->returnParameterNames(),
- externalFunctionType->returnParameterTypes(),
- _contractDef.isLibrary()
- );
- abi.append(method);
- }
- if (_contractDef.constructor())
- {
- Json::Value method;
- method["type"] = "constructor";
- auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
- solAssert(!!externalFunction, "");
- method["payable"] = externalFunction->isPayable();
- method["inputs"] = formatTypeList(
- externalFunction->parameterNames(),
- externalFunction->parameterTypes(),
- _contractDef.isLibrary()
- );
- abi.append(method);
- }
- if (_contractDef.fallbackFunction())
- {
- auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
- solAssert(!!externalFunctionType, "");
- Json::Value method;
- method["type"] = "fallback";
- method["payable"] = externalFunctionType->isPayable();
- abi.append(method);
- }
- for (auto const& it: _contractDef.interfaceEvents())
- {
- Json::Value event;
- event["type"] = "event";
- event["name"] = it->name();
- event["anonymous"] = it->isAnonymous();
- Json::Value params(Json::arrayValue);
- for (auto const& p: it->parameters())
- {
- solAssert(!!p->annotation().type->interfaceType(false), "");
- Json::Value input;
- input["name"] = p->name();
- input["type"] = p->annotation().type->interfaceType(false)->canonicalName(false);
- input["indexed"] = p->isIndexed();
- params.append(input);
- }
- event["inputs"] = params;
- abi.append(event);
- }
-
- return abi;
-}
-
-Json::Value InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
-{
- Json::Value doc;
- Json::Value methods(Json::objectValue);
-
- for (auto const& it: _contractDef.interfaceFunctions())
- if (it.second->hasDeclaration())
- if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
- {
- string value = extractDoc(f->annotation().docTags, "notice");
- if (!value.empty())
- {
- Json::Value user;
- // since @notice is the only user tag if missing function should not appear
- user["notice"] = Json::Value(value);
- methods[it.second->externalSignature()] = user;
- }
- }
- doc["methods"] = methods;
-
- return doc;
-}
-
-Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
-{
- Json::Value doc;
- Json::Value methods(Json::objectValue);
-
- auto author = extractDoc(_contractDef.annotation().docTags, "author");
- if (!author.empty())
- doc["author"] = author;
- auto title = extractDoc(_contractDef.annotation().docTags, "title");
- if (!title.empty())
- doc["title"] = title;
-
- for (auto const& it: _contractDef.interfaceFunctions())
- {
- if (!it.second->hasDeclaration())
- continue;
- Json::Value method;
- if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
- {
- auto dev = extractDoc(fun->annotation().docTags, "dev");
- if (!dev.empty())
- method["details"] = Json::Value(dev);
-
- auto author = extractDoc(fun->annotation().docTags, "author");
- if (!author.empty())
- method["author"] = author;
-
- auto ret = extractDoc(fun->annotation().docTags, "return");
- if (!ret.empty())
- method["return"] = ret;
-
- Json::Value params(Json::objectValue);
- auto paramRange = fun->annotation().docTags.equal_range("param");
- for (auto i = paramRange.first; i != paramRange.second; ++i)
- params[i->second.paramName] = Json::Value(i->second.content);
-
- if (!params.empty())
- method["params"] = params;
-
- if (!method.empty())
- // add the function, only if we have any documentation to add
- methods[it.second->externalSignature()] = method;
- }
- }
- doc["methods"] = methods;
-
- return doc;
-}
-
-Json::Value InterfaceHandler::formatTypeList(
- vector<string> const& _names,
- vector<TypePointer> const& _types,
- bool _forLibrary
-)
-{
- Json::Value params(Json::arrayValue);
- solAssert(_names.size() == _types.size(), "Names and types vector size does not match");
- for (unsigned i = 0; i < _names.size(); ++i)
- {
- solAssert(_types[i], "");
- Json::Value param;
- param["name"] = _names[i];
- param["type"] = _types[i]->canonicalName(_forLibrary);
- params.append(param);
- }
- return params;
-}
-
-string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
-{
- string value;
- auto range = _tags.equal_range(_name);
- for (auto i = range.first; i != range.second; i++)
- value += i->second.content;
- return value;
-}
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
new file mode 100644
index 00000000..70486e23
--- /dev/null
+++ b/libsolidity/interface/Natspec.cpp
@@ -0,0 +1,130 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Lefteris <lefteris@ethdev.com>
+ * @date 2014
+ * Takes the parsed AST and produces the Natspec documentation:
+ * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
+ *
+ * Can generally deal with JSON files
+ */
+
+#include <libsolidity/interface/Natspec.h>
+#include <boost/range/irange.hpp>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/interface/CompilerStack.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+Json::Value Natspec::documentation(
+ ContractDefinition const& _contractDef,
+ DocumentationType _type
+)
+{
+ switch(_type)
+ {
+ case DocumentationType::NatspecUser:
+ return userDocumentation(_contractDef);
+ case DocumentationType::NatspecDev:
+ return devDocumentation(_contractDef);
+ }
+
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
+}
+
+Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
+{
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ if (it.second->hasDeclaration())
+ if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
+ {
+ string value = extractDoc(f->annotation().docTags, "notice");
+ if (!value.empty())
+ {
+ Json::Value user;
+ // since @notice is the only user tag if missing function should not appear
+ user["notice"] = Json::Value(value);
+ methods[it.second->externalSignature()] = user;
+ }
+ }
+ doc["methods"] = methods;
+
+ return doc;
+}
+
+Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
+{
+ Json::Value doc;
+ Json::Value methods(Json::objectValue);
+
+ auto author = extractDoc(_contractDef.annotation().docTags, "author");
+ if (!author.empty())
+ doc["author"] = author;
+ auto title = extractDoc(_contractDef.annotation().docTags, "title");
+ if (!title.empty())
+ doc["title"] = title;
+
+ for (auto const& it: _contractDef.interfaceFunctions())
+ {
+ if (!it.second->hasDeclaration())
+ continue;
+ Json::Value method;
+ if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
+ {
+ auto dev = extractDoc(fun->annotation().docTags, "dev");
+ if (!dev.empty())
+ method["details"] = Json::Value(dev);
+
+ auto author = extractDoc(fun->annotation().docTags, "author");
+ if (!author.empty())
+ method["author"] = author;
+
+ auto ret = extractDoc(fun->annotation().docTags, "return");
+ if (!ret.empty())
+ method["return"] = ret;
+
+ Json::Value params(Json::objectValue);
+ auto paramRange = fun->annotation().docTags.equal_range("param");
+ for (auto i = paramRange.first; i != paramRange.second; ++i)
+ params[i->second.paramName] = Json::Value(i->second.content);
+
+ if (!params.empty())
+ method["params"] = params;
+
+ if (!method.empty())
+ // add the function, only if we have any documentation to add
+ methods[it.second->externalSignature()] = method;
+ }
+ }
+ doc["methods"] = methods;
+
+ return doc;
+}
+
+string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
+{
+ string value;
+ auto range = _tags.equal_range(_name);
+ for (auto i = range.first; i != range.second; i++)
+ value += i->second.content;
+ return value;
+}
diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/Natspec.h
index 56927d44..bec9acd2 100644
--- a/libsolidity/interface/InterfaceHandler.h
+++ b/libsolidity/interface/Natspec.h
@@ -17,8 +17,7 @@
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2014
- * Takes the parsed AST and produces the Natspec
- * documentation and the ABI interface
+ * Takes the parsed AST and produces the Natspec documentation:
* https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format
*
* Can generally deal with JSON files
@@ -59,7 +58,7 @@ enum class CommentOwner
Function
};
-class InterfaceHandler
+class Natspec
{
public:
/// Get the given type of documentation
@@ -71,10 +70,6 @@ public:
ContractDefinition const& _contractDef,
DocumentationType _type
);
- /// Get the ABI Interface of the contract
- /// @param _contractDef The contract definition
- /// @return A JSONrepresentation of the contract's ABI Interface
- static Json::Value abiInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's user documentation
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 223cc15d..e677afc8 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -115,14 +115,6 @@ StringMap createSourceList(Json::Value const& _input)
return sources;
}
-Json::Value methodIdentifiers(ContractDefinition const& _contract)
-{
- Json::Value methodIdentifiers(Json::objectValue);
- for (auto const& it: _contract.interfaceFunctions())
- methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
- return methodIdentifiers;
-}
-
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
Json::Value ret(Json::objectValue);
@@ -273,11 +265,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); };
- bool success = false;
-
try
{
- success = m_compilerStack.compile(optimize, optimizeRuns, libraries);
+ m_compilerStack.compile(optimize, optimizeRuns, libraries);
for (auto const& error: m_compilerStack.errors())
{
@@ -367,22 +357,26 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (errors.size() > 0)
output["errors"] = errors;
+ bool parsingSuccess = m_compilerStack.state() >= CompilerStack::State::ParsingSuccessful;
+ bool compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
+
/// Inconsistent state - stop here to receive error reports from users
- if (!success && (errors.size() == 0))
+ if (!compilationSuccess && (errors.size() == 0))
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
output["sources"] = Json::objectValue;
unsigned sourceIndex = 0;
- for (auto const& source: m_compilerStack.sourceNames())
+ for (auto const& source: parsingSuccess ? m_compilerStack.sourceNames() : vector<string>())
{
Json::Value sourceResult = Json::objectValue;
sourceResult["id"] = sourceIndex++;
- sourceResult["legacyAST"] = ASTJsonConverter(m_compilerStack.ast(source), m_compilerStack.sourceIndices()).json();
+ sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source));
+ sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(source));
output["sources"][source] = sourceResult;
}
Json::Value contractsOutput = Json::objectValue;
- for (string const& contractName: success ? m_compilerStack.contractNames() : vector<string>())
+ for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>())
{
size_t colon = contractName.find(':');
solAssert(colon != string::npos, "");
@@ -391,10 +385,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// ABI, documentation and metadata
Json::Value contractData(Json::objectValue);
- contractData["abi"] = m_compilerStack.metadata(contractName, DocumentationType::ABIInterface);
+ contractData["abi"] = m_compilerStack.contractABI(contractName);
contractData["metadata"] = m_compilerStack.onChainMetadata(contractName);
- contractData["userdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecUser);
- contractData["devdoc"] = m_compilerStack.metadata(contractName, DocumentationType::NatspecDev);
+ contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser);
+ contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev);
// EVM
Json::Value evmData(Json::objectValue);
@@ -403,7 +397,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), false);
evmData["assembly"] = tmp.str();
evmData["legacyAssembly"] = m_compilerStack.streamAssembly(tmp, contractName, createSourceList(_input), true);
- evmData["methodIdentifiers"] = methodIdentifiers(m_compilerStack.contractDefinition(contractName));
+ evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName);
evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
evmData["bytecode"] = collectEVMObject(
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
index 0d23f9c3..a35bfd29 100644
--- a/libsolidity/interface/Version.cpp
+++ b/libsolidity/interface/Version.cpp
@@ -24,7 +24,7 @@
#include <string>
#include <libdevcore/CommonData.h>
#include <libdevcore/Common.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <solidity/BuildInfo.h>
using namespace dev;
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index 8e912126..0409de72 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -1,6 +1,7 @@
#include <libsolidity/parsing/DocStringParser.h>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/ErrorReporter.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
@@ -51,9 +52,9 @@ string::const_iterator skipWhitespace(
}
-bool DocStringParser::parse(string const& _docString, ErrorList& _errors)
+bool DocStringParser::parse(string const& _docString, ErrorReporter& _errorReporter)
{
- m_errors = &_errors;
+ m_errorReporter = &_errorReporter;
m_errorsOccurred = false;
m_lastTag = nullptr;
@@ -172,8 +173,6 @@ void DocStringParser::newTag(string const& _tagName)
void DocStringParser::appendError(string const& _description)
{
- auto err = make_shared<Error>(Error::Type::DocstringParsingError);
- *err << errinfo_comment(_description);
- m_errors->push_back(err);
m_errorsOccurred = true;
+ m_errorReporter->docstringParsingError(_description);
}
diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h
index c7f81c55..5f2819cc 100644
--- a/libsolidity/parsing/DocStringParser.h
+++ b/libsolidity/parsing/DocStringParser.h
@@ -23,7 +23,6 @@
#pragma once
#include <string>
-#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h>
namespace dev
@@ -31,12 +30,14 @@ namespace dev
namespace solidity
{
+class ErrorReporter;
+
class DocStringParser
{
public:
/// Parse the given @a _docString and stores the parsed components internally.
/// @returns false on error and appends the error to @a _errors.
- bool parse(std::string const& _docString, ErrorList& _errors);
+ bool parse(std::string const& _docString, ErrorReporter& _errorReporter);
std::multimap<std::string, DocTag> const& tags() const { return m_docTags; }
@@ -62,7 +63,7 @@ private:
/// Mapping tag name -> content.
std::multimap<std::string, DocTag> m_docTags;
DocTag* m_lastTag = nullptr;
- ErrorList* m_errors = nullptr;
+ ErrorReporter* m_errorReporter = nullptr;
bool m_errorsOccurred = false;
};
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index b5130c8a..b0cf364e 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -26,8 +26,7 @@
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h>
-#include <libsolidity/interface/Exceptions.h>
-#include <libsolidity/interface/InterfaceHandler.h>
+#include <libsolidity/interface/ErrorReporter.h>
using namespace std;
@@ -95,7 +94,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
}
catch (FatalError const&)
{
- if (m_errors.empty())
+ if (m_errorReporter.errors().empty())
throw; // Something is weird here, rather throw again.
return nullptr;
}
@@ -324,11 +323,17 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
Token::Value token = m_scanner->currentToken();
if (token == Token::Const)
{
+ if (result.isDeclaredConst)
+ parserError(string("Multiple \"constant\" specifiers."));
+
result.isDeclaredConst = true;
m_scanner->next();
}
else if (m_scanner->currentToken() == Token::Payable)
{
+ if (result.isPayable)
+ parserError(string("Multiple \"payable\" specifiers."));
+
result.isPayable = true;
m_scanner->next();
}
@@ -348,8 +353,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else if (Token::isVisibilitySpecifier(token))
{
if (result.visibility != Declaration::Visibility::Default)
- fatalParserError(string("Multiple visibility specifiers."));
- result.visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Multiple visibility specifiers."));
+ m_scanner->next();
+ }
+ else
+ result.visibility = parseVisibilitySpecifier(token);
}
else
break;
@@ -502,8 +511,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
- fatalParserError(string("Visibility already specified."));
- visibility = parseVisibilitySpecifier(token);
+ {
+ parserError(string("Visibility already specified."));
+ m_scanner->next();
+ }
+ else
+ visibility = parseVisibilitySpecifier(token);
}
else
{
@@ -514,14 +527,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
- fatalParserError(string("Location already specified."));
- if (!type)
- fatalParserError(string("Location specifier needs explicit type name."));
- location = (
- token == Token::Memory ?
- VariableDeclaration::Location::Memory :
- VariableDeclaration::Location::Storage
- );
+ parserError(string("Location already specified."));
+ else if (!type)
+ parserError(string("Location specifier needs explicit type name."));
+ else
+ location = (
+ token == Token::Memory ?
+ VariableDeclaration::Location::Memory :
+ VariableDeclaration::Location::Storage
+ );
}
else
break;
@@ -703,7 +717,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var)
{
if (!_allowVar)
- fatalParserError(string("Expected explicit type name."));
+ parserError(string("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::Function)
@@ -866,7 +880,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
m_scanner->next();
}
- assembly::Parser asmParser(m_errors);
+ assembly::Parser asmParser(m_errorReporter);
shared_ptr<assembly::Block> block = asmParser.parse(m_scanner);
nodeFactory.markEndPosition();
return nodeFactory.createNode<InlineAssembly>(_docString, block);
@@ -1329,16 +1343,27 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
{
// call({arg1 : 1, arg2 : 2 })
expectToken(Token::LBrace);
+
+ bool first = true;
while (m_scanner->currentToken() != Token::RBrace)
{
+ if (!first)
+ expectToken(Token::Comma);
+
ret.second.push_back(expectIdentifierToken());
expectToken(Token::Colon);
ret.first.push_back(parseExpression());
- if (m_scanner->currentToken() == Token::Comma)
- expectToken(Token::Comma);
- else
- break;
+ if (
+ m_scanner->currentToken() == Token::Comma &&
+ m_scanner->peekNextToken() == Token::RBrace
+ )
+ {
+ parserError("Unexpected trailing comma.");
+ m_scanner->next();
+ }
+
+ first = false;
}
expectToken(Token::RBrace);
}
@@ -1438,5 +1463,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..19631c58 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -35,7 +35,7 @@ class Scanner;
class Parser: public ParserBase
{
public:
- Parser(ErrorList& _errors): ParserBase(_errors) {}
+ Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {}
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
@@ -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..5657c2c0 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/parsing/ParserBase.h>
#include <libsolidity/parsing/Scanner.h>
+#include <libsolidity/interface/ErrorReporter.h>
using namespace std;
using namespace dev;
@@ -42,6 +43,26 @@ int ParserBase::endPosition() const
return m_scanner->currentLocation().end;
}
+Token::Value ParserBase::currentToken() const
+{
+ return m_scanner->currentToken();
+}
+
+Token::Value ParserBase::peekNextToken() const
+{
+ return m_scanner->peekNextToken();
+}
+
+std::string ParserBase::currentLiteral() const
+{
+ return m_scanner->currentLiteral();
+}
+
+Token::Value ParserBase::advance()
+{
+ return m_scanner->next();
+}
+
void ParserBase::expectToken(Token::Value _value)
{
Token::Value tok = m_scanner->currentToken();
@@ -80,74 +101,12 @@ 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);
- *err <<
- errinfo_sourceLocation(SourceLocation(position(), position(), sourceName())) <<
- errinfo_comment(_description);
-
- m_errors.push_back(err);
+ m_errorReporter.parserError(SourceLocation(position(), position(), sourceName()), _description);
}
void ParserBase::fatalParserError(string const& _description)
{
- parserError(_description);
- BOOST_THROW_EXCEPTION(FatalError());
+ m_errorReporter.fatalParserError(SourceLocation(position(), position(), sourceName()), _description);
}
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
index dfb7cab7..5b03ab5e 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -23,21 +23,20 @@
#pragma once
#include <memory>
-#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/parsing/Token.h>
-#include <libsolidity/ast/ASTForward.h>
namespace dev
{
namespace solidity
{
+class ErrorReporter;
class Scanner;
class ParserBase
{
public:
- ParserBase(ErrorList& errors): m_errors(errors) {}
+ ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {}
std::shared_ptr<std::string const> const& sourceName() const;
@@ -47,14 +46,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;
+ Token::Value peekNextToken() const;
+ std::string currentLiteral() const;
+ Token::Value advance();
///@}
/// Creates a @ref ParserError and annotates it with the current position and the
@@ -67,7 +66,7 @@ protected:
std::shared_ptr<Scanner> m_scanner;
/// The reference to the list of errors and warning to add errors/warnings during parsing
- ErrorList& m_errors;
+ ErrorReporter& m_errorReporter;
};
}
diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
index 0e60fd0b..fdca23ea 100644
--- a/libsolidity/parsing/Scanner.cpp
+++ b/libsolidity/parsing/Scanner.cpp
@@ -52,7 +52,7 @@
#include <algorithm>
#include <tuple>
-#include <libsolidity/interface/Utils.h>
+#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/parsing/Scanner.h>
using namespace std;
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 9a557ebd..39c0eff9 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -43,7 +43,6 @@
#pragma once
#include <libdevcore/Common.h>
-#include <libsolidity/interface/Utils.h>
#include <libsolidity/interface/Exceptions.h>
#include <libdevcore/UndefMacros.h>