Browse Source

Merge branch 'develop' into network

cl-refactor
subtly 10 years ago
parent
commit
874b59c22c
  1. 2
      alethzero/MainWin.cpp
  2. 43
      libp2p/Host.cpp
  3. 69
      libsolidity/AST.cpp
  4. 36
      libsolidity/AST.h
  5. 2
      libsolidity/ASTForward.h
  6. 9
      libsolidity/ASTPrinter.cpp
  7. 3
      libsolidity/ASTPrinter.h
  8. 4
      libsolidity/ASTVisitor.h
  9. 10
      libsolidity/BaseTypes.h
  10. 180
      libsolidity/CompilerStack.cpp
  11. 77
      libsolidity/CompilerStack.h
  12. 4
      libsolidity/DeclarationContainer.cpp
  13. 2
      libsolidity/DeclarationContainer.h
  14. 1
      libsolidity/Exceptions.h
  15. 139
      libsolidity/ExpressionCompiler.cpp
  16. 7
      libsolidity/ExpressionCompiler.h
  17. 3
      libsolidity/GlobalContext.cpp
  18. 4
      libsolidity/GlobalContext.h
  19. 14
      libsolidity/InterfaceHandler.cpp
  20. 8
      libsolidity/InterfaceHandler.h
  21. 13
      libsolidity/NameAndTypeResolver.cpp
  22. 6
      libsolidity/NameAndTypeResolver.h
  23. 86
      libsolidity/Parser.cpp
  24. 4
      libsolidity/Parser.h
  25. 11
      libsolidity/Scanner.cpp
  26. 14
      libsolidity/Scanner.h
  27. 41
      libsolidity/SourceReferenceFormatter.cpp
  28. 4
      libsolidity/SourceReferenceFormatter.h
  29. 33
      libsolidity/Types.cpp
  30. 9
      libsolidity/Types.h
  31. 2
      libweb3jsonrpc/WebThreeStubServer.cpp
  32. 1
      libwhisper/Message.cpp
  33. 2
      libwhisper/WhisperHost.cpp
  34. 2
      libwhisper/WhisperHost.h
  35. 2
      libwhisper/WhisperPeer.cpp
  36. 2
      libwhisper/WhisperPeer.h
  37. 2
      mix/ConstantCompilationModel.cpp
  38. 59
      solc/main.cpp
  39. 25
      test/solidityCompiler.cpp
  40. 135
      test/solidityEndToEndTest.cpp
  41. 47
      test/solidityExpressionCompiler.cpp
  42. 2
      test/solidityJSONInterfaceTest.cpp
  43. 8
      test/solidityNameAndTypeResolution.cpp
  44. 4
      test/solidityNatspecJSON.cpp
  45. 55
      test/solidityParser.cpp

2
alethzero/MainWin.cpp

@ -1626,7 +1626,7 @@ void Main::on_data_textChanged()
catch (dev::Exception const& exception) catch (dev::Exception const& exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>"; solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
} }
catch (...) catch (...)

43
libp2p/Host.cpp

@ -447,26 +447,31 @@ void Host::connect(std::shared_ptr<Node> const& _n)
_n->failedAttempts++; _n->failedAttempts++;
m_ready -= _n->index; m_ready -= _n->index;
bi::tcp::socket* s = new bi::tcp::socket(m_ioService); bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_n->address, [=](boost::system::error_code const& ec)
{ auto n = node(_n->id);
if (ec) if (n)
{ s->async_connect(_n->address, [=](boost::system::error_code const& ec)
clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")";
_n->lastDisconnect = TCPError;
_n->lastAttempted = std::chrono::system_clock::now();
m_ready += _n->index;
}
else
{ {
clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address; if (ec)
_n->lastConnected = std::chrono::system_clock::now(); {
auto p = make_shared<Session>(this, std::move(*s), node(_n->id), true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism. clog(NetConnect) << "Connection refused to node" << _n->id.abridged() << "@" << _n->address << "(" << ec.message() << ")";
p->start(); _n->lastDisconnect = TCPError;
} _n->lastAttempted = std::chrono::system_clock::now();
delete s; m_ready += _n->index;
Guard l(x_pendingNodeConns); }
m_pendingNodeConns.erase(nptr); else
}); {
clog(NetConnect) << "Connected to" << _n->id.abridged() << "@" << _n->address;
_n->lastConnected = std::chrono::system_clock::now();
auto p = make_shared<Session>(this, std::move(*s), n, true); // true because we don't care about ids matched for now. Once we have permenant IDs this will matter a lot more and we can institute a safer mechanism.
p->start();
}
delete s;
Guard l(x_pendingNodeConns);
m_pendingNodeConns.erase(nptr);
});
else
clog(NetWarn) << "Trying to connect to node not in node table.";
} }
bool Host::havePeer(NodeId _id) const bool Host::havePeer(NodeId _id) const

69
libsolidity/AST.cpp

@ -33,6 +33,19 @@ namespace dev
namespace solidity namespace solidity
{ {
void SourceUnit::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_nodes, _visitor);
_visitor.endVisit(*this);
}
void ImportDirective::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ContractDefinition::accept(ASTVisitor& _visitor) void ContractDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -57,34 +70,6 @@ void StructDefinition::checkValidityOfMembers()
checkRecursion(); checkRecursion();
} }
void StructDefinition::checkMemberTypes()
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
}
void StructDefinition::checkRecursion()
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
}
}
}
void ParameterList::accept(ASTVisitor& _visitor) void ParameterList::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -312,6 +297,34 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co
return exportedFunctions; return exportedFunctions;
} }
void StructDefinition::checkMemberTypes()
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
}
void StructDefinition::checkRecursion()
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
}
}
}
void FunctionDefinition::checkTypeRequirements() void FunctionDefinition::checkTypeRequirements()
{ {
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters()) for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())

36
libsolidity/AST.h

@ -79,6 +79,42 @@ private:
Location m_location; Location m_location;
}; };
/**
* Source unit containing import directives and contract definitions.
*/
class SourceUnit: public ASTNode
{
public:
SourceUnit(Location const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_location), m_nodes(_nodes) {}
virtual void accept(ASTVisitor& _visitor) override;
std::vector<ASTPointer<ASTNode>> getNodes() const { return m_nodes; }
private:
std::vector<ASTPointer<ASTNode>> m_nodes;
};
/**
* Import directive for referencing other files / source objects.
* Example: import "abc.sol"
* Source objects are identified by a string which can be a file name but does not have to be.
*/
class ImportDirective: public ASTNode
{
public:
ImportDirective(Location const& _location, ASTPointer<ASTString> const& _identifier):
ASTNode(_location), m_identifier(_identifier) {}
virtual void accept(ASTVisitor& _visitor) override;
ASTString const& getIdentifier() const { return *m_identifier; }
private:
ASTPointer<ASTString> m_identifier;
};
/** /**
* Abstract AST class for a declaration (contract, function, struct, variable). * Abstract AST class for a declaration (contract, function, struct, variable).
*/ */

2
libsolidity/ASTForward.h

@ -34,6 +34,8 @@ namespace solidity
{ {
class ASTNode; class ASTNode;
class SourceUnit;
class ImportDirective;
class Declaration; class Declaration;
class ContractDefinition; class ContractDefinition;
class StructDefinition; class StructDefinition;

9
libsolidity/ASTPrinter.cpp

@ -43,6 +43,13 @@ void ASTPrinter::print(ostream& _stream)
} }
bool ASTPrinter::visit(ImportDirective& _node)
{
writeLine("ImportDirective \"" + _node.getIdentifier() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ContractDefinition& _node) bool ASTPrinter::visit(ContractDefinition& _node)
{ {
writeLine("ContractDefinition \"" + _node.getName() + "\""); writeLine("ContractDefinition \"" + _node.getName() + "\"");
@ -270,7 +277,7 @@ bool ASTPrinter::visit(Literal& _node)
return goDeeper(); return goDeeper();
} }
void ASTPrinter::endVisit(ASTNode&) void ASTPrinter::endVisit(ImportDirective&)
{ {
m_indentation--; m_indentation--;
} }

3
libsolidity/ASTPrinter.h

@ -42,6 +42,7 @@ public:
/// Output the string representation of the AST to _stream. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);
bool visit(ImportDirective& _node) override;
bool visit(ContractDefinition& _node) override; bool visit(ContractDefinition& _node) override;
bool visit(StructDefinition& _node) override; bool visit(StructDefinition& _node) override;
bool visit(ParameterList& _node) override; bool visit(ParameterList& _node) override;
@ -73,7 +74,7 @@ public:
bool visit(ElementaryTypeNameExpression& _node) override; bool visit(ElementaryTypeNameExpression& _node) override;
bool visit(Literal& _node) override; bool visit(Literal& _node) override;
void endVisit(ASTNode& _node) override; void endVisit(ImportDirective&) override;
void endVisit(ContractDefinition&) override; void endVisit(ContractDefinition&) override;
void endVisit(StructDefinition&) override; void endVisit(StructDefinition&) override;
void endVisit(ParameterList&) override; void endVisit(ParameterList&) override;

4
libsolidity/ASTVisitor.h

@ -42,6 +42,8 @@ class ASTVisitor
{ {
public: public:
virtual bool visit(ASTNode&) { return true; } virtual bool visit(ASTNode&) { return true; }
virtual bool visit(SourceUnit&) { return true; }
virtual bool visit(ImportDirective&) { return true; }
virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; } virtual bool visit(StructDefinition&) { return true; }
virtual bool visit(ParameterList&) { return true; } virtual bool visit(ParameterList&) { return true; }
@ -74,6 +76,8 @@ public:
virtual bool visit(Literal&) { return true; } virtual bool visit(Literal&) { return true; }
virtual void endVisit(ASTNode&) { } virtual void endVisit(ASTNode&) { }
virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { } virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(StructDefinition&) { } virtual void endVisit(StructDefinition&) { }
virtual void endVisit(ParameterList&) { } virtual void endVisit(ParameterList&) { }

10
libsolidity/BaseTypes.h

@ -22,6 +22,8 @@
#pragma once #pragma once
#include <memory>
#include <string>
#include <ostream> #include <ostream>
namespace dev namespace dev
@ -35,19 +37,19 @@ namespace solidity
*/ */
struct Location struct Location
{ {
Location(int _start, int _end): start(_start), end(_end) { } Location(int _start, int _end, std::shared_ptr<std::string const> _sourceName):
start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { } Location(): start(-1), end(-1) { }
bool IsValid() const { return start >= 0 && end >= start; }
int start; int start;
int end; int end;
std::shared_ptr<std::string const> sourceName;
}; };
/// Stream output for Location (used e.g. in boost exceptions). /// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location) inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{ {
return _out << "[" << _location.start << "," << _location.end << ")"; return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
} }
} }

180
libsolidity/CompilerStack.cpp

@ -36,22 +36,43 @@ namespace dev
namespace solidity namespace solidity
{ {
CompilerStack::CompilerStack(): m_interfaceHandler(make_shared<InterfaceHandler>()) {} void CompilerStack::addSource(string const& _name, string const& _content)
{
if (m_sources.count(_name))
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists."));
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
}
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
m_scanner = make_shared<Scanner>(CharStream(_sourceCode)); addSource("", _sourceCode);
} }
void CompilerStack::parse() void CompilerStack::parse()
{ {
if (!m_scanner) for (auto& sourcePair: m_sources)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); {
m_contractASTNode = Parser().parse(m_scanner); sourcePair.second.scanner->reset();
sourcePair.second.ast = Parser().parse(sourcePair.second.scanner);
}
resolveImports();
m_globalContext = make_shared<GlobalContext>(); m_globalContext = make_shared<GlobalContext>();
m_globalContext->setCurrentContract(*m_contractASTNode); NameAndTypeResolver resolver(m_globalContext->getDeclarations());
NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); for (Source const* source: m_sourceOrder)
resolver.registerDeclarations(*source->ast);
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
m_parseSuccessful = true; m_parseSuccessful = true;
} }
@ -61,54 +82,90 @@ void CompilerStack::parse(string const& _sourceCode)
parse(); parse();
} }
bytes const& CompilerStack::compile(bool _optimize) vector<string> CompilerStack::getContractNames()
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear(); vector<string> contractNames;
m_compiler = make_shared<Compiler>(); for (auto const& contract: m_contracts)
m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); contractNames.push_back(contract.first);
return m_bytecode = m_compiler->getAssembledBytecode(_optimize); return contractNames;
}
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
parse();
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>();
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(_optimize);
compiledContract.compiler = move(compiler);
}
} }
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{ {
parse(_sourceCode); parse(_sourceCode);
return compile(_optimize); compile(_optimize);
return getBytecode();
} }
void CompilerStack::streamAssembly(ostream& _outStream) bytes const& CompilerStack::getBytecode(string const& _contractName)
{ {
if (!m_compiler || m_bytecode.empty()) return getContract(_contractName).bytecode;
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
m_compiler->streamAssembly(_outStream);
} }
std::string const& CompilerStack::getJsonDocumentation(DocumentationType _type) void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName)
{
getContract(_contractName).compiler->streamAssembly(_outStream);
}
string const& CompilerStack::getInterface(std::string const& _contractName)
{
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
}
std::string const& CompilerStack::getJsonDocumentation(std::string const& _contractName, DocumentationType _type)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
auto createDocIfNotThere = [this, _type](std::unique_ptr<string>& _doc) Contract& contract = getContract(_contractName);
{
if (!_doc)
_doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type);
};
std::unique_ptr<string>* doc;
switch (_type) switch (_type)
{ {
case DocumentationType::NATSPEC_USER: case DocumentationType::NATSPEC_USER:
createDocIfNotThere(m_userDocumentation); doc = &contract.userDocumentation;
return *m_userDocumentation; break;
case DocumentationType::NATSPEC_DEV: case DocumentationType::NATSPEC_DEV:
createDocIfNotThere(m_devDocumentation); doc = &contract.devDocumentation;
return *m_devDocumentation; break;
case DocumentationType::ABI_INTERFACE: case DocumentationType::ABI_INTERFACE:
createDocIfNotThere(m_interface); doc = &contract.interface;
return *m_interface; break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
} }
if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
return *(*doc);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); Scanner const& CompilerStack::getScanner(string const& _sourceName)
{
return *getSource(_sourceName).scanner;
}
SourceUnit& CompilerStack::getAST(string const& _sourceName)
{
return *getSource(_sourceName).ast;
} }
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
@ -117,7 +174,70 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz
return stack.compile(_sourceCode, _optimize); return stack.compile(_sourceCode, _optimize);
} }
void CompilerStack::reset(bool _keepSources)
{
m_parseSuccessful = false;
if (_keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
m_sources.clear();
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
}
void CompilerStack::resolveImports()
{
// topological sorting (depth first search) of the import graph, cutting potential cycles
vector<Source const*> sourceOrder;
set<Source const*> sourcesSeen;
function<void(Source const*)> toposort = [&](Source const* _source)
{
if (sourcesSeen.count(_source))
return;
sourcesSeen.insert(_source);
for (ASTPointer<ASTNode> const& node: _source->ast->getNodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{
string const& id = import->getIdentifier();
if (!m_sources.count(id))
BOOST_THROW_EXCEPTION(ParserError()
<< errinfo_sourceLocation(import->getLocation())
<< errinfo_comment("Source not found."));
toposort(&m_sources[id]);
}
sourceOrder.push_back(_source);
};
for (auto const& sourcePair: m_sources)
toposort(&sourcePair.second);
swap(m_sourceOrder, sourceOrder);
}
CompilerStack::Contract& CompilerStack::getContract(string const& _contractName)
{
if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
if (_contractName.empty())
return m_contracts.begin()->second;
auto it = m_contracts.find(_contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));
return it->second;
}
CompilerStack::Source& CompilerStack::getSource(string const& _sourceName)
{
auto it = m_sources.find(_sourceName);
if (it == m_sources.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Given source file not found."));
return it->second;
}
CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {}
} }
} }

77
libsolidity/CompilerStack.h

@ -25,6 +25,7 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <memory> #include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
namespace dev { namespace dev {
@ -33,6 +34,7 @@ namespace solidity {
// forward declarations // forward declarations
class Scanner; class Scanner;
class ContractDefinition; class ContractDefinition;
class SourceUnit;
class Compiler; class Compiler;
class GlobalContext; class GlobalContext;
class InterfaceHandler; class InterfaceHandler;
@ -49,52 +51,85 @@ enum class DocumentationType: uint8_t
* It holds state and can be used to either step through the compilation stages (and abort e.g. * It holds state and can be used to either step through the compilation stages (and abort e.g.
* before compilation to bytecode) or run the whole compilation in one call. * before compilation to bytecode) or run the whole compilation in one call.
*/ */
class CompilerStack class CompilerStack: boost::noncopyable
{ {
public: public:
CompilerStack(); CompilerStack(): m_parseSuccessful(false) {}
void reset() { *this = CompilerStack(); }
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
void addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode); void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse(); void parse();
/// Sets the given source code as the only source unit and parses it.
void parse(std::string const& _sourceCode); void parse(std::string const& _sourceCode);
/// Compiles the contract that was previously parsed. /// Returns a list of the contract names in the sources.
bytes const& compile(bool _optimize = false); std::vector<std::string> getContractNames();
/// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false);
/// Parses and compiles the given source code. /// Parses and compiles the given source code.
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
bytes const& getBytecode() const { return m_bytecode; } bytes const& getBytecode(std::string const& _contractName = "");
/// Streams a verbose version of the assembly to @a _outStream. /// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation. /// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream); void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "");
/// Returns a string representing the contract interface in JSON. /// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(); std::string const& getInterface(std::string const& _contractName = "");
/// Returns a string representing the contract's documentation in JSON. /// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get. /// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c documentation_type /// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(DocumentationType type); std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type);
/// Returns the previously used scanner, useful for counting lines during error reporting. /// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner() const { return *m_scanner; } Scanner const& getScanner(std::string const& _sourceName = "");
ContractDefinition& getAST() const { return *m_contractASTNode; } SourceUnit& getAST(std::string const& _sourceName = "");
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information. /// scanning the source code - this is useful for printing exception information.
static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false); static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
private: private:
std::shared_ptr<Scanner> m_scanner; /**
std::shared_ptr<GlobalContext> m_globalContext; * Information pertaining to one source unit, filled gradually during parsing and compilation.
std::shared_ptr<ContractDefinition> m_contractASTNode; */
struct Source
{
std::shared_ptr<Scanner> scanner;
std::shared_ptr<SourceUnit> ast;
std::string interface;
void reset() { scanner.reset(); ast.reset(); interface.clear(); }
};
struct Contract
{
ContractDefinition* contract;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
std::unique_ptr<std::string> interface;
std::unique_ptr<std::string> userDocumentation;
std::unique_ptr<std::string> devDocumentation;
Contract();
};
void reset(bool _keepSources = false);
void resolveImports();
Contract& getContract(std::string const& _contractName = "");
Source& getSource(std::string const& _sourceName = "");
bool m_parseSuccessful; bool m_parseSuccessful;
std::unique_ptr<std::string> m_interface; std::map<std::string, Source> m_sources;
std::unique_ptr<std::string> m_userDocumentation; std::shared_ptr<GlobalContext> m_globalContext;
std::unique_ptr<std::string> m_devDocumentation; std::vector<Source const*> m_sourceOrder;
std::shared_ptr<Compiler> m_compiler; std::map<std::string, Contract> m_contracts;
std::shared_ptr<InterfaceHandler> m_interfaceHandler;
bytes m_bytecode;
}; };
} }

4
libsolidity/DeclarationContainer.cpp

@ -28,9 +28,9 @@ namespace dev
namespace solidity namespace solidity
{ {
bool DeclarationContainer::registerDeclaration(Declaration& _declaration) bool DeclarationContainer::registerDeclaration(Declaration& _declaration, bool _update)
{ {
if (m_declarations.find(_declaration.getName()) != m_declarations.end()) if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end())
return false; return false;
m_declarations[_declaration.getName()] = &_declaration; m_declarations[_declaration.getName()] = &_declaration;
return true; return true;

2
libsolidity/DeclarationContainer.h

@ -43,7 +43,7 @@ public:
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff /// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared. /// it was not yet declared.
bool registerDeclaration(Declaration& _declaration); bool registerDeclaration(Declaration& _declaration, bool _update = false);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; } Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; }

1
libsolidity/Exceptions.h

@ -38,7 +38,6 @@ struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {};
struct DocstringParsingError: virtual Exception {}; struct DocstringParsingError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation; typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;
} }

139
libsolidity/ExpressionCompiler.cpp

@ -185,7 +185,9 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
if (asserts(arguments.size() == function.getParameterTypes().size())) if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
if (function.getLocation() == Location::INTERNAL) switch (function.getLocation())
{
case Location::INTERNAL:
{ {
// Calling convention: Caller pushes return address and arguments // Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values // Callee removes them and pushes return values
@ -208,61 +210,90 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// all others // all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
break;
} }
else if (function.getLocation() == Location::EXTERNAL) case Location::EXTERNAL:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("External function calls not implemented yet."));
else
{ {
switch (function.getLocation()) unsigned dataOffset = 1; // reserve one byte for the function index
{ for (unsigned i = 0; i < arguments.size(); ++i)
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
{ {
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1}, arguments[i]->accept(*this);
{Location::SHA256, 2}, Type const& type = *function.getParameterTypes()[i];
{Location::RIPEMD160, 3}}; appendTypeConversion(*arguments[i]->getType(), type);
u256 contractAddress = contractAddresses.find(function.getLocation())->second; unsigned const numBytes = type.getCalldataEncodedSize();
// @todo later, combine this code with external function call if (numBytes == 0 || numBytes > 32)
for (unsigned i = 0; i < arguments.size(); ++i) BOOST_THROW_EXCEPTION(CompilerError()
{ << errinfo_sourceLocation(arguments[i]->getLocation())
arguments[i]->accept(*this); << errinfo_comment("Type " + type.toString() + " not yet supported."));
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true); if (numBytes != 32)
// @todo move this once we actually use memory m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(i * 32) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
} dataOffset += numBytes;
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
break;
} }
default: //@todo only return the first return value for now
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function not yet implemented.")); unsigned retSize = function.getReturnParameterTypes().empty() ? 0
: function.getReturnParameterTypes().front()->getCalldataEncodedSize();
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
_functionCall.getExpression().accept(*this); // pushes addr and function index
m_context << u256(0) << eth::Instruction::MSTORE8
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
if (retSize == 32)
m_context << u256(0) << eth::Instruction::MLOAD;
else if (retSize > 0)
m_context << (u256(1) << ((32 - retSize) * 8))
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
break;
}
case Location::SEND:
m_context << u256(0) << u256(0) << u256(0) << u256(0);
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
_functionCall.getExpression().accept(*this);
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP;
break;
case Location::SUICIDE:
arguments.front()->accept(*this);
//@todo might not be necessary
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
{
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
// @todo later, combine this code with external function call
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
// @todo move this once we actually use memory
m_context << u256(i * 32) << eth::Instruction::MSTORE;
} }
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
<< contractAddress << u256(500) //@todo determine actual gas requirement
<< eth::Instruction::CALL
<< eth::Instruction::POP
<< u256(0) << eth::Instruction::MLOAD;
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type."));
} }
} }
return false; return false;
@ -289,9 +320,11 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break; break;
case Type::Category::CONTRACT: case Type::Category::CONTRACT:
// call function {
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented.")); ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
m_context << type.getFunctionIndex(member);
break; break;
}
case Type::Category::MAGIC: case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member // we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase") if (member == "coinbase")

7
libsolidity/ExpressionCompiler.h

@ -31,9 +31,10 @@ class AssemblyItem; // forward
} }
namespace solidity { namespace solidity {
class CompilerContext; // forward // forward declarations
class Type; // forward class CompilerContext;
class IntegerType; // forward class Type;
class IntegerType;
/** /**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream

3
libsolidity/GlobalContext.cpp

@ -74,11 +74,10 @@ vector<Declaration*> GlobalContext::getDeclarations() const
declarations.reserve(m_magicVariables.size() + 1); declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<Declaration> const& variable: m_magicVariables) for (ASTPointer<Declaration> const& variable: m_magicVariables)
declarations.push_back(variable.get()); declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations; return declarations;
} }
MagicVariableDeclaration*GlobalContext::getCurrentThis() const MagicVariableDeclaration* GlobalContext::getCurrentThis() const
{ {
if (!m_thisPointer[m_currentContract]) if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(

4
libsolidity/GlobalContext.h

@ -47,12 +47,14 @@ class GlobalContext: private boost::noncopyable
public: public:
GlobalContext(); GlobalContext();
void setCurrentContract(ContractDefinition const& _contract); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const; std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration*> getDeclarations() const; std::vector<Declaration*> getDeclarations() const;
private: private:
MagicVariableDeclaration* getCurrentThis() const;
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables; std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
ContractDefinition const* m_currentContract; ContractDefinition const* m_currentContract;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer; std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;

14
libsolidity/InterfaceHandler.cpp

@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::NONE; m_lastTag = DocTagType::NONE;
} }
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef,
DocumentationType _type) DocumentationType _type)
{ {
switch(_type) switch(_type)
@ -32,11 +32,11 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr<
return nullptr; return nullptr;
} }
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef)
{ {
Json::Value methods(Json::arrayValue); Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
Json::Value inputs(Json::arrayValue); Json::Value inputs(Json::arrayValue);
@ -63,12 +63,12 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(std::shared_ptr<C
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods))); return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
} }
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef)
{ {
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value user; Json::Value user;
auto strPtr = f->getDocumentation(); auto strPtr = f->getDocumentation();
@ -88,14 +88,14 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(std::shared_
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
} }
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef) std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef)
{ {
// LTODO: Somewhere in this function warnings for mismatch of param names // LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown // should be thrown
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
auto strPtr = f->getDocumentation(); auto strPtr = f->getDocumentation();

8
libsolidity/InterfaceHandler.h

@ -59,23 +59,23 @@ public:
/// types provided by @c DocumentationType /// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of provided type /// representation of provided type
std::unique_ptr<std::string> getDocumentation(std::shared_ptr<ContractDefinition> _contractDef, std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef,
DocumentationType _type); DocumentationType _type);
/// Get the ABI Interface of the contract /// Get the ABI Interface of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface /// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getABIInterface(ContractDefinition& _contractDef);
/// Get the User documentation of the contract /// Get the User documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's user documentation /// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getUserDocumentation(ContractDefinition& _contractDef);
/// Get the Developer's documentation of the contract /// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation /// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(std::shared_ptr<ContractDefinition> _contractDef); std::unique_ptr<std::string> getDevDocumentation(ContractDefinition& _contractDef);
private: private:
void resetUser(); void resetUser();

13
libsolidity/NameAndTypeResolver.cpp

@ -38,9 +38,14 @@ NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globa
m_scopes[nullptr].registerDeclaration(*declaration); m_scopes[nullptr].registerDeclaration(*declaration);
} }
void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
{
// The helper registers all declarations in m_scopes as a side-effect of its construction.
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit);
}
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{ {
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract]; m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr); ReferencesResolver resolver(*structDef, *this, nullptr);
@ -65,6 +70,12 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[nullptr]; m_currentScope = &m_scopes[nullptr];
} }
void NameAndTypeResolver::updateDeclaration(Declaration& _declaration)
{
m_scopes[nullptr].registerDeclaration(_declaration, true);
_declaration.setScope(nullptr);
}
Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{ {
auto iterator = m_scopes.find(_scope); auto iterator = m_scopes.find(_scope);

6
libsolidity/NameAndTypeResolver.h

@ -42,7 +42,13 @@ class NameAndTypeResolver: private boost::noncopyable
{ {
public: public:
explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals); explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals);
/// Registers all declarations found in the source unit.
void registerDeclarations(SourceUnit& _sourceUnit);
/// Resolves all names and types referenced from the given contract.
void resolveNamesAndTypes(ContractDefinition& _contract); void resolveNamesAndTypes(ContractDefinition& _contract);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
void updateDeclaration(Declaration& _declaration);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract). /// the global scope is used (i.e. the one containing only the contract).

86
libsolidity/Parser.cpp

@ -20,30 +20,27 @@
* Solidity parser. * Solidity parser.
*/ */
#include <vector>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libsolidity/BaseTypes.h> #include <libsolidity/BaseTypes.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
{ {
ASTPointer<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
return parseContractDefinition();
}
/// AST node factory that also tracks the begin and end position of an AST node /// AST node factory that also tracks the begin and end position of an AST node
/// while it is being parsed /// while it is being parsed
class Parser::ASTNodeFactory class Parser::ASTNodeFactory
{ {
public: public:
ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {} ASTNodeFactory(Parser const& _parser):
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty() { m_location.end = m_location.start; }
@ -55,7 +52,7 @@ public:
{ {
if (m_location.end < 0) if (m_location.end < 0)
markEndPosition(); markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); return make_shared<NodeType>(m_location, forward<Args>(_args)...);
} }
private: private:
@ -63,6 +60,33 @@ private:
Location m_location; Location m_location;
}; };
ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<ASTNode>> nodes;
while (_scanner->getCurrentToken() != Token::EOS)
{
switch (m_scanner->getCurrentToken())
{
case Token::IMPORT:
nodes.push_back(parseImportDirective());
break;
case Token::CONTRACT:
nodes.push_back(parseContractDefinition());
break;
default:
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
}
}
return nodeFactory.createNode<SourceUnit>(nodes);
}
std::shared_ptr<const string> const& Parser::getSourceName() const
{
return m_scanner->getSourceName();
}
int Parser::getPosition() const int Parser::getPosition() const
{ {
return m_scanner->getCurrentLocation().start; return m_scanner->getCurrentLocation().start;
@ -73,15 +97,27 @@ int Parser::getEndPosition() const
return m_scanner->getCurrentLocation().end; return m_scanner->getCurrentLocation().end;
} }
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::IMPORT);
if (m_scanner->getCurrentToken() != Token::STRING_LITERAL)
BOOST_THROW_EXCEPTION(createParserError("Expected string literal (URL)."));
ASTPointer<ASTString> url = getLiteralAndAdvance();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
return nodeFactory.createNode<ImportDirective>(url);
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition() ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT); expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
std::vector<ASTPointer<StructDefinition>> structs; vector<ASTPointer<StructDefinition>> structs;
std::vector<ASTPointer<VariableDeclaration>> stateVariables; vector<ASTPointer<VariableDeclaration>> stateVariables;
std::vector<ASTPointer<FunctionDefinition>> functions; vector<ASTPointer<FunctionDefinition>> functions;
bool visibilityIsPublic = true; bool visibilityIsPublic = true;
while (true) while (true)
{ {
@ -110,7 +146,6 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
} }
@ -119,7 +154,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "") if (m_scanner->getCurrentCommentLiteral() != "")
docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION); expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
@ -142,7 +177,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
// create an empty parameter list at a zero-length location // create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty(); nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>()); returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
} }
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
@ -156,7 +191,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT); expectToken(Token::STRUCT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
std::vector<ASTPointer<VariableDeclaration>> members; vector<ASTPointer<VariableDeclaration>> members;
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBRACE)
{ {
@ -228,7 +263,7 @@ ASTPointer<Mapping> Parser::parseMapping()
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
std::vector<ASTPointer<VariableDeclaration>> parameters; vector<ASTPointer<VariableDeclaration>> parameters;
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{ {
@ -249,7 +284,7 @@ ASTPointer<Block> Parser::parseBlock()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
std::vector<ASTPointer<Statement>> statements; vector<ASTPointer<Statement>> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE) while (m_scanner->getCurrentToken() != Token::RBRACE)
statements.push_back(parseStatement()); statements.push_back(parseStatement());
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -447,7 +482,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LPAREN: case Token::LPAREN:
{ {
m_scanner->next(); m_scanner->next();
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); expression = nodeFactory.createNode<FunctionCall>(expression, arguments);
@ -503,9 +538,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
return expression; return expression;
} }
std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
{ {
std::vector<ASTPointer<Expression>> arguments; vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN) if (m_scanner->getCurrentToken() != Token::RPAREN)
{ {
arguments.push_back(parseExpression()); arguments.push_back(parseExpression());
@ -521,7 +556,7 @@ std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)
{ {
if (m_scanner->getCurrentToken() != _value) if (m_scanner->getCurrentToken() != _value)
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected token ") + std::string(Token::getName(_value)))); BOOST_THROW_EXCEPTION(createParserError(string("Expected token ") + string(Token::getName(_value))));
m_scanner->next(); m_scanner->next();
} }
@ -543,14 +578,15 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
ASTPointer<ASTString> Parser::getLiteralAndAdvance() ASTPointer<ASTString> Parser::getLiteralAndAdvance()
{ {
ASTPointer<ASTString> identifier = std::make_shared<ASTString>(m_scanner->getCurrentLiteral()); ASTPointer<ASTString> identifier = make_shared<ASTString>(m_scanner->getCurrentLiteral());
m_scanner->next(); m_scanner->next();
return identifier; return identifier;
} }
ParserError Parser::createParserError(std::string const& _description) const ParserError Parser::createParserError(string const& _description) const
{ {
return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description); return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))
<< errinfo_comment(_description);
} }

4
libsolidity/Parser.h

@ -34,7 +34,8 @@ class Scanner;
class Parser class Parser
{ {
public: public:
ASTPointer<ContractDefinition> parse(std::shared_ptr<Scanner> const& _scanner); ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
std::shared_ptr<std::string const> const& getSourceName() const;
private: private:
class ASTNodeFactory; class ASTNodeFactory;
@ -46,6 +47,7 @@ private:
///@{ ///@{
///@name Parsing functions for the AST nodes ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();

11
libsolidity/Scanner.cpp

@ -143,17 +143,22 @@ private:
}; // end of LiteralScope class }; // end of LiteralScope class
void Scanner::reset(CharStream const& _source) void Scanner::reset(CharStream const& _source, string const& _sourceName)
{ {
m_source = _source; m_source = _source;
m_sourceName = make_shared<string const>(_sourceName);
reset();
}
void Scanner::reset()
{
m_source.reset();
m_char = m_source.get(); m_char = m_source.get();
skipWhitespace(); skipWhitespace();
scanToken(); scanToken();
next(); next();
} }
bool Scanner::scanHexByte(char& o_scannedByte) bool Scanner::scanHexByte(char& o_scannedByte)
{ {
char x = 0; char x = 0;

14
libsolidity/Scanner.h

@ -79,6 +79,8 @@ public:
char advanceAndGet(size_t _chars=1); char advanceAndGet(size_t _chars=1);
char rollback(size_t _amount); char rollback(size_t _amount);
void reset() { m_pos = 0; }
///@{ ///@{
///@name Error printing helper functions ///@name Error printing helper functions
/// Functions that help pretty-printing parse errors /// Functions that help pretty-printing parse errors
@ -99,11 +101,12 @@ class Scanner
friend class LiteralScope; friend class LiteralScope;
public: public:
Scanner() { reset(CharStream()); } explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); }
explicit Scanner(CharStream const& _source) { reset(_source); }
/// Resets the scanner as if newly constructed with _input as input. /// Resets the scanner as if newly constructed with _source and _sourceName as input.
void reset(CharStream const& _source); void reset(CharStream const& _source, std::string const& _sourceName);
/// Resets scanner to the start of input.
void reset();
/// Returns the next token and advances input /// Returns the next token and advances input
Token::Value next(); Token::Value next();
@ -139,6 +142,8 @@ public:
std::string const& peekLiteral() const { return m_nextToken.literal; } std::string const& peekLiteral() const { return m_nextToken.literal; }
///@} ///@}
std::shared_ptr<std::string const> const& getSourceName() const { return m_sourceName; }
///@{ ///@{
///@name Error printing helper functions ///@name Error printing helper functions
/// Functions that help pretty-printing parse errors /// Functions that help pretty-printing parse errors
@ -203,6 +208,7 @@ private:
TokenDesc m_nextToken; // desc for next token (one token look-ahead) TokenDesc m_nextToken; // desc for next token (one token look-ahead)
CharStream m_source; CharStream m_source;
std::shared_ptr<std::string const> m_sourceName;
/// one character look-ahead, equals 0 at end of input /// one character look-ahead, equals 0 at end of input
char m_char; char m_char;

41
libsolidity/SourceReferenceFormatter.cpp

@ -21,6 +21,7 @@
*/ */
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
@ -38,7 +39,6 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
int startLine; int startLine;
int startColumn; int startColumn;
tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
_stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n";
int endLine; int endLine;
int endColumn; int endColumn;
tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
@ -58,37 +58,28 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
<< "Spanning multiple lines.\n"; << "Spanning multiple lines.\n";
} }
void SourceReferenceFormatter::printSourcePosition(ostream& _stream,
int _position,
const Scanner& _scanner)
{
int line;
int column;
tie(line, column) = _scanner.translatePositionToLineColumn(_position);
_stream << "at line " << (line + 1) << ", column " << (column + 1) << endl
<< _scanner.getLineAtPosition(_position) << endl
<< string(column, ' ') << "^" << endl;
}
void SourceReferenceFormatter::printExceptionInformation(ostream& _stream, void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
Exception const& _exception, Exception const& _exception,
string const& _name, string const& _name,
Scanner const& _scanner) CompilerStack& _compiler)
{ {
_stream << _name; Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
if (string const* description = boost::get_error_info<errinfo_comment>(_exception)) Scanner const* scanner;
_stream << ": " << *description;
if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception)) if (location)
{ {
_stream << " "; scanner = &_compiler.getScanner(*location->sourceName);
printSourcePosition(_stream, *position, _scanner); int startLine;
} int startColumn;
if (Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception)) tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start);
{ _stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
_stream << " ";
printSourceLocation(_stream, *location, _scanner);
} }
_stream << _name;
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description << endl;
if (location)
printSourceLocation(_stream, *location, *scanner);
} }
} }

4
libsolidity/SourceReferenceFormatter.h

@ -34,14 +34,14 @@ namespace solidity
{ {
class Scanner; // forward class Scanner; // forward
class CompilerStack; // forward
struct SourceReferenceFormatter struct SourceReferenceFormatter
{ {
public: public:
static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner);
static void printSourcePosition(std::ostream& _stream, int _position, Scanner const& _scanner);
static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, static void printExceptionInformation(std::ostream& _stream, Exception const& _exception,
std::string const& _name, Scanner const& _scanner); std::string const& _name, CompilerStack& _compiler);
}; };
} }

33
libsolidity/Types.cpp

@ -140,7 +140,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return _convertTo.getCategory() == getCategory(); return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
} }
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@ -247,6 +247,31 @@ string ContractType::toString() const
return "contract " + m_contract.getName(); return "contract " + m_contract.getName();
} }
MemberList const& ContractType::getMembers() const
{
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
members[function->getName()] = make_shared<FunctionType>(*function, false);
m_members.reset(new MemberList(members));
}
return *m_members;
}
unsigned ContractType::getFunctionIndex(string const& _functionName) const
{
unsigned index = 0;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
{
if (function->getName() == _functionName)
return index;
++index;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested."));
}
bool StructType::operator==(Type const& _other) const bool StructType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -302,7 +327,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
} }
FunctionType::FunctionType(FunctionDefinition const& _function) FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
{ {
TypePointers params; TypePointers params;
TypePointers retParams; TypePointers retParams;
@ -314,7 +339,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function)
retParams.push_back(var->getType()); retParams.push_back(var->getType());
swap(params, m_parameterTypes); swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes); swap(retParams, m_returnParameterTypes);
m_location = Location::INTERNAL; m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
} }
bool FunctionType::operator==(Type const& _other) const bool FunctionType::operator==(Type const& _other) const
@ -323,6 +348,8 @@ bool FunctionType::operator==(Type const& _other) const
return false; return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other); FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if (m_location != other.m_location)
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() || if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false; return false;

9
libsolidity/Types.h

@ -214,10 +214,17 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString() const override;
virtual MemberList const& getMembers() const override;
unsigned getFunctionIndex(std::string const& _functionName) const;
private: private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
}; };
/** /**
@ -263,7 +270,7 @@ public:
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 }; enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
virtual Category getCategory() const override { return Category::FUNCTION; } virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
Location _location = Location::INTERNAL): Location _location = Location::INTERNAL):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),

2
libweb3jsonrpc/WebThreeStubServer.cpp

@ -510,7 +510,7 @@ std::string WebThreeStubServer::eth_solidity(std::string const& _code)
catch (dev::Exception const& exception) catch (dev::Exception const& exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler.getScanner()); solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
cwarn << "Solidity compilation error: " << error.str(); cwarn << "Solidity compilation error: " << error.str();
} }
catch (...) catch (...)

1
libwhisper/Message.cpp

@ -91,7 +91,6 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
Envelope::Envelope(RLP const& _m) Envelope::Envelope(RLP const& _m)
{ {
cdebug << _m;
m_expiry = _m[0].toInt<unsigned>(); m_expiry = _m[0].toInt<unsigned>();
m_ttl = _m[1].toInt<unsigned>(); m_ttl = _m[1].toInt<unsigned>();
m_topic = _m[2].toVector<FixedHash<4>>(); m_topic = _m[2].toVector<FixedHash<4>>();

2
libwhisper/WhisperHost.cpp

@ -68,7 +68,7 @@ void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
return; return;
UpgradeGuard ll(l); UpgradeGuard ll(l);
m_messages[h] = _m; m_messages[h] = _m;
m_expiryQueue[_m.expiry()] = h; m_expiryQueue.insert(make_pair(_m.expiry(), h));
} }
// if (_p) // if (_p)

2
libwhisper/WhisperHost.h

@ -78,7 +78,7 @@ private:
mutable dev::SharedMutex x_messages; mutable dev::SharedMutex x_messages;
std::map<h256, Envelope> m_messages; std::map<h256, Envelope> m_messages;
std::map<unsigned, h256> m_expiryQueue; std::multimap<unsigned, h256> m_expiryQueue;
mutable dev::Mutex m_filterLock; mutable dev::Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters; std::map<h256, InstalledFilter> m_filters;

2
libwhisper/WhisperPeer.cpp

@ -106,5 +106,5 @@ void WhisperPeer::sendMessages()
void WhisperPeer::noteNewMessage(h256 _h, Message const& _m) void WhisperPeer::noteNewMessage(h256 _h, Message const& _m)
{ {
Guard l(x_unseen); Guard l(x_unseen);
m_unseen[rating(_m)] = _h; m_unseen.insert(make_pair(rating(_m), _h));
} }

2
libwhisper/WhisperPeer.h

@ -67,7 +67,7 @@ private:
void noteNewMessage(h256 _h, Message const& _m); void noteNewMessage(h256 _h, Message const& _m);
mutable dev::Mutex x_unseen; mutable dev::Mutex x_unseen;
std::map<unsigned, h256> m_unseen; ///< Rated according to what they want. std::multimap<unsigned, h256> m_unseen; ///< Rated according to what they want.
std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now(); std::chrono::system_clock::time_point m_timer = std::chrono::system_clock::now();
}; };

2
mix/ConstantCompilationModel.cpp

@ -46,7 +46,7 @@ CompilerResult ConstantCompilationModel::compile(QString _code)
catch (dev::Exception const& _exception) catch (dev::Exception const& _exception)
{ {
ostringstream error; ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler.getScanner()); solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", compiler);
res.success = false; res.success = false;
res.comment = QString::fromStdString(error.str()).toHtmlEscaped(); res.comment = QString::fromStdString(error.str()).toHtmlEscaped();
res.hexCode = ""; res.hexCode = "";

59
solc/main.cpp

@ -59,7 +59,7 @@ void version()
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
string infile; vector<string> infiles;
bool optimize = false; bool optimize = false;
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
@ -71,49 +71,52 @@ int main(int argc, char** argv)
else if (arg == "-V" || arg == "--version") else if (arg == "-V" || arg == "--version")
version(); version();
else else
infile = argv[i]; infiles.push_back(argv[i]);
} }
string sourceCode; map<string, string> sourceCodes;
if (infile.empty()) if (infiles.empty())
{ {
string s; string s;
while (!cin.eof()) while (!cin.eof())
{ {
getline(cin, s); getline(cin, s);
sourceCode.append(s); sourceCodes["<stdin>"].append(s);
} }
} }
else else
sourceCode = asString(dev::contents(infile)); for (string const& infile: infiles)
sourceCodes[infile] = asString(dev::contents(infile));
CompilerStack compiler; CompilerStack compiler;
try try
{ {
compiler.compile(sourceCode, optimize); for (auto const& sourceCode: sourceCodes)
compiler.addSource(sourceCode.first, sourceCode.second);
compiler.compile(optimize);
} }
catch (ParserError const& exception) catch (ParserError const& exception)
{ {
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler.getScanner()); SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler);
return -1; return -1;
} }
catch (DeclarationError const& exception) catch (DeclarationError const& exception)
{ {
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler.getScanner()); SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler);
return -1; return -1;
} }
catch (TypeError const& exception) catch (TypeError const& exception)
{ {
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler.getScanner()); SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler);
return -1; return -1;
} }
catch (CompilerError const& exception) catch (CompilerError const& exception)
{ {
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler.getScanner()); SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler);
return -1; return -1;
} }
catch (InternalCompilerError const& exception) catch (InternalCompilerError const& exception)
{ {
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler.getScanner()); SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler);
return -1; return -1;
} }
catch (Exception const& exception) catch (Exception const& exception)
@ -127,17 +130,27 @@ int main(int argc, char** argv)
return -1; return -1;
} }
cout << "Syntax tree for the contract:" << endl; cout << "Syntax trees:" << endl << endl;
ASTPrinter printer(compiler.getAST(), sourceCode); for (auto const& sourceCode: sourceCodes)
printer.print(cout); {
cout << "EVM assembly:" << endl; cout << endl << "======= " << sourceCode.first << " =======" << endl;
compiler.streamAssembly(cout); ASTPrinter printer(compiler.getAST(sourceCode.first), sourceCode.second);
cout << "Opcodes:" << endl; printer.print(cout);
cout << eth::disassemble(compiler.getBytecode()) << endl; }
cout << "Binary: " << toHex(compiler.getBytecode()) << endl; vector<string> contracts = compiler.getContractNames();
cout << "Interface specification: " << compiler.getJsonDocumentation(DocumentationType::ABI_INTERFACE) << endl; cout << endl << "Contracts:" << endl;
cout << "Natspec user documentation: " << compiler.getJsonDocumentation(DocumentationType::NATSPEC_USER) << endl; for (string const& contract: contracts)
cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(DocumentationType::NATSPEC_DEV) << endl; {
cout << endl << "======= " << contract << " =======" << endl
<< "EVM assembly:" << endl;
compiler.streamAssembly(cout, contract);
cout << "Opcodes:" << endl
<< eth::disassemble(compiler.getBytecode(contract)) << endl
<< "Binary: " << toHex(compiler.getBytecode(contract)) << endl
<< "Interface specification: " << compiler.getJsonDocumentation(contract, DocumentationType::ABI_INTERFACE) << endl
<< "Natspec user documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_USER) << endl
<< "Natspec developer documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_DEV) << endl;
}
return 0; return 0;
} }

25
test/solidityCompiler.cpp

@ -46,16 +46,23 @@ namespace
bytes compileContract(const string& _sourceCode) bytes compileContract(const string& _sourceCode)
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract; ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
Compiler compiler; if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
compiler.compileContract(*contract, {}); {
// debug BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode(); Compiler compiler;
compiler.compileContract(*contract, {});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();
}
BOOST_FAIL("No contract found in source.");
return bytes();
} }
/// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation. /// Checks that @a _compiledCode is present starting from offset @a _offset in @a _expectation.

135
test/solidityEndToEndTest.cpp

@ -47,9 +47,11 @@ class ExecutionFramework
public: public:
ExecutionFramework() { g_logVerbosity = 0; } ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0, string const& _contractName = "")
{ {
bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); dev::solidity::CompilerStack compiler;
compiler.compile(_sourceCode);
bytes code = compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
@ -115,6 +117,7 @@ private:
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{ {
m_state.addBalance(m_sender, _value); // just in case
eth::Executive executive(m_state); eth::Executive executive(m_state);
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
@ -127,7 +130,7 @@ private:
catch (...) {} catch (...) {}
if (_isCreation) if (_isCreation)
{ {
BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); BOOST_REQUIRE(!executive.create(m_sender, _value, m_gasPrice, m_gas, &_data, m_sender));
m_contractAddress = executive.newAddress(); m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress); BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
@ -135,14 +138,16 @@ private:
else else
{ {
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
} }
BOOST_REQUIRE(executive.go()); BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender);
executive.finalize(); executive.finalize();
m_output = executive.out().toVector(); m_output = executive.out().toVector();
} }
protected: protected:
Address m_sender;
Address m_contractAddress; Address m_contractAddress;
eth::State m_state; eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo; u256 const m_gasPrice = 100 * eth::szabo;
@ -898,6 +903,128 @@ BOOST_AUTO_TEST_CASE(ecrecover)
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr)); BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr));
} }
BOOST_AUTO_TEST_CASE(inter_contract_calls)
{
char const* sourceCode = R"(
contract Helper {
function multiply(uint a, uint b) returns (uint c) {
return a * b;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint c) {
return h.multiply(a, b);
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters)
{
char const* sourceCode = R"(
contract Helper {
function sel(uint a, bool select, uint b) returns (uint c) {
if (select) return a; else return b;
}
}
contract Main {
Helper h;
function callHelper(uint a, bool select, uint b) returns (uint c) {
return h.sel(a, select, b) * 3;
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3));
BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this)
{
char const* sourceCode = R"(
contract Helper {
function getAddress() returns (address addr) {
return address(this);
}
}
contract Main {
Helper h;
function callHelper() returns (address addr) {
return h.getAddress();
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress));
}
BOOST_AUTO_TEST_CASE(calls_to_this)
{
char const* sourceCode = R"(
contract Helper {
function invoke(uint a, uint b) returns (uint c) {
return this.multiply(a, b, 10);
}
function multiply(uint a, uint b, uint8 c) returns (uint ret) {
return a * b + c;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint ret) {
return h.invoke(a, b);
}
function getHelper() returns (address addr) {
return address(h);
}
function setHelper(address addr) {
h = Helper(addr);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

47
test/solidityExpressionCompiler.cpp

@ -86,27 +86,34 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
vector<vector<string>> _localVariables = {}) vector<vector<string>> _localVariables = {})
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract; ASTPointer<SourceUnit> sourceUnit;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); resolver.registerDeclarations(*sourceUnit);
FirstExpressionExtractor extractor(*contract); for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
BOOST_REQUIRE(extractor.getExpression() != nullptr); if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
CompilerContext context; BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
for (vector<string> const& function: _functions) FirstExpressionExtractor extractor(*contract);
context.addFunction(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver))); BOOST_REQUIRE(extractor.getExpression() != nullptr);
for (vector<string> const& variable: _localVariables)
context.addVariable(dynamic_cast<VariableDeclaration const&>(resolveDeclaration(variable, resolver))); CompilerContext context;
for (vector<string> const& function: _functions)
ExpressionCompiler::compileExpression(context, *extractor.getExpression()); context.addFunction(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
for (vector<string> const& variable: _localVariables)
for (vector<string> const& function: _functions) context.addVariable(dynamic_cast<VariableDeclaration const&>(resolveDeclaration(variable, resolver)));
context << context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
bytes instructions = context.getAssembledBytecode(); ExpressionCompiler::compileExpression(context, *extractor.getExpression());
// debug
// cout << eth::disassemble(instructions) << endl; for (vector<string> const& function: _functions)
return instructions; context << context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver)));
bytes instructions = context.getAssembledBytecode();
// debug
// cout << eth::disassemble(instructions) << endl;
return instructions;
}
BOOST_FAIL("No contract found in source.");
return bytes();
} }
} // end anonymous namespace } // end anonymous namespace

2
test/solidityJSONInterfaceTest.cpp

@ -50,7 +50,7 @@ public:
msg += *extra; msg += *extra;
BOOST_FAIL(msg); BOOST_FAIL(msg);
} }
std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(DocumentationType::ABI_INTERFACE); std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation("", DocumentationType::ABI_INTERFACE);
Json::Value generatedInterface; Json::Value generatedInterface;
m_reader.parse(generatedInterfaceString, generatedInterface); m_reader.parse(generatedInterfaceString, generatedInterface);
Json::Value expectedInterface; Json::Value expectedInterface;

8
test/solidityNameAndTypeResolution.cpp

@ -41,10 +41,12 @@ namespace
void parseTextAndResolveNames(std::string const& _source) void parseTextAndResolveNames(std::string const& _source)
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract = parser.parse( ASTPointer<SourceUnit> sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source)));
std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver({}); NameAndTypeResolver resolver({});
resolver.resolveNamesAndTypes(*contract); resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
resolver.resolveNamesAndTypes(*contract);
} }
} }

4
test/solidityNatspecJSON.cpp

@ -55,9 +55,9 @@ public:
} }
if (_userDocumentation) if (_userDocumentation)
generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_USER); generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_USER);
else else
generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_DEV); generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_DEV);
Json::Value generatedDocumentation; Json::Value generatedDocumentation;
m_reader.parse(generatedDocumentationString, generatedDocumentation); m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation; Json::Value expectedDocumentation;

55
test/solidityParser.cpp

@ -21,13 +21,15 @@
*/ */
#include <string> #include <string>
#include <memory>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -40,7 +42,12 @@ namespace
ASTPointer<ContractDefinition> parseText(std::string const& _source) ASTPointer<ContractDefinition> parseText(std::string const& _source)
{ {
Parser parser; Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source))); ASTPointer<SourceUnit> sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source)));
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ASTPointer<ContractDefinition> contract = dynamic_pointer_cast<ContractDefinition>(node))
return contract;
BOOST_FAIL("No contract found in source.");
return ASTPointer<ContractDefinition>();
} }
} }
@ -380,6 +387,50 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
} }
BOOST_AUTO_TEST_CASE(import_directive)
{
char const* text = "import \"abc\";\n"
"contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multiple_contracts)
{
char const* text = "contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"contract test2 {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multiple_contracts_and_imports)
{
char const* text = "import \"abc\";\n"
"contract test {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"import \"def\";\n"
"contract test2 {\n"
" function fun() {\n"
" uint64(2);\n"
" }\n"
"}\n"
"import \"ghi\";\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save