diff options
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.cpp | 38 | ||||
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.h | 13 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 93 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 27 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 10 | ||||
-rw-r--r-- | libsolidity/ast/AST.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 6 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 35 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 2 | ||||
-rw-r--r-- | test/libsolidity/Imports.cpp | 100 | ||||
-rw-r--r-- | test/libsolidity/SolidityExpressionCompiler.cpp | 25 |
12 files changed, 288 insertions, 75 deletions
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7339ad5d..ac56562e 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -28,15 +28,19 @@ using namespace std; using namespace dev; using namespace dev::solidity; -Declaration const* DeclarationContainer::conflictingDeclaration(Declaration const& _declaration) const +Declaration const* DeclarationContainer::conflictingDeclaration( + Declaration const& _declaration, + ASTString const* _name +) const { - ASTString const& declarationName(_declaration.name()); - solAssert(!declarationName.empty(), ""); + if (!_name) + _name = &_declaration.name(); + solAssert(!_name->empty(), ""); vector<Declaration const*> declarations; - if (m_declarations.count(declarationName)) - declarations += m_declarations.at(declarationName); - if (m_invisibleDeclarations.count(declarationName)) - declarations += m_invisibleDeclarations.at(declarationName); + if (m_declarations.count(*_name)) + declarations += m_declarations.at(*_name); + if (m_invisibleDeclarations.count(*_name)) + declarations += m_invisibleDeclarations.at(*_name); if (dynamic_cast<FunctionDefinition const*>(&_declaration)) { @@ -51,25 +55,31 @@ Declaration const* DeclarationContainer::conflictingDeclaration(Declaration cons return nullptr; } -bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update) +bool DeclarationContainer::registerDeclaration( + Declaration const& _declaration, + ASTString const* _name, + bool _invisible, + bool _update +) { - ASTString const& declarationName(_declaration.name()); - if (declarationName.empty()) + if (!_name) + _name = &_declaration.name(); + if (_name->empty()) return true; if (_update) { solAssert(!dynamic_cast<FunctionDefinition const*>(&_declaration), "Attempt to update function definition."); - m_declarations.erase(declarationName); - m_invisibleDeclarations.erase(declarationName); + m_declarations.erase(*_name); + m_invisibleDeclarations.erase(*_name); } else if (conflictingDeclaration(_declaration)) return false; if (_invisible) - m_invisibleDeclarations[declarationName].push_back(&_declaration); + m_invisibleDeclarations[*_name].push_back(&_declaration); else - m_declarations[declarationName].push_back(&_declaration); + m_declarations[*_name].push_back(&_declaration); return true; } diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index 064724d1..5862f7a5 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -41,23 +41,24 @@ class DeclarationContainer { public: explicit DeclarationContainer( - Declaration const* _enclosingDeclaration = nullptr, + ASTNode const* _enclosingNode = nullptr, DeclarationContainer const* _enclosingContainer = nullptr ): - m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} + m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) {} /// Registers the declaration in the scope unless its name is already declared or the name is empty. + /// @param _name the name to register, if nullptr the intrinsic name of @a _declaration is used. /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. - bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false); + bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const; - Declaration const* enclosingDeclaration() const { return m_enclosingDeclaration; } + ASTNode const* enclosingNode() const { return m_enclosingNode; } std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; } /// @returns whether declaration is valid, and if not also returns previous declaration. - Declaration const* conflictingDeclaration(Declaration const& _declaration) const; + Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; private: - Declaration const* m_enclosingDeclaration; + ASTNode const* m_enclosingNode; DeclarationContainer const* m_enclosingContainer; std::map<ASTString, std::vector<Declaration const*>> m_declarations; std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 612989e1..efdb1953 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -38,12 +38,18 @@ NameAndTypeResolver::NameAndTypeResolver( ) : m_errors(_errors) { + if (!m_scopes[nullptr]) + m_scopes[nullptr].reset(new DeclarationContainer()); for (Declaration const* declaration: _globals) - m_scopes[nullptr].registerDeclaration(*declaration); + m_scopes[nullptr]->registerDeclaration(*declaration); } bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) { + solAssert(!m_scopes[&_sourceUnit], ""); + m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); + m_currentScope = m_scopes[&_sourceUnit].get(); + // The helper registers all declarations in m_scopes as a side-effect of its construction. try { @@ -58,11 +64,43 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) return true; } +bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, SourceUnit const*> const& _sourceUnits) +{ + DeclarationContainer& target = *m_scopes.at(&_sourceUnit); + bool error = false; + for (auto const& node: _sourceUnit.nodes()) + if (auto imp = dynamic_cast<ImportDirective const*>(node.get())) + { + string const& path = imp->annotation().absolutePath; + if (!_sourceUnits.count(path)) + { + reportDeclarationError( node->location(), + "Import \"" + + path + + "\" (referenced as \"" + + imp->identifier() + + "\") not found." + ); + error = true; + } + else + { + auto scope = m_scopes.find(_sourceUnits.at(path)); + solAssert(scope != end(m_scopes), ""); + for (auto const& nameAndDeclaration: scope->second->declarations()) + for (auto const& declaration: nameAndDeclaration.second) + target.registerDeclaration(*declaration, &nameAndDeclaration.first); + } + } + return !error; +} + bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { try { - m_currentScope = &m_scopes[nullptr]; + m_currentScope = m_scopes[_contract.scope()].get(); + solAssert(!!m_currentScope, ""); ReferencesResolver resolver(m_errors, *this, nullptr); bool success = true; @@ -70,12 +108,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) if (!resolver.resolve(*baseContract)) success = false; - m_currentScope = &m_scopes[&_contract]; + m_currentScope = m_scopes[&_contract].get(); if (success) { linearizeBaseContracts(_contract); - std::vector<ContractDefinition const*> properBases( + vector<ContractDefinition const*> properBases( ++_contract.annotation().linearizedBaseContracts.begin(), _contract.annotation().linearizedBaseContracts.end() ); @@ -87,7 +125,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) // these can contain code, only resolve parameters for now for (ASTPointer<ASTNode> const& node: _contract.subNodes()) { - m_currentScope = &m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract]; + m_currentScope = m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract].get(); if (!resolver.resolve(*node)) success = false; } @@ -95,12 +133,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) if (!success) return false; - m_currentScope = &m_scopes[&_contract]; + m_currentScope = m_scopes[&_contract].get(); // now resolve references inside the code for (ModifierDefinition const* modifier: _contract.functionModifiers()) { - m_currentScope = &m_scopes[modifier]; + m_currentScope = m_scopes[modifier].get(); ReferencesResolver resolver(m_errors, *this, nullptr, true); if (!resolver.resolve(*modifier)) success = false; @@ -108,7 +146,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) for (FunctionDefinition const* function: _contract.definedFunctions()) { - m_currentScope = &m_scopes[function]; + m_currentScope = m_scopes[function].get(); if (!ReferencesResolver( m_errors, *this, @@ -133,7 +171,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) { try { - m_scopes[nullptr].registerDeclaration(_declaration, false, true); + m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true); solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope."); } catch (FatalError const&) @@ -145,12 +183,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) return true; } -vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const +vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const { auto iterator = m_scopes.find(_scope); if (iterator == end(m_scopes)) return vector<Declaration const*>({}); - return iterator->second.resolveName(_name, false); + return iterator->second->resolveName(_name, false); } vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const @@ -166,7 +204,7 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c { if (!m_scopes.count(candidates.front())) return nullptr; - candidates = m_scopes.at(candidates.front()).resolveName(_path[i], false); + candidates = m_scopes.at(candidates.front())->resolveName(_path[i], false); } if (candidates.size() == 1) return candidates.front(); @@ -210,7 +248,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) { auto iterator = m_scopes.find(&_base); solAssert(iterator != end(m_scopes), ""); - for (auto const& nameAndDeclaration: iterator->second.declarations()) + for (auto const& nameAndDeclaration: iterator->second->declarations()) for (auto const& declaration: nameAndDeclaration.second) // Import if it was declared in the base, is not the constructor and is visible in derived classes if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts()) @@ -342,14 +380,15 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e) } DeclarationRegistrationHelper::DeclarationRegistrationHelper( - map<ASTNode const*, DeclarationContainer>& _scopes, + map<ASTNode const*, unique_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, ErrorList& _errors ): m_scopes(_scopes), - m_currentScope(nullptr), + m_currentScope(&_astRoot), m_errors(_errors) { + solAssert(!!m_scopes.at(m_currentScope), ""); _astRoot.accept(*this); } @@ -450,9 +489,10 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) { - map<ASTNode const*, DeclarationContainer>::iterator iter; + map<ASTNode const*, unique_ptr<DeclarationContainer>>::iterator iter; bool newlyAdded; - tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope])); + unique_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); + tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container)); solAssert(newlyAdded, "Unable to add new scope."); m_currentScope = &_declaration; } @@ -460,16 +500,16 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara void DeclarationRegistrationHelper::closeCurrentScope() { solAssert(m_currentScope, "Closed non-existing scope."); - m_currentScope = m_scopes[m_currentScope].enclosingDeclaration(); + m_currentScope = m_scopes[m_currentScope]->enclosingNode(); } void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) { - if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract())) + if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract())) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; - Declaration const* conflictingDeclaration = m_scopes[m_currentScope].conflictingDeclaration(_declaration); + Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration); solAssert(conflictingDeclaration, ""); if (_declaration.location().start < conflictingDeclaration->location().start) @@ -500,14 +540,17 @@ string DeclarationRegistrationHelper::currentCanonicalName() const { string ret; for ( - Declaration const* scope = m_currentScope; + ASTNode const* scope = m_currentScope; scope != nullptr; - scope = m_scopes[scope].enclosingDeclaration() + scope = m_scopes[scope]->enclosingNode() ) { - if (!ret.empty()) - ret = "." + ret; - ret = scope->name() + ret; + if (auto decl = dynamic_cast<Declaration const*>(scope)) + { + if (!ret.empty()) + ret = "." + ret; + ret = decl->name() + ret; + } } return ret; } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 1547a274..3c444aba 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -46,6 +46,8 @@ public: /// Registers all declarations found in the source unit. /// @returns false in case of error. bool registerDeclarations(SourceUnit& _sourceUnit); + /// Applies the effect of import directives. + bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits); /// Resolves all names and types referenced from the given contract. /// @returns false in case of error. bool resolveNamesAndTypes(ContractDefinition& _contract); @@ -55,9 +57,9 @@ public: bool updateDeclaration(Declaration const& _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). + /// the global scope is used (i.e. the one containing only the pre-defined global variables). /// @returns a pointer to the declaration on success or nullptr on failure. - std::vector<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; + std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const; /// Resolves a name in the "current" scope. Should only be called during the initial /// resolving phase. @@ -88,11 +90,6 @@ private: template <class _T> static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge); - /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, - /// where nullptr denotes the global scope. Note that structs are not scope since they do - /// not contain code. - std::map<ASTNode const*, DeclarationContainer> m_scopes; - // creates the Declaration error and adds it in the errors list void reportDeclarationError( SourceLocation _sourceLoction, @@ -110,6 +107,12 @@ private: // creates the Declaration error and adds it in the errors list and throws FatalError void reportFatalTypeError(Error const& _e); + + /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, + /// where nullptr denotes the global scope. Note that structs are not scope since they do + /// not contain code. + std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>> m_scopes; + DeclarationContainer* m_currentScope = nullptr; ErrorList& m_errors; }; @@ -121,7 +124,11 @@ private: class DeclarationRegistrationHelper: private ASTVisitor { public: - DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot, ErrorList& _errors); + DeclarationRegistrationHelper( + std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& _scopes, + ASTNode& _astRoot, + ErrorList& _errors + ); private: bool visit(ContractDefinition& _contract) override; @@ -159,8 +166,8 @@ private: // creates the Declaration error and adds it in the errors list and throws FatalError void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); - std::map<ASTNode const*, DeclarationContainer>& m_scopes; - Declaration const* m_currentScope = nullptr; + std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& m_scopes; + ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; ErrorList& m_errors; }; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 2fe53e8b..ca002f58 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -139,7 +139,9 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) bool isPointer = true; if (_variable.isExternalCallableParameter()) { - auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope()); + auto const& contract = dynamic_cast<ContractDefinition const&>( + *dynamic_cast<Declaration const&>(*_variable.scope()).scope() + ); if (contract.isLibrary()) { if (varLoc == Location::Memory) @@ -162,9 +164,11 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) else typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; } - else if (_variable.isCallableParameter() && _variable.scope()->isPublic()) + else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic()) { - auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope()); + auto const& contract = dynamic_cast<ContractDefinition const&>( + *dynamic_cast<Declaration const&>(*_variable.scope()).scope() + ); // force locations of public or external function (return) parameters to memory if (varLoc == Location::Storage && !contract.isLibrary()) fatalTypeError(_variable.location(), diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6006d441..701202f9 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -56,6 +56,13 @@ Error ASTNode::createTypeError(string const& _description) const return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); } +ImportAnnotation& ImportDirective::annotation() const +{ + if (!m_annotation) + m_annotation = new ImportAnnotation(); + return static_cast<ImportAnnotation&>(*m_annotation); +} + map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const { auto exportedFunctionList = interfaceFunctionList(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 1217d945..75cb9ab2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -142,6 +142,7 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; ASTString const& identifier() const { return *m_identifier; } + virtual ImportAnnotation& annotation() const override; private: ASTPointer<ASTString> m_identifier; @@ -172,8 +173,8 @@ public: /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. - Declaration const* scope() const { return m_scope; } - void setScope(Declaration const* _scope) { m_scope = _scope; } + ASTNode const* scope() const { return m_scope; } + void setScope(ASTNode const* _scope) { m_scope = _scope; } virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } @@ -190,7 +191,7 @@ protected: private: ASTPointer<ASTString> m_name; Visibility m_visibility; - Declaration const* m_scope; + ASTNode const* m_scope; }; /** diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 4e0187cf..0bc91c60 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -54,6 +54,12 @@ struct DocumentedAnnotation std::multimap<std::string, DocTag> docTags; }; +struct ImportAnnotation: ASTAnnotation +{ + /// The absolute path of the source unit to import. + std::string absolutePath; +}; + struct TypeDeclarationAnnotation: ASTAnnotation { /// The name of this type, prefixed by proper namespaces if globally accessible. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 18eec0a2..7f097523 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -22,6 +22,7 @@ */ #include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> #include <libsolidity/ast/AST.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> @@ -103,12 +104,14 @@ bool CompilerStack::parse() m_errors.clear(); m_parseSuccessful = false; + map<string, SourceUnit const*> sourceUnitsByName; for (auto& sourcePair: m_sources) { sourcePair.second.scanner->reset(); sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner); if (!sourcePair.second.ast) solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error."); + sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get(); } if (!Error::containsOnlyWarnings(m_errors)) // errors while parsing. sould stop before type checking @@ -129,6 +132,10 @@ bool CompilerStack::parse() return false; for (Source const* source: m_sourceOrder) + if (!resolver.performImports(*source->ast, sourceUnitsByName)) + return false; + + for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { @@ -361,7 +368,7 @@ void CompilerStack::resolveImports() vector<Source const*> sourceOrder; set<Source const*> sourcesSeen; - function<void(Source const*)> toposort = [&](Source const* _source) + function<void(string const&, Source const*)> toposort = [&](string const& _sourceName, Source const* _source) { if (sourcesSeen.count(_source)) return; @@ -369,26 +376,44 @@ void CompilerStack::resolveImports() for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) { - string const& id = import->identifier(); - if (!m_sources.count(id)) + string path = absolutePath(import->identifier(), _sourceName); + import->annotation().absolutePath = path; + if (!m_sources.count(path)) BOOST_THROW_EXCEPTION( Error(Error::Type::ParserError) << errinfo_sourceLocation(import->location()) << errinfo_comment("Source not found.") ); - toposort(&m_sources[id]); + toposort(path, &m_sources[path]); } sourceOrder.push_back(_source); }; for (auto const& sourcePair: m_sources) if (!sourcePair.second.isLibrary) - toposort(&sourcePair.second); + toposort(sourcePair.first, &sourcePair.second); swap(m_sourceOrder, sourceOrder); } +string CompilerStack::absolutePath(string const& _path, string const& _reference) const +{ + // Anything that does not start with `.` is an absolute path. + if (_path.empty() || _path.front() != '.') + return _path; + using path = boost::filesystem::path; + path p(_path); + path result(_reference); + result.remove_filename(); + for (path::iterator it = p.begin(); it != p.end(); ++it) + if (*it == "..") + result = result.parent_path(); + else if (*it != ".") + result /= *it; + return result.string(); +} + void CompilerStack::compileContract( bool _optimize, unsigned _runs, diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 0473d58b..3e6dc456 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -199,6 +199,8 @@ private: }; void resolveImports(); + /// @returns the absolute path corresponding to @a _path relative to @a _reference. + std::string absolutePath(std::string const& _path, std::string const& _reference) const; /// Compile a single contract and put the result in @a _compiledContracts. void compileContract( bool _optimize, diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp new file mode 100644 index 00000000..ab8e2257 --- /dev/null +++ b/test/libsolidity/Imports.cpp @@ -0,0 +1,100 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Tests for high level features like import. + */ + +#include <string> +#include <boost/test/unit_test.hpp> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/CompilerStack.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(SolidityImports) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CompilerStack c; + c.addSource("a", "contract C {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(regular_import) +{ + CompilerStack c; + c.addSource("a", "contract C {}"); + c.addSource("b", "import \"a\"; contract D is C {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(import_does_not_clutter_importee) +{ + CompilerStack c; + c.addSource("a", "contract C { D d; }"); + c.addSource("b", "import \"a\"; contract D is C {}"); + BOOST_CHECK(!c.compile()); +} + +BOOST_AUTO_TEST_CASE(import_is_transitive) +{ + CompilerStack c; + c.addSource("a", "contract C { }"); + c.addSource("b", "import \"a\";"); + c.addSource("c", "import \"b\"; contract D is C {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(circular_import) +{ + CompilerStack c; + c.addSource("a", "import \"b\"; contract C { D d; }"); + c.addSource("b", "import \"a\"; contract D { C c; }"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(relative_import) +{ + CompilerStack c; + c.addSource("a", "import \"./dir/b\"; contract A is B {}"); + c.addSource("dir/b", "contract B {}"); + c.addSource("dir/c", "import \"../a\"; contract C is A {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(relative_import_multiplex) +{ + CompilerStack c; + c.addSource("a", "contract A {}"); + c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index a622c738..0e814e56 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -71,18 +71,21 @@ private: }; Declaration const& resolveDeclaration( - vector<string> const& _namespacedName, NameAndTypeResolver const& _resolver) + SourceUnit const& _sourceUnit, + vector<string> const& _namespacedName, + NameAndTypeResolver const& _resolver +) { - Declaration const* declaration = nullptr; + ASTNode const* scope = &_sourceUnit; // bracers are required, cause msvc couldnt handle this macro in for statement for (string const& namePart: _namespacedName) { - auto declarations = _resolver.resolveName(namePart, declaration); + auto declarations = _resolver.resolveName(namePart, scope); BOOST_REQUIRE(!declarations.empty()); - BOOST_REQUIRE(declaration = *declarations.begin()); + BOOST_REQUIRE(scope = *declarations.begin()); } - BOOST_REQUIRE(declaration); - return *declaration; + BOOST_REQUIRE(scope); + return dynamic_cast<Declaration const&>(*scope); } bytes compileFirstExpression( @@ -140,13 +143,17 @@ bytes compileFirstExpression( unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack context.adjustStackOffset(parametersSize); for (vector<string> const& variable: _localVariables) - context.addVariable(dynamic_cast<VariableDeclaration const&>(resolveDeclaration(variable, resolver)), - parametersSize--); + context.addVariable( + dynamic_cast<VariableDeclaration const&>(resolveDeclaration(*sourceUnit, variable, resolver)), + parametersSize-- + ); ExpressionCompiler(context).compile(*extractor.expression()); for (vector<string> const& function: _functions) - context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(resolveDeclaration(function, resolver))); + context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>( + resolveDeclaration(*sourceUnit, function, resolver) + )); bytes instructions = context.assembledObject().bytecode; // debug // cout << eth::disassemble(instructions) << endl; |