Browse Source

Merge pull request #1414 from LefterisJP/sol_notImplementedFuncs

Interface contracts
cl-refactor
chriseth 10 years ago
parent
commit
077a508b3b
  1. 34
      libsolidity/AST.cpp
  2. 82
      libsolidity/AST.h
  3. 6
      libsolidity/AST_accept.h
  4. 2
      libsolidity/CompilerStack.cpp
  5. 24
      libsolidity/Parser.cpp
  6. 57
      test/SolidityNameAndTypeResolution.cpp
  7. 8
      test/SolidityParser.cpp

34
libsolidity/AST.cpp

@ -21,6 +21,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/Utils.h> #include <libsolidity/Utils.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements()
baseSpecifier->checkTypeRequirements(); baseSpecifier->checkTypeRequirements();
checkIllegalOverrides(); checkIllegalOverrides();
checkAbstractFunctions();
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
@ -60,6 +62,7 @@ void ContractDefinition::checkTypeRequirements()
FunctionDefinition const* fallbackFunction = nullptr; FunctionDefinition const* fallbackFunction = nullptr;
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
{
if (function->getName().empty()) if (function->getName().empty())
{ {
if (fallbackFunction) if (fallbackFunction)
@ -71,6 +74,9 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters.")); BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
} }
} }
if (!function->isFullyImplemented())
setFullyImplemented(false);
}
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers()) for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements(); modifier->checkTypeRequirements();
@ -124,6 +130,28 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const
return nullptr; return nullptr;
} }
void ContractDefinition::checkAbstractFunctions()
{
map<string, bool> functions;
// Search from base to derived
for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts()))
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
string const& name = function->getName();
if (!function->isFullyImplemented() && functions.count(name) && functions[name])
BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract"));
functions[name] = function->isFullyImplemented();
}
for (auto const& it: functions)
if (!it.second)
{
setFullyImplemented(false);
break;
}
}
void ContractDefinition::checkIllegalOverrides() const void ContractDefinition::checkIllegalOverrides() const
{ {
// TODO unify this at a later point. for this we need to put the constness and the access specifier // TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -316,8 +344,8 @@ void FunctionDefinition::checkTypeRequirements()
modifier->checkTypeRequirements(isConstructor() ? modifier->checkTypeRequirements(isConstructor() ?
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() : dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
vector<ASTPointer<InheritanceSpecifier>>()); vector<ASTPointer<InheritanceSpecifier>>());
if (m_body)
m_body->checkTypeRequirements(); m_body->checkTypeRequirements();
} }
string FunctionDefinition::externalSignature() const string FunctionDefinition::externalSignature() const
@ -649,6 +677,8 @@ void NewExpression::checkTypeRequirements()
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract) if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract); shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType}, m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

82
libsolidity/AST.h

@ -196,6 +196,22 @@ protected:
ASTPointer<ASTString> m_documentation; ASTPointer<ASTString> m_documentation;
}; };
/**
* Abstract class that is added to AST nodes that can be marked as not being fully implemented
*/
class ImplementationOptional
{
public:
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
bool isFullyImplemented() const { return m_implemented; }
void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
protected:
bool m_implemented;
};
/// @} /// @}
/** /**
@ -203,20 +219,24 @@ protected:
* 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.
*/ */
class ContractDefinition: public Declaration, public Documented class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
{ {
public: public:
ContractDefinition(SourceLocation const& _location, ContractDefinition(
ASTPointer<ASTString> const& _name, SourceLocation const& _location,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<EventDefinition>> const& _events): std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
Declaration(_location, _name), Documented(_documentation), std::vector<ASTPointer<EventDefinition>> const& _events
):
Declaration(_location, _name),
Documented(_documentation),
ImplementationOptional(true),
m_baseContracts(_baseContracts), m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums), m_definedEnums(_definedEnums),
@ -263,6 +283,7 @@ public:
private: private:
void checkIllegalOverrides() const; void checkIllegalOverrides() const;
void checkAbstractFunctions();
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const; std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
@ -378,24 +399,29 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters; std::vector<ASTPointer<VariableDeclaration>> m_parameters;
}; };
class FunctionDefinition: public Declaration, public VariableScope, public Documented class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional
{ {
public: public:
FunctionDefinition(SourceLocation const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(
Declaration::Visibility _visibility, bool _isConstructor, SourceLocation const& _location,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _name,
ASTPointer<ParameterList> const& _parameters, Declaration::Visibility _visibility, bool _isConstructor,
bool _isDeclaredConst, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, ASTPointer<ParameterList> const& _parameters,
ASTPointer<ParameterList> const& _returnParameters, bool _isDeclaredConst,
ASTPointer<Block> const& _body): std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
Declaration(_location, _name, _visibility), Documented(_documentation), ASTPointer<ParameterList> const& _returnParameters,
m_isConstructor(_isConstructor), ASTPointer<Block> const& _body
m_parameters(_parameters), ):
m_isDeclaredConst(_isDeclaredConst), Declaration(_location, _name, _visibility),
m_functionModifiers(_modifiers), Documented(_documentation),
m_returnParameters(_returnParameters), ImplementationOptional(_body != nullptr),
m_body(_body) m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;

6
libsolidity/AST_accept.h

@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); if (m_body)
m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor); listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor); if (m_body)
m_body->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

2
libsolidity/CompilerStack.cpp

@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
if (!contract->isFullyImplemented())
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, contractBytecode); compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];

24
libsolidity/Parser.cpp

@ -164,8 +164,17 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBrace); expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums, return nodeFactory.createNode<ContractDefinition>(
stateVariables, functions, modifiers, events); name,
docString,
baseContracts,
structs,
enums,
stateVariables,
functions,
modifiers,
events
);
} }
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
@ -247,8 +256,15 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
} }
else else
returnParameters = createEmptyParameterList(); returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = ASTPointer<Block>();
nodeFactory.setEndPositionFromNode(block); nodeFactory.markEndPosition();
if (m_scanner->getCurrentToken() != Token::Semicolon)
{
block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
}
else
m_scanner->next(); // just consume the ';'
bool const c_isConstructor = (_contractName && *name == *_contractName); bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring, return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
parameters, isDeclaredConst, modifiers, parameters, isDeclaredConst, modifiers,

57
test/SolidityNameAndTypeResolution.cpp

@ -359,6 +359,63 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence)
ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed");
} }
BOOST_AUTO_TEST_CASE(function_no_implementation)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get());
BOOST_CHECK(contract);
BOOST_CHECK(!contract->isFullyImplemented());
BOOST_CHECK(!contract->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->getNodes();
ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get());
ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get());
BOOST_CHECK(base);
BOOST_CHECK(!base->isFullyImplemented());
BOOST_CHECK(!base->getDefinedFunctions()[0]->isFullyImplemented());
BOOST_CHECK(derived);
BOOST_CHECK(derived->isFullyImplemented());
BOOST_CHECK(derived->getDefinedFunctions()[0]->isFullyImplemented());
}
BOOST_AUTO_TEST_CASE(create_abstract_contract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived {
base b;
function foo() { b = new base();}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract base { function foo(); }
contract derived is base { function foo() {} }
contract wrong is derived { function foo(); }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(function_canonical_signature) BOOST_AUTO_TEST_CASE(function_canonical_signature)
{ {
ASTPointer<SourceUnit> sourceUnit; ASTPointer<SourceUnit> sourceUnit;

8
test/SolidityParser.cpp

@ -108,6 +108,14 @@ BOOST_AUTO_TEST_CASE(single_function_param)
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed."); ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
} }
BOOST_AUTO_TEST_CASE(function_no_body)
{
char const* text = "contract test {\n"
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_CHECK_NO_THROW(parseText(text), "Parsing failed.");
}
BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args) BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"

Loading…
Cancel
Save