diff options
-rw-r--r-- | libsolidity/AST.cpp | 18 | ||||
-rw-r--r-- | libsolidity/AST.h | 12 | ||||
-rw-r--r-- | libsolidity/Parser.cpp | 12 | ||||
-rw-r--r-- | libsolidity/Parser.h | 8 | ||||
-rw-r--r-- | libsolidity/Token.h | 3 | ||||
-rw-r--r-- | libsolidity/grammar.txt | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 33 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 10 |
8 files changed, 85 insertions, 13 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 1b22c44f..5e51d222 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -103,6 +103,9 @@ void ContractDefinition::checkTypeRequirements() )); hashes.insert(hash); } + + if (isLibrary()) + checkLibraryRequirements(); } map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const @@ -332,6 +335,17 @@ void ContractDefinition::checkExternalTypeClashes() const )); } +void ContractDefinition::checkLibraryRequirements() const +{ + solAssert(m_isLibrary, ""); + if (!m_baseContracts.empty()) + BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit.")); + + for (auto const& var: m_stateVariables) + if (!var->isConstant()) + BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables")); +} + vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const { if (!m_interfaceEvents) @@ -449,6 +463,10 @@ void InheritanceSpecifier::checkTypeRequirements() ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration()); solAssert(base, "Base contract not available."); + + if (base->isLibrary()) + BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from.")); + TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError( diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 48790e6a..da6a7d58 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -215,7 +215,7 @@ protected: /// @} /** - * Definition of a contract. This is the only AST nodes where child nodes are not visited in + * Definition of a contract or library. This is the only AST nodes where child nodes are not visited in * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ @@ -232,7 +232,8 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, - std::vector<ASTPointer<EventDefinition>> const& _events + std::vector<ASTPointer<EventDefinition>> const& _events, + bool _isLibrary ): Declaration(_location, _name), Documented(_documentation), @@ -243,7 +244,8 @@ public: m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), m_functionModifiers(_functionModifiers), - m_events(_events) + m_events(_events), + m_isLibrary(_isLibrary) {} virtual void accept(ASTVisitor& _visitor) override; @@ -257,6 +259,7 @@ public: std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; } std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const; + bool isLibrary() const { return m_isLibrary; } virtual TypePointer type(ContractDefinition const* m_currentContract) const override; @@ -297,6 +300,8 @@ private: /// Checks that different functions with external visibility end up having different /// external argument types (i.e. different signature). void checkExternalTypeClashes() const; + /// Checks that all requirements for a library are fulfilled if this is a library. + void checkLibraryRequirements() const; std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const; @@ -307,6 +312,7 @@ private: std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; std::vector<ASTPointer<EventDefinition>> m_events; + bool m_isLibrary; // parsed Natspec documentation of the contract. std::string m_userDocumentation; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 7f779ed0..24f7734c 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) vector<ASTPointer<ASTNode>> nodes; while (m_scanner->currentToken() != Token::EOS) { - switch (m_scanner->currentToken()) + switch (auto token = m_scanner->currentToken()) { case Token::Import: nodes.push_back(parseImportDirective()); break; case Token::Contract: - nodes.push_back(parseContractDefinition()); + case Token::Library: + nodes.push_back(parseContractDefinition(token == Token::Library)); break; default: BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition."))); @@ -113,13 +114,13 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() return nodeFactory.createNode<ImportDirective>(url); } -ASTPointer<ContractDefinition> Parser::parseContractDefinition() +ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary) { ASTNodeFactory nodeFactory(*this); ASTPointer<ASTString> docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - expectToken(Token::Contract); + expectToken(_isLibrary ? Token::Library : Token::Contract); ASTPointer<ASTString> name = expectIdentifierToken(); vector<ASTPointer<InheritanceSpecifier>> baseContracts; vector<ASTPointer<StructDefinition>> structs; @@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() stateVariables, functions, modifiers, - events + events, + _isLibrary ); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 36302f74..79eb73f0 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -61,15 +61,17 @@ private: ///@{ ///@name Parsing functions for the AST nodes ASTPointer<ImportDirective> parseImportDirective(); - ASTPointer<ContractDefinition> parseContractDefinition(); + ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumValue> parseEnumValue(); - ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(), - ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); + ASTPointer<VariableDeclaration> parseVariableDeclaration( + VarDeclParserOptions const& _options = VarDeclParserOptions(), + ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>() + ); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<ModifierInvocation> parseModifierInvocation(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index bc242ecf..1632a693 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -160,6 +160,7 @@ namespace solidity K(Internal, "internal", 0) \ K(Import, "import", 0) \ K(Is, "is", 0) \ + K(Library, "library", 0) \ K(Mapping, "mapping", 0) \ K(Memory, "memory", 0) \ K(Modifier, "modifier", 0) \ @@ -305,7 +306,7 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, NULL, 0) \ \ - /* Keywords reserved for future. use. */ \ + /* Keywords reserved for future use. */ \ K(As, "as", 0) \ K(Case, "case", 0) \ K(Catch, "catch", 0) \ diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 6503516c..467aebee 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,4 +1,4 @@ -ContractDefinition = 'contract' Identifier +ContractDefinition = ( 'contract' | 'library' ) Identifier ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? '{' ContractPart* '}' ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 883d7807..adff9499 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2194,6 +2194,39 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion) BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } +BOOST_AUTO_TEST_CASE(inheriting_from_library) +{ + char const* text = R"( + library Lib {} + contract Test is Lib {} + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(inheriting_library) +{ + char const* text = R"( + contract Test {} + library Lib is Test {} + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(library_having_variables) +{ + char const* text = R"( + library Lib { uint x; } + )"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(valid_library) +{ + char const* text = R"( + library Lib { uint constant x = 9; } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 14b9e9e4..1e034863 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -924,6 +924,16 @@ BOOST_AUTO_TEST_CASE(empty_comment) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(library_simple) +{ + char const* text = R"( + library Lib { + function f() { } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |