Parsing and type checking of libraries without inheritance.

This commit is contained in:
chriseth 2015-09-08 16:48:33 +02:00
parent c5b6d9d2a9
commit 337fde9d11
8 changed files with 85 additions and 13 deletions

View File

@ -103,6 +103,9 @@ void ContractDefinition::checkTypeRequirements()
)); ));
hashes.insert(hash); hashes.insert(hash);
} }
if (isLibrary())
checkLibraryRequirements();
} }
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const 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 vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
{ {
if (!m_interfaceEvents) if (!m_interfaceEvents)
@ -449,6 +463,10 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration()); ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
solAssert(base, "Base contract not available."); solAssert(base, "Base contract not available.");
if (base->isLibrary())
BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from."));
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size()) if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(

View File

@ -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 * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
@ -232,7 +232,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events std::vector<ASTPointer<EventDefinition>> const& _events,
bool _isLibrary
): ):
Declaration(_location, _name), Declaration(_location, _name),
Documented(_documentation), Documented(_documentation),
@ -243,7 +244,8 @@ public:
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions), m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers), m_functionModifiers(_functionModifiers),
m_events(_events) m_events(_events),
m_isLibrary(_isLibrary)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
@ -257,6 +259,7 @@ public:
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; } 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& events() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const; std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
bool isLibrary() const { return m_isLibrary; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; 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 /// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature). /// external argument types (i.e. different signature).
void checkExternalTypeClashes() const; 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; std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
@ -307,6 +312,7 @@ private:
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events; std::vector<ASTPointer<EventDefinition>> m_events;
bool m_isLibrary;
// parsed Natspec documentation of the contract. // parsed Natspec documentation of the contract.
std::string m_userDocumentation; std::string m_userDocumentation;

View File

@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
vector<ASTPointer<ASTNode>> nodes; vector<ASTPointer<ASTNode>> nodes;
while (m_scanner->currentToken() != Token::EOS) while (m_scanner->currentToken() != Token::EOS)
{ {
switch (m_scanner->currentToken()) switch (auto token = m_scanner->currentToken())
{ {
case Token::Import: case Token::Import:
nodes.push_back(parseImportDirective()); nodes.push_back(parseImportDirective());
break; break;
case Token::Contract: case Token::Contract:
nodes.push_back(parseContractDefinition()); case Token::Library:
nodes.push_back(parseContractDefinition(token == Token::Library));
break; break;
default: default:
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition."))); 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); return nodeFactory.createNode<ImportDirective>(url);
} }
ASTPointer<ContractDefinition> Parser::parseContractDefinition() ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString; ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "") if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
expectToken(Token::Contract); expectToken(_isLibrary ? Token::Library : Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts; vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs; vector<ASTPointer<StructDefinition>> structs;
@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
stateVariables, stateVariables,
functions, functions,
modifiers, modifiers,
events events,
_isLibrary
); );
} }

View File

@ -61,15 +61,17 @@ private:
///@{ ///@{
///@name Parsing functions for the AST nodes ///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue(); ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(), ASTPointer<VariableDeclaration> parseVariableDeclaration(
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<ModifierInvocation> parseModifierInvocation();

View File

@ -160,6 +160,7 @@ namespace solidity
K(Internal, "internal", 0) \ K(Internal, "internal", 0) \
K(Import, "import", 0) \ K(Import, "import", 0) \
K(Is, "is", 0) \ K(Is, "is", 0) \
K(Library, "library", 0) \
K(Mapping, "mapping", 0) \ K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \ K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \ K(Modifier, "modifier", 0) \
@ -305,7 +306,7 @@ namespace solidity
/* Identifiers (not keywords or future reserved words). */ \ /* Identifiers (not keywords or future reserved words). */ \
T(Identifier, NULL, 0) \ T(Identifier, NULL, 0) \
\ \
/* Keywords reserved for future. use. */ \ /* Keywords reserved for future use. */ \
K(As, "as", 0) \ K(As, "as", 0) \
K(Case, "case", 0) \ K(Case, "case", 0) \
K(Catch, "catch", 0) \ K(Catch, "catch", 0) \

View File

@ -1,4 +1,4 @@
ContractDefinition = 'contract' Identifier ContractDefinition = ( 'contract' | 'library' ) Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}' '{' ContractPart* '}'
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition

View File

@ -2194,6 +2194,39 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); 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) BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract)
{ {

View File

@ -924,6 +924,16 @@ BOOST_AUTO_TEST_CASE(empty_comment)
BOOST_CHECK_NO_THROW(parseText(text)); 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() BOOST_AUTO_TEST_SUITE_END()
} }