diff --git a/AST.cpp b/AST.cpp index acf399349..78b83d064 100644 --- a/AST.cpp +++ b/AST.cpp @@ -55,6 +55,7 @@ void ContractDefinition::checkTypeRequirements() checkDuplicateFunctions(); checkIllegalOverrides(); checkAbstractFunctions(); + checkAbstractConstructors(); FunctionDefinition const* constructor = getConstructor(); if (constructor && !constructor->getReturnParameters().empty()) @@ -181,6 +182,45 @@ void ContractDefinition::checkAbstractFunctions() } } +void ContractDefinition::checkAbstractConstructors() +{ + 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) + { + if (FunctionDefinition const* constructor = contract->getConstructor()) + for (auto const& modifier: constructor->getModifiers()) + { + auto baseContract = dynamic_cast( + &modifier->getName()->getReferencedDeclaration() + ); + if (baseContract) + argumentsNeeded.erase(baseContract); + } + + + for (ASTPointer const& base: contract->getBaseContracts()) + { + auto baseContract = dynamic_cast( + &base->getName()->getReferencedDeclaration() + ); + solAssert(baseContract, ""); + if (!base->getArguments().empty()) + argumentsNeeded.erase(baseContract); + } + } + if (!argumentsNeeded.empty()) + 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 @@ -354,7 +394,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.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])) @@ -421,8 +461,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(); } @@ -500,7 +540,7 @@ void ModifierDefinition::checkTypeRequirements() m_body->checkTypeRequirements(); } -void ModifierInvocation::checkTypeRequirements(vector> const& _bases) +void ModifierInvocation::checkTypeRequirements(vector const& _bases) { TypePointers argumentTypes; for (ASTPointer const& argument: m_arguments) @@ -517,10 +557,10 @@ void ModifierInvocation::checkTypeRequirements(vectorgetParameters(); else // check parameters for Base constructors - for (auto const& base: _bases) - if (declaration == &base->getName()->getReferencedDeclaration()) + for (ContractDefinition const* base: _bases) + if (declaration == base) { - if (auto referencedConstructor = dynamic_cast(*declaration).getConstructor()) + if (auto referencedConstructor = base->getConstructor()) parameters = &referencedConstructor->getParameters(); else parameters = &emptyParameterList; @@ -777,7 +817,7 @@ void NewExpression::checkTypeRequirements(TypePointers const*) 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}, diff --git a/AST.h b/AST.h index f0144c7ec..f5f36f6fe 100644 --- a/AST.h +++ b/AST.h @@ -287,6 +287,7 @@ private: void checkDuplicateFunctions() const; void checkIllegalOverrides() const; void checkAbstractFunctions(); + void checkAbstractConstructors(); /// Checks that different functions with external visibility end up having different /// external argument types (i.e. different signature). void checkExternalTypeClashes() const; @@ -382,7 +383,7 @@ class EnumValue: public Declaration virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - TypePointer getType(ContractDefinition const* = nullptr) const; + virtual TypePointer getType(ContractDefinition const* = nullptr) const override; }; /** @@ -572,7 +573,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 83954922e..37b577ccd 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/Compiler.h b/Compiler.h index 4b1e1b4d6..d476ec684 100644 --- a/Compiler.h +++ b/Compiler.h @@ -42,9 +42,10 @@ public: bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const + /// @arg _inJsonFromat shows whether the out should be in Json format + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const { - m_context.streamAssembly(_stream, _sourceCodes); + m_context.streamAssembly(_stream, _sourceCodes, _inJsonFormat); } /// @returns Assembly items of the normal compiler context eth::AssemblyItems const& getAssemblyItems() const { return m_context.getAssembly().getItems(); } diff --git a/CompilerContext.h b/CompilerContext.h index 53d0a89a4..34a3f97cd 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -122,7 +122,11 @@ public: eth::Assembly const& getAssembly() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings - void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); } + /// @arg _inJsonFormat shows whether the out should be in Json format + void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const + { + m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat); + } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 1301bfa5d..d6274e2c7 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 @@ -182,9 +184,13 @@ dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const return dev::sha3(getRuntimeBytecode(_contractName)); } -void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes) const +void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const { - getContract(_contractName).compiler->streamAssembly(_outStream, _sourceCodes); + Contract const& contract = getContract(_contractName); + if (contract.compiler) + contract.compiler->streamAssembly(_outStream, _sourceCodes, _inJsonFormat); + 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..2e7c217d5 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -94,16 +94,17 @@ 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; /// Streams a verbose version of the assembly to @a _outStream. /// @arg _sourceCodes is the map of input files to source code strings + /// @arg _inJsonFromat shows whether the out should be in Json format /// Prerequisite: Successful compilation. - void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "", StringMap _sourceCodes = StringMap(), bool _inJsonFormat = false) const; /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile.