From 45117f53b064d4c04ce0e4eda490daee4891d62c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 15:10:35 +0200 Subject: [PATCH] Allowing abstract contracts constructor to have no args - If a constructor is part of an abstract contract we can omit its arguments - IF a contract is abstract make sure to not create and/or request Assembly code about it since it's not compiled --- libsolidity/AST.cpp | 14 +++++++------- libsolidity/AST.h | 2 +- libsolidity/Compiler.cpp | 1 + libsolidity/CompilerStack.cpp | 16 +++++++++++----- libsolidity/CompilerStack.h | 4 ++-- mix/CodeModel.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 18 ++++++++++++++++-- 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 0abdf7819..370166462 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -281,7 +281,7 @@ void InheritanceSpecifier::checkTypeRequirements() ContractDefinition const* base = dynamic_cast(m_baseName->getReferencedDeclaration()); solAssert(base, "Base contract not available."); TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes(); - if (parameterTypes.size() != m_arguments.size()) + if (m_arguments.size() != 0 && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call.")); for (size_t i = 0; i < m_arguments.size(); ++i) if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) @@ -348,8 +348,8 @@ void FunctionDefinition::checkTypeRequirements() } for (ASTPointer const& modifier: m_functionModifiers) modifier->checkTypeRequirements(isConstructor() ? - dynamic_cast(*getScope()).getBaseContracts() : - vector>()); + dynamic_cast(*getScope()).getLinearizedBaseContracts() : + vector()); if (m_body) m_body->checkTypeRequirements(); } @@ -426,7 +426,7 @@ void ModifierDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -void ModifierInvocation::checkTypeRequirements(vector> const& _bases) +void ModifierInvocation::checkTypeRequirements(vector const& _bases) { m_modifierName->checkTypeRequirements(); for (ASTPointer const& argument: m_arguments) @@ -439,10 +439,10 @@ void ModifierInvocation::checkTypeRequirements(vectorgetParameters(); else // check parameters for Base constructors - for (auto const& base: _bases) - if (declaration == base->getName()->getReferencedDeclaration()) + for (auto const* base: _bases) + if (declaration == base) { - if (auto referencedConstructor = dynamic_cast(*declaration).getConstructor()) + if (auto referencedConstructor = base->getConstructor()) parameters = &referencedConstructor->getParameters(); else parameters = &emptyParameterList; diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 9f5f9fcb1..8c36caeaf 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -566,7 +566,7 @@ public: std::vector> const& getArguments() const { return m_arguments; } /// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed. - void checkTypeRequirements(std::vector> const& _bases); + void checkTypeRequirements(std::vector const& _bases); private: ASTPointer m_modifierName; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 886565cb4..f0197e23b 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -136,6 +136,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor) FunctionType constructorType(_constructor); if (!constructorType.getParameterTypes().empty()) { + solAssert(m_baseArguments.count(&_constructor), ""); std::vector> const* arguments = m_baseArguments[&_constructor]; solAssert(arguments, ""); for (unsigned i = 0; i < arguments->size(); ++i) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 1301bfa5d..c35d9324c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -157,14 +157,16 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize) return getBytecode(); } -eth::AssemblyItems const& CompilerStack::getAssemblyItems(string const& _contractName) const +eth::AssemblyItems const* CompilerStack::getAssemblyItems(string const& _contractName) const { - return getContract(_contractName).compiler->getAssemblyItems(); + Contract const& contract = getContract(_contractName); + return contract.compiler ? &getContract(_contractName).compiler->getAssemblyItems() : nullptr; } -eth::AssemblyItems const& CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const +eth::AssemblyItems const* CompilerStack::getRuntimeAssemblyItems(string const& _contractName) const { - return getContract(_contractName).compiler->getRuntimeAssemblyItems(); + Contract const& contract = getContract(_contractName); + return contract.compiler ? &getContract(_contractName).compiler->getRuntimeAssemblyItems() : nullptr; } bytes const& CompilerStack::getBytecode(string const& _contractName) const @@ -184,7 +186,11 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const { - getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes); + Contract const& contract = getContract(_contractName); + if (contract.compiler) + getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes); + else + _outStream << "Contract not fully implemented" << endl; } string const& CompilerStack::getInterface(string const& _contractName) const diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 1cf576ab4..19c1ba4e1 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -94,9 +94,9 @@ public: /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor. bytes const& getRuntimeBytecode(std::string const& _contractName = "") const; /// @returns normal contract assembly items - eth::AssemblyItems const& getAssemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* getAssemblyItems(std::string const& _contractName = "") const; /// @returns runtime contract assembly items - eth::AssemblyItems const& getRuntimeAssemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* getRuntimeAssemblyItems(std::string const& _contractName = "") const; /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor. dev::h256 getContractCodeHash(std::string const& _contractName = "") const; diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 87aa5a2f8..63e8cc1c3 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -135,7 +135,7 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler CollectDeclarationsVisitor visitor(&m_functions, &m_locals); m_storage = collectStorage(contractDefinition); contractDefinition.accept(visitor); - m_assemblyItems = _compiler.getRuntimeAssemblyItems(name); + m_assemblyItems = *_compiler.getRuntimeAssemblyItems(name); m_constructorAssemblyItems = _compiler.getAssemblyItems(name); } diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index ffa78ed9e..caca8b04e 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -407,6 +407,20 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); } +BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional) +{ + ASTPointer sourceUnit; + char const* text = R"( + contract BaseBase { function BaseBase(uint j); } + contract base is BaseBase { function foo(); } + contract derived is base { + function derived(uint i) BaseBase(i){} + function foo() {} + } + )"; + ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Parsing and name resolving failed"); +} + BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) { ASTPointer sourceUnit; @@ -665,7 +679,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) contract A { function A(uint a) { } } contract B is A { } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) @@ -674,7 +688,7 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) contract A { function A(uint a) { } } contract B is A { } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)