diff options
author | chriseth <c@ethdev.com> | 2015-12-05 10:09:47 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-12-10 02:09:24 +0800 |
commit | 7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879 (patch) | |
tree | d6ac5e5fe041afc29dcea2aafa97d60dca82a991 | |
parent | e510e7e7929326de3a556d6d2e66b8b4376af7a9 (diff) | |
download | dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar.gz dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar.bz2 dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar.lz dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar.xz dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.tar.zst dexon-solidity-7cb7818ceaf8f2e50cdd66b33a4e0d17c2a0e879.zip |
Source units are independent scopes.
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.cpp | 38 | ||||
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.h | 13 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 59 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 19 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 10 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 6 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 6 | ||||
-rw-r--r-- | test/libsolidity/Imports.cpp | 83 | ||||
-rw-r--r-- | test/libsolidity/SolidityExpressionCompiler.cpp | 25 |
9 files changed, 204 insertions, 55 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 fb3e9305..907d703c 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -38,13 +38,18 @@ NameAndTypeResolver::NameAndTypeResolver( ) : m_errors(_errors) { - m_scopes[nullptr].reset(new DeclarationContainer()); + if (!m_scopes[nullptr]) + m_scopes[nullptr].reset(new DeclarationContainer()); for (Declaration const* declaration: _globals) 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 { @@ -59,11 +64,37 @@ 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())) + { + if (!_sourceUnits.count(imp->identifier())) + { + reportDeclarationError(node->location(), "Import \"" + imp->identifier() + "\" not found."); + error = true; + } + else + { + auto scope = m_scopes.find(_sourceUnits.at(imp->identifier())); + 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].get(); + m_currentScope = m_scopes[_contract.scope()].get(); + solAssert(!!m_currentScope, ""); ReferencesResolver resolver(m_errors, *this, nullptr); bool success = true; @@ -134,7 +165,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&) @@ -146,7 +177,7 @@ 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)) @@ -348,9 +379,10 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper( ErrorList& _errors ): m_scopes(_scopes), - m_currentScope(nullptr), + m_currentScope(&_astRoot), m_errors(_errors) { + solAssert(!!m_scopes.at(m_currentScope), ""); _astRoot.accept(*this); } @@ -462,12 +494,12 @@ 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; @@ -502,14 +534,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 805a5596..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*, std::unique_ptr<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; }; @@ -164,7 +167,7 @@ private: void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); std::map<ASTNode const*, std::unique_ptr<DeclarationContainer>>& m_scopes; - Declaration const* m_currentScope = nullptr; + 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.h b/libsolidity/ast/AST.h index 1217d945..446088ad 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -172,8 +172,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 +190,7 @@ protected: private: ASTPointer<ASTString> m_name; Visibility m_visibility; - Declaration const* m_scope; + ASTNode const* m_scope; }; /** diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 18eec0a2..38fe5956 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -103,12 +103,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 +131,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())) { diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp new file mode 100644 index 00000000..b5ff0c54 --- /dev/null +++ b/test/libsolidity/Imports.cpp @@ -0,0 +1,83 @@ +/* + 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_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; |