Browse Source

Abstract contract and inheritance

- Checking the linearized base contracts for abstract functions and
  handle their existence appropriately

- If a contract is abstract it can't be created with new

- An abstract contract is not compiled (no backend code is generated)

- Of course tests
cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
dfcaf8a414
  1. 40
      libsolidity/AST.cpp
  2. 1
      libsolidity/AST.h
  3. 2
      libsolidity/CompilerStack.cpp
  4. 5
      libsolidity/Parser.cpp
  5. 48
      test/SolidityNameAndTypeResolution.cpp

40
libsolidity/AST.cpp

@ -21,6 +21,7 @@
*/
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements()
baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
checkAbstractFunctions();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
@ -128,6 +130,42 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const
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"));
// if (functions.count(name) && !functions[name] && function->isFullyImplemented())
// functions.insert(make_pair(name, true);
// if (functions.count(name) && !functions[name] && function->isFullyImplemented)
// functions.insert(make_pair(name, true));
// functions.insert(make_pair(name, function->isFullyImplemented()));
functions[name] = function->isFullyImplemented();
// if (function->isFullyImplemented())
// full_functions.insert(make_pair(name, function.get()));
// else
// abs_functions.insert(make_pair(name, function.get()));
}
}
for (auto const& it: functions)
if (!it.second)
{
setFullyImplemented(false);
break;
}
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
@ -643,6 +681,8 @@ void NewExpression::checkTypeRequirements()
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
if (!m_contract->isFullyImplemented())
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an object of an abstract contract."));
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},

1
libsolidity/AST.h

@ -283,6 +283,7 @@ public:
private:
void checkIllegalOverrides() const;
void checkAbstractFunctions();
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;

2
libsolidity/CompilerStack.cpp

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

5
libsolidity/Parser.cpp

@ -141,10 +141,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
if (currentToken == Token::RBrace)
break;
else if (currentToken == Token::Function)
{
ASTPointer<FunctionDefinition> func = parseFunctionDefinition(name.get());
functions.push_back(func);
}
functions.push_back(parseFunctionDefinition(name.get()));
else if (currentToken == Token::Struct)
structs.push_back(parseStructDefinition());
else if (currentToken == Token::Enum)

48
test/SolidityNameAndTypeResolution.cpp

@ -353,14 +353,56 @@ BOOST_AUTO_TEST_CASE(function_no_implementation)
" function functionName(bytes32 input) returns (bytes32 out);\n"
"}\n";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed");
ContractDefinition* contract;
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
contract = dynamic_cast<ContractDefinition*>(node.get());
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)
{
ASTPointer<SourceUnit> sourceUnit;

Loading…
Cancel
Save