From d997dc55d154c93af01175d880734b9e737d34ca Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 15:10:35 +0200 Subject: [PATCH 1/7] 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 --- AST.cpp | 14 +++++++------- AST.h | 2 +- Compiler.cpp | 1 + CompilerStack.cpp | 16 +++++++++++----- CompilerStack.h | 4 ++-- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/AST.cpp b/AST.cpp index 0abdf7819..370166462 100644 --- a/AST.cpp +++ b/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/AST.h b/AST.h index 9f5f9fcb1..8c36caeaf 100644 --- a/AST.h +++ b/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/Compiler.cpp b/Compiler.cpp index 886565cb4..f0197e23b 100644 --- a/Compiler.cpp +++ b/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/CompilerStack.cpp b/CompilerStack.cpp index 1301bfa5d..c35d9324c 100644 --- a/CompilerStack.cpp +++ b/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/CompilerStack.h b/CompilerStack.h index 1cf576ab4..19c1ba4e1 100644 --- a/CompilerStack.h +++ b/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; From beba2705ba2fd9f87bf76f439c4a99cec0f6a06d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 16:56:12 +0200 Subject: [PATCH 2/7] Check all constructors in inheritance chain get args - Also add a missing override in a function of EnumValue --- AST.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ AST.h | 3 ++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index 370166462..4d183889c 100644 --- a/AST.cpp +++ b/AST.cpp @@ -54,6 +54,7 @@ void ContractDefinition::checkTypeRequirements() checkIllegalOverrides(); checkAbstractFunctions(); + checkAbstractConstructors(); FunctionDefinition const* constructor = getConstructor(); if (constructor && !constructor->getReturnParameters().empty()) @@ -152,6 +153,47 @@ void ContractDefinition::checkAbstractFunctions() } } +void ContractDefinition::checkAbstractConstructors() +{ + set argumentsNeeded; + set argumentsProvided; + // check that we get arguments for all base constructors that need it. + // If not mark the contract as abstract (not fully implemented) + vector const& bases = getLinearizedBaseContracts(); + for (ContractDefinition const* contract: bases) + { + FunctionDefinition const* constructor = contract->getConstructor(); + if (constructor) + { + if (!constructor->getParameters().empty()) + argumentsProvided.insert(constructor); + for (auto const& modifier: constructor->getModifiers()) + { + auto baseContract = dynamic_cast( + modifier->getName()->getReferencedDeclaration()); + if (baseContract) + { + FunctionDefinition const* baseConstructor = baseContract->getConstructor(); + if (argumentsProvided.count(baseConstructor) == 1) + argumentsNeeded.insert(baseConstructor); + } + } + } + + for (ASTPointer const& base: contract->getBaseContracts()) + { + ContractDefinition const* baseContract = dynamic_cast( + base->getName()->getReferencedDeclaration()); + solAssert(baseContract, ""); + FunctionDefinition const* baseConstructor = baseContract->getConstructor(); + if (argumentsProvided.count(baseConstructor) == 1) + argumentsNeeded.insert(baseConstructor); + } + } + if (argumentsProvided != argumentsNeeded) + setFullyImplemented(false); +} + void ContractDefinition::checkIllegalOverrides() const { // TODO unify this at a later point. for this we need to put the constness and the access specifier diff --git a/AST.h b/AST.h index 8c36caeaf..99abf2295 100644 --- a/AST.h +++ b/AST.h @@ -284,6 +284,7 @@ public: private: void checkIllegalOverrides() const; void checkAbstractFunctions(); + void checkAbstractConstructors(); std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; @@ -376,7 +377,7 @@ class EnumValue: public Declaration virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - TypePointer getType(ContractDefinition const* = nullptr) const; + TypePointer getType(ContractDefinition const* = nullptr) const override; }; /** From 52a442a100ef065ec36cc9e20f62f6d878347e6d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 17:05:27 +0200 Subject: [PATCH 3/7] size()!=0 -> !empty() --- AST.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index 4d183889c..a66aa9897 100644 --- a/AST.cpp +++ b/AST.cpp @@ -323,7 +323,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 (m_arguments.size() != 0 && parameterTypes.size() != m_arguments.size()) + if (!m_arguments.empty() && 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])) From 0c296cd8093af05507426f9a5a5b25bb2b51f4fb Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 17:12:15 +0200 Subject: [PATCH 4/7] Fixing new abstract contract error location reporting --- AST.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index a66aa9897..7f4b8710e 100644 --- a/AST.cpp +++ b/AST.cpp @@ -728,7 +728,7 @@ void NewExpression::checkTypeRequirements() 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 instance of an abstract contract.")); + BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract.")); shared_ptr contractType = make_shared(*m_contract); TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes(); m_type = make_shared(parameterTypes, TypePointers{contractType}, From eb84c3171aa362c507c7213c937748b9e9f78ca9 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 17:42:43 +0200 Subject: [PATCH 5/7] Add "this" contract's ctor to provided ctors set - Also properly naming the 2 sets in checkAbstractConstructors() function --- AST.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AST.cpp b/AST.cpp index 7f4b8710e..1c12ef5a1 100644 --- a/AST.cpp +++ b/AST.cpp @@ -166,7 +166,7 @@ void ContractDefinition::checkAbstractConstructors() if (constructor) { if (!constructor->getParameters().empty()) - argumentsProvided.insert(constructor); + argumentsNeeded.insert(constructor); for (auto const& modifier: constructor->getModifiers()) { auto baseContract = dynamic_cast( @@ -174,8 +174,8 @@ void ContractDefinition::checkAbstractConstructors() if (baseContract) { FunctionDefinition const* baseConstructor = baseContract->getConstructor(); - if (argumentsProvided.count(baseConstructor) == 1) - argumentsNeeded.insert(baseConstructor); + if (argumentsNeeded.count(baseConstructor) == 1) + argumentsProvided.insert(baseConstructor); } } } @@ -186,10 +186,13 @@ void ContractDefinition::checkAbstractConstructors() base->getName()->getReferencedDeclaration()); solAssert(baseContract, ""); FunctionDefinition const* baseConstructor = baseContract->getConstructor(); - if (argumentsProvided.count(baseConstructor) == 1) - argumentsNeeded.insert(baseConstructor); + if (argumentsNeeded.count(baseConstructor) == 1) + argumentsProvided.insert(baseConstructor); } } + // add this contract's constructor to the provided too + if (getConstructor() && !getConstructor()->getParameters().empty()) + argumentsProvided.insert(getConstructor()); if (argumentsProvided != argumentsNeeded) setFullyImplemented(false); } From 1a19d436db535b2ea1325b7c00295befdb8be216 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 2 Apr 2015 17:50:43 +0200 Subject: [PATCH 6/7] Adding forgotten virtual specifier to EnumValue getType() --- AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AST.h b/AST.h index 99abf2295..0c133ff1a 100644 --- a/AST.h +++ b/AST.h @@ -377,7 +377,7 @@ class EnumValue: public Declaration virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - TypePointer getType(ContractDefinition const* = nullptr) const override; + virtual TypePointer getType(ContractDefinition const* = nullptr) const override; }; /** From c558aa06567f4e6e28b8f15c3dee73867fde447b Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 17 Apr 2015 16:17:21 +0200 Subject: [PATCH 7/7] Fixing detection of abstract contract --- AST.cpp | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/AST.cpp b/AST.cpp index 1c12ef5a1..5f681205d 100644 --- a/AST.cpp +++ b/AST.cpp @@ -155,45 +155,40 @@ void ContractDefinition::checkAbstractFunctions() void ContractDefinition::checkAbstractConstructors() { - set argumentsNeeded; - set argumentsProvided; + set argumentsNeeded; // check that we get arguments for all base constructors that need it. // If not mark the contract as abstract (not fully implemented) + vector const& bases = getLinearizedBaseContracts(); + for (ContractDefinition const* contract: bases) + if (FunctionDefinition const* constructor = contract->getConstructor()) + if (contract != this && !constructor->getParameters().empty()) + argumentsNeeded.insert(contract); + for (ContractDefinition const* contract: bases) { - FunctionDefinition const* constructor = contract->getConstructor(); - if (constructor) - { - if (!constructor->getParameters().empty()) - argumentsNeeded.insert(constructor); + if (FunctionDefinition const* constructor = contract->getConstructor()) for (auto const& modifier: constructor->getModifiers()) { auto baseContract = dynamic_cast( - modifier->getName()->getReferencedDeclaration()); + modifier->getName()->getReferencedDeclaration() + ); if (baseContract) - { - FunctionDefinition const* baseConstructor = baseContract->getConstructor(); - if (argumentsNeeded.count(baseConstructor) == 1) - argumentsProvided.insert(baseConstructor); - } + argumentsNeeded.erase(baseContract); } - } + for (ASTPointer const& base: contract->getBaseContracts()) { - ContractDefinition const* baseContract = dynamic_cast( - base->getName()->getReferencedDeclaration()); + auto baseContract = dynamic_cast( + base->getName()->getReferencedDeclaration() + ); solAssert(baseContract, ""); - FunctionDefinition const* baseConstructor = baseContract->getConstructor(); - if (argumentsNeeded.count(baseConstructor) == 1) - argumentsProvided.insert(baseConstructor); + if (!base->getArguments().empty()) + argumentsNeeded.erase(baseContract); } } - // add this contract's constructor to the provided too - if (getConstructor() && !getConstructor()->getParameters().empty()) - argumentsProvided.insert(getConstructor()); - if (argumentsProvided != argumentsNeeded) + if (!argumentsNeeded.empty()) setFullyImplemented(false); }