diff options
author | Marek Kotewicz <marek.kotewicz@gmail.com> | 2014-12-08 20:56:30 +0800 |
---|---|---|
committer | Marek Kotewicz <marek.kotewicz@gmail.com> | 2014-12-08 20:56:30 +0800 |
commit | 6afb6757d785fab414915ab11c1784b585669143 (patch) | |
tree | 862171b42c5f76365d6ae0e3c50d3e1c4111343d | |
parent | ddf473aa0b3684ac2c3ad9f6135e3be6cb2b06e7 (diff) | |
parent | 260a1529a758fb7b75840e05d8c0be18975ff3b2 (diff) | |
download | dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar.gz dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar.bz2 dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar.lz dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar.xz dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.tar.zst dexon-solidity-6afb6757d785fab414915ab11c1784b585669143.zip |
Merge branch 'develop' into build_enhancement
-rw-r--r-- | AST.cpp | 69 | ||||
-rw-r--r-- | AST.h | 36 | ||||
-rw-r--r-- | ASTForward.h | 2 | ||||
-rw-r--r-- | ASTPrinter.cpp | 9 | ||||
-rw-r--r-- | ASTPrinter.h | 3 | ||||
-rw-r--r-- | ASTVisitor.h | 4 | ||||
-rw-r--r-- | BaseTypes.h | 10 | ||||
-rw-r--r-- | CompilerStack.cpp | 180 | ||||
-rw-r--r-- | CompilerStack.h | 77 | ||||
-rw-r--r-- | DeclarationContainer.cpp | 4 | ||||
-rw-r--r-- | DeclarationContainer.h | 2 | ||||
-rw-r--r-- | Exceptions.h | 1 | ||||
-rw-r--r-- | GlobalContext.cpp | 3 | ||||
-rw-r--r-- | GlobalContext.h | 4 | ||||
-rw-r--r-- | InterfaceHandler.cpp | 14 | ||||
-rw-r--r-- | InterfaceHandler.h | 8 | ||||
-rw-r--r-- | NameAndTypeResolver.cpp | 13 | ||||
-rw-r--r-- | NameAndTypeResolver.h | 6 | ||||
-rw-r--r-- | Parser.cpp | 86 | ||||
-rw-r--r-- | Parser.h | 4 | ||||
-rw-r--r-- | Scanner.cpp | 11 | ||||
-rw-r--r-- | Scanner.h | 14 | ||||
-rw-r--r-- | SourceReferenceFormatter.cpp | 41 | ||||
-rw-r--r-- | SourceReferenceFormatter.h | 4 |
24 files changed, 441 insertions, 164 deletions
@@ -33,6 +33,19 @@ namespace dev 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) { if (_visitor.visit(*this)) @@ -57,34 +70,6 @@ void StructDefinition::checkValidityOfMembers() 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) { if (_visitor.visit(*this)) @@ -312,6 +297,34 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co 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() { for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters()) @@ -80,6 +80,42 @@ private: }; /** + * 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). */ class Declaration: public ASTNode diff --git a/ASTForward.h b/ASTForward.h index a369c8a7..8b4bac1c 100644 --- a/ASTForward.h +++ b/ASTForward.h @@ -34,6 +34,8 @@ namespace solidity { class ASTNode; +class SourceUnit; +class ImportDirective; class Declaration; class ContractDefinition; class StructDefinition; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index 987ad11c..3d09fd9a 100644 --- a/ASTPrinter.cpp +++ b/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) { writeLine("ContractDefinition \"" + _node.getName() + "\""); @@ -270,7 +277,7 @@ bool ASTPrinter::visit(Literal& _node) return goDeeper(); } -void ASTPrinter::endVisit(ASTNode&) +void ASTPrinter::endVisit(ImportDirective&) { m_indentation--; } diff --git a/ASTPrinter.h b/ASTPrinter.h index e0757fbc..1a18fc4a 100644 --- a/ASTPrinter.h +++ b/ASTPrinter.h @@ -42,6 +42,7 @@ public: /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); + bool visit(ImportDirective& _node) override; bool visit(ContractDefinition& _node) override; bool visit(StructDefinition& _node) override; bool visit(ParameterList& _node) override; @@ -73,7 +74,7 @@ public: bool visit(ElementaryTypeNameExpression& _node) override; bool visit(Literal& _node) override; - void endVisit(ASTNode& _node) override; + void endVisit(ImportDirective&) override; void endVisit(ContractDefinition&) override; void endVisit(StructDefinition&) override; void endVisit(ParameterList&) override; diff --git a/ASTVisitor.h b/ASTVisitor.h index 6e579f35..bf1ccc41 100644 --- a/ASTVisitor.h +++ b/ASTVisitor.h @@ -42,6 +42,8 @@ class ASTVisitor { public: 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(StructDefinition&) { return true; } virtual bool visit(ParameterList&) { return true; } @@ -74,6 +76,8 @@ public: virtual bool visit(Literal&) { return true; } virtual void endVisit(ASTNode&) { } + virtual void endVisit(SourceUnit&) { } + virtual void endVisit(ImportDirective&) { } virtual void endVisit(ContractDefinition&) { } virtual void endVisit(StructDefinition&) { } virtual void endVisit(ParameterList&) { } diff --git a/BaseTypes.h b/BaseTypes.h index d1ffd7bb..a8fd77c8 100644 --- a/BaseTypes.h +++ b/BaseTypes.h @@ -22,6 +22,8 @@ #pragma once +#include <memory> +#include <string> #include <ostream> namespace dev @@ -35,19 +37,19 @@ namespace solidity */ 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) { } - bool IsValid() const { return start >= 0 && end >= start; } - int start; int end; + std::shared_ptr<std::string const> sourceName; }; /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, Location const& _location) { - return _out << "[" << _location.start << "," << _location.end << ")"; + return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; } } diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 01bf99d9..41ae5603 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -36,22 +36,43 @@ namespace dev 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) { reset(); - m_scanner = make_shared<Scanner>(CharStream(_sourceCode)); + addSource("", _sourceCode); } void CompilerStack::parse() { - if (!m_scanner) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); - m_contractASTNode = Parser().parse(m_scanner); + for (auto& sourcePair: m_sources) + { + sourcePair.second.scanner->reset(); + sourcePair.second.ast = Parser().parse(sourcePair.second.scanner); + } + resolveImports(); + m_globalContext = make_shared<GlobalContext>(); - m_globalContext->setCurrentContract(*m_contractASTNode); - NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); + NameAndTypeResolver resolver(m_globalContext->getDeclarations()); + 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; } @@ -61,54 +82,90 @@ void CompilerStack::parse(string const& _sourceCode) parse(); } -bytes const& CompilerStack::compile(bool _optimize) +vector<string> CompilerStack::getContractNames() { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - m_bytecode.clear(); - m_compiler = make_shared<Compiler>(); - m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); - return m_bytecode = m_compiler->getAssembledBytecode(_optimize); + vector<string> contractNames; + for (auto const& contract: m_contracts) + contractNames.push_back(contract.first); + 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) { 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()) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); - m_compiler->streamAssembly(_outStream); + return getContract(_contractName).bytecode; } -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) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - auto createDocIfNotThere = [this, _type](std::unique_ptr<string>& _doc) - { - if (!_doc) - _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type); - }; + Contract& contract = getContract(_contractName); + std::unique_ptr<string>* doc; switch (_type) { case DocumentationType::NATSPEC_USER: - createDocIfNotThere(m_userDocumentation); - return *m_userDocumentation; + doc = &contract.userDocumentation; + break; case DocumentationType::NATSPEC_DEV: - createDocIfNotThere(m_devDocumentation); - return *m_devDocumentation; + doc = &contract.devDocumentation; + break; case DocumentationType::ABI_INTERFACE: - createDocIfNotThere(m_interface); - return *m_interface; + doc = &contract.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) @@ -117,7 +174,70 @@ bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimiz 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>()) {} } } diff --git a/CompilerStack.h b/CompilerStack.h index 6286eb7f..6f036d3f 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -25,6 +25,7 @@ #include <ostream> #include <string> #include <memory> +#include <boost/noncopyable.hpp> #include <libdevcore/Common.h> namespace dev { @@ -33,6 +34,7 @@ namespace solidity { // forward declarations class Scanner; class ContractDefinition; +class SourceUnit; class Compiler; class GlobalContext; class InterfaceHandler; @@ -49,36 +51,44 @@ enum class DocumentationType: uint8_t * 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. */ -class CompilerStack +class CompilerStack: boost::noncopyable { public: - CompilerStack(); - void reset() { *this = CompilerStack(); } + CompilerStack(): m_parseSuccessful(false) {} + + /// 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); + /// Parses all source units that were added void parse(); + /// Sets the given source code as the only source unit and parses it. void parse(std::string const& _sourceCode); - /// Compiles the contract that was previously parsed. - bytes const& compile(bool _optimize = false); + /// Returns a list of the contract names in the sources. + 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. + /// @returns the compiled bytecode 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. /// 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. /// 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. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. - /// Can be one of 3 types defined at @c documentation_type - std::string const& getJsonDocumentation(DocumentationType type); + /// Can be one of 3 types defined at @c DocumentationType + std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type); /// Returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& getScanner() const { return *m_scanner; } - ContractDefinition& getAST() const { return *m_contractASTNode; } + Scanner const& getScanner(std::string const& _sourceName = ""); + SourceUnit& getAST(std::string const& _sourceName = ""); /// 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. @@ -101,16 +111,41 @@ public: } private: - std::shared_ptr<Scanner> m_scanner; - std::shared_ptr<GlobalContext> m_globalContext; - std::shared_ptr<ContractDefinition> m_contractASTNode; + /** + * Information pertaining to one source unit, filled gradually during parsing and compilation. + */ + 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; - std::unique_ptr<std::string> m_interface; - std::unique_ptr<std::string> m_userDocumentation; - std::unique_ptr<std::string> m_devDocumentation; - std::shared_ptr<Compiler> m_compiler; - std::shared_ptr<InterfaceHandler> m_interfaceHandler; - bytes m_bytecode; + std::map<std::string, Source> m_sources; + std::shared_ptr<GlobalContext> m_globalContext; + std::vector<Source const*> m_sourceOrder; + std::map<std::string, Contract> m_contracts; }; } diff --git a/DeclarationContainer.cpp b/DeclarationContainer.cpp index 6ea9c28c..c0dea757 100644 --- a/DeclarationContainer.cpp +++ b/DeclarationContainer.cpp @@ -28,9 +28,9 @@ namespace dev 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; m_declarations[_declaration.getName()] = &_declaration; return true; diff --git a/DeclarationContainer.h b/DeclarationContainer.h index db681289..e1b363e0 100644 --- a/DeclarationContainer.h +++ b/DeclarationContainer.h @@ -43,7 +43,7 @@ public: m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// 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* getEnclosingDeclaration() const { return m_enclosingDeclaration; } diff --git a/Exceptions.h b/Exceptions.h index 8298c981..14f91977 100644 --- a/Exceptions.h +++ b/Exceptions.h @@ -38,7 +38,6 @@ struct CompilerError: virtual Exception {}; struct InternalCompilerError: 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; } diff --git a/GlobalContext.cpp b/GlobalContext.cpp index b54b93c0..2d384a15 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -74,11 +74,10 @@ vector<Declaration*> GlobalContext::getDeclarations() const declarations.reserve(m_magicVariables.size() + 1); for (ASTPointer<Declaration> const& variable: m_magicVariables) declarations.push_back(variable.get()); - declarations.push_back(getCurrentThis()); return declarations; } -MagicVariableDeclaration*GlobalContext::getCurrentThis() const +MagicVariableDeclaration* GlobalContext::getCurrentThis() const { if (!m_thisPointer[m_currentContract]) m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( diff --git a/GlobalContext.h b/GlobalContext.h index 0166734c..ddbd049c 100644 --- a/GlobalContext.h +++ b/GlobalContext.h @@ -47,12 +47,14 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + MagicVariableDeclaration* getCurrentThis() const; + /// @returns all magic variables. std::vector<MagicVariableDeclaration const*> getMagicVariables() const; + /// @returns a vector of all implicit global declarations excluding "this". std::vector<Declaration*> getDeclarations() const; private: - MagicVariableDeclaration* getCurrentThis() const; std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables; ContractDefinition const* m_currentContract; std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer; diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 18c053cb..c3e62cad 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler() 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) { switch(_type) @@ -32,11 +32,11 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(std::shared_ptr< 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); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; 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))); } -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 methods(Json::objectValue); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value user; 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))); } -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 // should be thrown Json::Value doc; Json::Value methods(Json::objectValue); - for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; auto strPtr = f->getDocumentation(); diff --git a/InterfaceHandler.h b/InterfaceHandler.h index a31cd5c1..4fa63fcd 100644 --- a/InterfaceHandler.h +++ b/InterfaceHandler.h @@ -59,23 +59,23 @@ public: /// types provided by @c DocumentationType /// @return A unique pointer contained string with the json /// 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); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// 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 /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// 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 /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json /// 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: void resetUser(); diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp index d473348b..3715df6a 100644 --- a/NameAndTypeResolver.cpp +++ b/NameAndTypeResolver.cpp @@ -38,9 +38,14 @@ NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globa 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) { - DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); @@ -65,6 +70,12 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) 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 { auto iterator = m_scopes.find(_scope); diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h index 797eca60..816d8006 100644 --- a/NameAndTypeResolver.h +++ b/NameAndTypeResolver.h @@ -42,7 +42,13 @@ class NameAndTypeResolver: private boost::noncopyable { public: 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); + /// 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, /// the global scope is used (i.e. the one containing only the contract). @@ -20,30 +20,27 @@ * Solidity parser. */ +#include <vector> #include <libdevcore/Log.h> #include <libsolidity/BaseTypes.h> #include <libsolidity/Parser.h> #include <libsolidity/Scanner.h> #include <libsolidity/Exceptions.h> +using namespace std; + namespace dev { 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 /// while it is being parsed class Parser::ASTNodeFactory { 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 setLocationEmpty() { m_location.end = m_location.start; } @@ -55,7 +52,7 @@ public: { if (m_location.end < 0) markEndPosition(); - return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); + return make_shared<NodeType>(m_location, forward<Args>(_args)...); } private: @@ -63,6 +60,33 @@ private: 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 { return m_scanner->getCurrentLocation().start; @@ -73,15 +97,27 @@ int Parser::getEndPosition() const 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() { ASTNodeFactory nodeFactory(*this); expectToken(Token::CONTRACT); ASTPointer<ASTString> name = expectIdentifierToken(); expectToken(Token::LBRACE); - std::vector<ASTPointer<StructDefinition>> structs; - std::vector<ASTPointer<VariableDeclaration>> stateVariables; - std::vector<ASTPointer<FunctionDefinition>> functions; + vector<ASTPointer<StructDefinition>> structs; + vector<ASTPointer<VariableDeclaration>> stateVariables; + vector<ASTPointer<FunctionDefinition>> functions; bool visibilityIsPublic = true; while (true) { @@ -110,7 +146,6 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - expectToken(Token::EOS); return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); } @@ -119,7 +154,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) ASTNodeFactory nodeFactory(*this); ASTPointer<ASTString> docstring; if (m_scanner->getCurrentCommentLiteral() != "") - docstring = std::make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); + docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); expectToken(Token::FUNCTION); ASTPointer<ASTString> name(expectIdentifierToken()); @@ -142,7 +177,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); - returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>()); + returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>()); } ASTPointer<Block> block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -156,7 +191,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); ASTPointer<ASTString> name = expectIdentifierToken(); - std::vector<ASTPointer<VariableDeclaration>> members; + vector<ASTPointer<VariableDeclaration>> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -228,7 +263,7 @@ ASTPointer<Mapping> Parser::parseMapping() ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - std::vector<ASTPointer<VariableDeclaration>> parameters; + vector<ASTPointer<VariableDeclaration>> parameters; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { @@ -249,7 +284,7 @@ ASTPointer<Block> Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); - std::vector<ASTPointer<Statement>> statements; + vector<ASTPointer<Statement>> statements; while (m_scanner->getCurrentToken() != Token::RBRACE) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); @@ -447,7 +482,7 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); + vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); expression = nodeFactory.createNode<FunctionCall>(expression, arguments); @@ -503,9 +538,9 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() 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) { arguments.push_back(parseExpression()); @@ -521,7 +556,7 @@ std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() void Parser::expectToken(Token::Value _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(); } @@ -543,14 +578,15 @@ ASTPointer<ASTString> Parser::expectIdentifierToken() 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(); 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); } @@ -34,7 +34,8 @@ class Scanner; class Parser { 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: class ASTNodeFactory; @@ -46,6 +47,7 @@ private: ///@{ ///@name Parsing functions for the AST nodes + ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ASTPointer<StructDefinition> parseStructDefinition(); diff --git a/Scanner.cpp b/Scanner.cpp index 2f5f8d37..08bf744d 100644 --- a/Scanner.cpp +++ b/Scanner.cpp @@ -143,17 +143,22 @@ private: }; // end of LiteralScope class -void Scanner::reset(CharStream const& _source) +void Scanner::reset(CharStream const& _source, string const& _sourceName) { m_source = _source; + m_sourceName = make_shared<string const>(_sourceName); + reset(); +} + +void Scanner::reset() +{ + m_source.reset(); m_char = m_source.get(); skipWhitespace(); scanToken(); - next(); } - bool Scanner::scanHexByte(char& o_scannedByte) { char x = 0; @@ -79,6 +79,8 @@ public: char advanceAndGet(size_t _chars=1); char rollback(size_t _amount); + void reset() { m_pos = 0; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -99,11 +101,12 @@ class Scanner friend class LiteralScope; public: - Scanner() { reset(CharStream()); } - explicit Scanner(CharStream const& _source) { reset(_source); } + explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } - /// Resets the scanner as if newly constructed with _input as input. - void reset(CharStream const& _source); + /// Resets the scanner as if newly constructed with _source and _sourceName as input. + 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 Token::Value next(); @@ -139,6 +142,8 @@ public: 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 /// Functions that help pretty-printing parse errors @@ -203,6 +208,7 @@ private: TokenDesc m_nextToken; // desc for next token (one token look-ahead) CharStream m_source; + std::shared_ptr<std::string const> m_sourceName; /// one character look-ahead, equals 0 at end of input char m_char; diff --git a/SourceReferenceFormatter.cpp b/SourceReferenceFormatter.cpp index d3f2152a..1a7d12a9 100644 --- a/SourceReferenceFormatter.cpp +++ b/SourceReferenceFormatter.cpp @@ -21,6 +21,7 @@ */ #include <libsolidity/SourceReferenceFormatter.h> +#include <libsolidity/CompilerStack.h> #include <libsolidity/Scanner.h> #include <libsolidity/Exceptions.h> @@ -38,7 +39,6 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, int startLine; int startColumn; tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); - _stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n"; int endLine; int endColumn; tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); @@ -58,37 +58,28 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream, << "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, Exception const& _exception, string const& _name, - Scanner const& _scanner) + CompilerStack& _compiler) { - _stream << _name; - if (string const* description = boost::get_error_info<errinfo_comment>(_exception)) - _stream << ": " << *description; + Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); + Scanner const* scanner; - if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception)) + if (location) { - _stream << " "; - printSourcePosition(_stream, *position, _scanner); - } - if (Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception)) - { - _stream << " "; - printSourceLocation(_stream, *location, _scanner); + scanner = &_compiler.getScanner(*location->sourceName); + int startLine; + int startColumn; + tie(startLine, startColumn) = scanner->translatePositionToLineColumn(location->start); + _stream << *location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } + _stream << _name; + if (string const* description = boost::get_error_info<errinfo_comment>(_exception)) + _stream << ": " << *description << endl; + + if (location) + printSourceLocation(_stream, *location, *scanner); } } diff --git a/SourceReferenceFormatter.h b/SourceReferenceFormatter.h index 4736066f..9b556704 100644 --- a/SourceReferenceFormatter.h +++ b/SourceReferenceFormatter.h @@ -34,14 +34,14 @@ namespace solidity { class Scanner; // forward +class CompilerStack; // forward struct SourceReferenceFormatter { public: 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, - std::string const& _name, Scanner const& _scanner); + std::string const& _name, CompilerStack& _compiler); }; } |