From 282d4b8add13fd81a0320fa0d3db331e09b3d487 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 28 Jan 2015 18:19:01 +0100 Subject: [PATCH 01/25] Fix stack size of typetypes. --- Types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Types.h b/Types.h index 83436efaf..b7f87d760 100644 --- a/Types.h +++ b/Types.h @@ -452,6 +452,7 @@ public: virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned getSizeOnStack() const override { return 0; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual MemberList const& getMembers() const override; @@ -477,6 +478,7 @@ public: virtual bool canBeStored() const override { return false; } virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned getSizeOnStack() const override { return 0; } virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; From 97c31b3e7d6a0bc4b024b15bc13d19b851b68ead Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 21 Jan 2015 17:19:57 +0100 Subject: [PATCH 02/25] Parsing accessor functions for public contract state variables - During the contract parsing depending on whether or not a state variable is public an extra acessor FunctionDefinition is parsed for it --- Parser.cpp | 25 +++++++++++++++++++++++++ Parser.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/Parser.cpp b/Parser.cpp index d99d33acc..cd395c77a 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -109,6 +109,29 @@ ASTPointer Parser::parseImportDirective() return nodeFactory.createNode(url); } +void Parser::addStateVariableAccessor(ASTPointer const& _varDecl, + vector> & _functions) +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer emptyDoc; + ASTPointer emptyParamList; + nodeFactory.markEndPosition(); + auto expression = nodeFactory.createNode(make_shared(_varDecl->getName())); + vector> block_statements = {nodeFactory.createNode(expression)}; + + _functions.push_back(nodeFactory.createNode( + make_shared(_varDecl->getName()), + true, // isPublic + false, // not a Constructor + emptyDoc, // no documentation + emptyParamList, // no parameters (temporary, a mapping would need parameters for example) + true, // is constant + emptyParamList, // no return parameters + nodeFactory.createNode(block_statements) + ) + ); +} + ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); @@ -151,6 +174,8 @@ ASTPointer Parser::parseContractDefinition() { bool const allowVar = false; stateVariables.push_back(parseVariableDeclaration(allowVar)); + if (visibilityIsPublic) + addStateVariableAccessor(stateVariables.back(), functions); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) diff --git a/Parser.h b/Parser.h index 211e952d3..d911598b6 100644 --- a/Parser.h +++ b/Parser.h @@ -78,6 +78,10 @@ private: ///@{ ///@name Helper functions + /// Depending on whether a state Variable is Public, appends an accessor to the contract's functions + void addStateVariableAccessor(ASTPointer const& _varDecl, + std::vector> & _functions); + /// Peeks ahead in the scanner to determine if a variable definition is going to follow bool peekVariableDefinition(); From 9759eec2da1d75c70b8cd3eff3fe1bffade6854d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 21 Jan 2015 19:07:03 +0100 Subject: [PATCH 03/25] Fix in addStateVariableAccessor and adjustment of parser tests --- Parser.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Parser.cpp b/Parser.cpp index cd395c77a..c9ab551e4 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -113,9 +113,10 @@ void Parser::addStateVariableAccessor(ASTPointer const& _va vector> & _functions) { ASTNodeFactory nodeFactory(*this); + nodeFactory.setLocationEmpty(); ASTPointer emptyDoc; - ASTPointer emptyParamList; - nodeFactory.markEndPosition(); + + vector> parameters; auto expression = nodeFactory.createNode(make_shared(_varDecl->getName())); vector> block_statements = {nodeFactory.createNode(expression)}; @@ -124,9 +125,9 @@ void Parser::addStateVariableAccessor(ASTPointer const& _va true, // isPublic false, // not a Constructor emptyDoc, // no documentation - emptyParamList, // no parameters (temporary, a mapping would need parameters for example) + nodeFactory.createNode(vector>()), true, // is constant - emptyParamList, // no return parameters + nodeFactory.createNode(vector>()), nodeFactory.createNode(block_statements) ) ); From 3cc04923015cc3f40ad285fba5ed71464bd9ff2a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 22 Jan 2015 17:40:22 +0100 Subject: [PATCH 04/25] Work in progress for state variable accessors - Changed the code so that a generic declaration with the combination of a function type can be used wherer a function definition was used before - Since using an std::pair everywhere is really tiring with this commit I am in the process of abstracting it into a function --- AST.cpp | 48 ++++++++++++++++++++++++++++++++++----------- AST.h | 35 ++++++++++++++++++++++++++------- Compiler.cpp | 46 +++++++++++++++++++++---------------------- Compiler.h | 8 ++++---- CompilerContext.cpp | 4 ++-- CompilerContext.h | 9 ++++++--- Parser.cpp | 32 +++--------------------------- Parser.h | 6 +----- Types.cpp | 21 ++++++++++++++++---- Types.h | 3 ++- 10 files changed, 122 insertions(+), 90 deletions(-) diff --git a/AST.cpp b/AST.cpp index e5967caa5..7620eeece 100644 --- a/AST.cpp +++ b/AST.cpp @@ -68,19 +68,21 @@ void ContractDefinition::checkTypeRequirements() set> hashes; for (auto const& hashAndFunction: getInterfaceFunctionList()) { - FixedHash<4> const& hash = hashAndFunction.first; + FixedHash<4> const& hash = std::get<0>(hashAndFunction); if (hashes.count(hash)) - BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " + - hashAndFunction.second->getCanonicalSignature())); + BOOST_THROW_EXCEPTION(createTypeError( + "Function signature hash collision for " + + std::get<1>(hashAndFunction)>->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); hashes.insert(hash); } } -map, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const +map, pair> ContractDefinition::getInterfaceFunctions() const { - vector, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList(); - map, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(), - exportedFunctionList.end()); + vector, FunctionType const*, Declaration const*>>> exportedFunctionList = getInterfaceFunctionList(); + map, pair> exportedFunctions(exportedFunctionList.begin(), + exportedFunctionList.end()); + solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -134,20 +136,31 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector, FunctionType const*, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, FunctionDefinition const*>>()); + m_interfaceFunctionList.reset(new vector, FunctionType const*, Declaration const*>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) + { for (ASTPointer const& f: contract->getDefinedFunctions()) if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0) { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_pair(hash, f.get())); + m_interfaceFunctionList->push_back(make_tuple(hash, FunctionType(*f), f.get())); } + + for (ASTPointer const& v: contract->getStateVariables()) + if (v->isPublic()) + { + FunctionType ftype(*v); + functionsSeen.insert(v->getName()); + FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())); + m_interfaceFunctionList->push_back(make_tuple(hash, ftype, v.get())); + } + } } return *m_interfaceFunctionList; } @@ -219,7 +232,7 @@ void FunctionDefinition::checkTypeRequirements() string FunctionDefinition::getCanonicalSignature() const { - return getName() + FunctionType(*this).getCanonicalSignature(); + return FunctionType(*this).getCanonicalSignature(getName()); } Declaration::LValueType VariableDeclaration::getLValueType() const @@ -504,5 +517,18 @@ void Literal::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } + +ASTPointer FunctionDescription::getDocumentation() +{ + auto function = dynamic_cast(m_description.second); + if (function) + return function->getDocumentation(); +} + +string FunctionDescription::getSignature() +{ + return m_description.first->getCanonicalSignature(m_description.second->getName()); +} + } } diff --git a/AST.h b/AST.h index e047f9761..695e504ac 100755 --- a/AST.h +++ b/AST.h @@ -156,6 +156,23 @@ private: Declaration const* m_scope; }; + +/** + * Generic function description able to describe both normal functions and + * functions that should be made as accessors to state variables + */ +struct FunctionDescription +{ + FunctionDescription(FunctionType const *_type, Declaration const* _decl): + m_description(_type, _decl){} + + ASTPointer getDocumentation(); + std::string getSignature(); + + std::pair m_description; +}; + + /** * Definition of a contract. This is the only AST nodes where child nodes are not visited in * document order. It first visits all struct declarations, then all variable declarations and @@ -202,7 +219,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, FunctionDefinition const*> getInterfaceFunctions() const; + std::map, std::pair> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -215,7 +232,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, FunctionDefinition const*>> const& getInterfaceFunctionList() const; + std::vector, FunctionType const*, Declaration const*>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -225,7 +242,7 @@ private: ASTPointer m_documentation; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, FunctionDefinition const*>>> m_interfaceFunctionList; + mutable std::unique_ptr, FunctionType const*, Declaration const*>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode @@ -372,8 +389,8 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name): - Declaration(_location, _name), m_typeName(_type) {} + ASTPointer const& _name, bool _isPublic): + Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -385,10 +402,13 @@ public: void setType(std::shared_ptr const& _type) { m_type = _type; } virtual LValueType getLValueType() const override; + bool isLocalVariable() const { return !!dynamic_cast(getScope()); } + bool isPublic() const { return m_isPublic; } + private: - ASTPointer m_typeName; ///< can be empty ("var") - + ASTPointer m_typeName; ///< can be empty ("var") + bool m_isPublic; ///< Whether there is an accessor for it or not std::shared_ptr m_type; ///< derived type, initially empty }; @@ -1076,5 +1096,6 @@ private: /// @} + } } diff --git a/Compiler.cpp b/Compiler.cpp index 5190f93f3..13f8282e6 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -201,7 +201,7 @@ set Compiler::getFunctionsCalled(set void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions(); + map, FunctionType const*, Declaration const*> interfaceFunctions = _contract.getInterfaceFunctions(); map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -209,7 +209,6 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) CompilerUtils(m_context).loadFromMemory(0, 4, false, true); // stack now is: 1 0 - // for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) for (auto const& it: interfaceFunctions) { callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag())); @@ -220,29 +219,28 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionDefinition const& function = *it.second; + FunctionType const* functionType = *it.second.first; m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); - appendCalldataUnpacker(function); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); + appendCalldataUnpacker(functionType->getParameterTypes()); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second.second)); m_context << returnTag; - appendReturnValuePacker(function); + appendReturnValuePacker(functionType->getReturnParameterTypes()); } } -unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory) +unsigned Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) { // We do not check the calldata size, everything is zero-padded. unsigned dataOffset = CompilerUtils::dataStartOffset; // the 4 bytes of the function hash signature //@todo this can be done more efficiently, saving some CALLDATALOAD calls - for (ASTPointer const& var: _function.getParameters()) + for (TypePointer const& type: _typeParameters) { - unsigned const c_numBytes = var->getType()->getCalldataEncodedSize(); + unsigned const c_numBytes = type->getCalldataEncodedSize(); if (c_numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(var->getLocation()) - << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); - bool const c_leftAligned = var->getType()->getCategory() == Type::Category::STRING; + << errinfo_comment("Type " + type->toString() + " not yet supported.")); + bool const c_leftAligned = type->getCategory() == Type::Category::STRING; bool const c_padToWords = true; dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned, !_fromMemory, c_padToWords); @@ -250,26 +248,26 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b return dataOffset; } -void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) +void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters) { //@todo this can be also done more efficiently unsigned dataOffset = 0; - vector> const& parameters = _function.getReturnParameters(); - unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters); - for (unsigned i = 0; i < parameters.size(); ++i) + unsigned stackDepth = 0; + for (TypePointer const& type: _typeParameters) + stackDepth += type->getSizeOnStack(); + + for (TypePointer const& type: _typeParameters) { - Type const& paramType = *parameters[i]->getType(); - unsigned numBytes = paramType.getCalldataEncodedSize(); + unsigned numBytes = type->getCalldataEncodedSize(); if (numBytes > 32) BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(parameters[i]->getLocation()) - << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); - CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); - ExpressionCompiler::appendTypeConversion(m_context, paramType, paramType, true); - bool const c_leftAligned = paramType.getCategory() == Type::Category::STRING; + << errinfo_comment("Type " + type->toString() + " not yet supported.")); + CompilerUtils(m_context).copyToStackTop(stackDepth, *type); + ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true); + bool const c_leftAligned = type->getCategory() == Type::Category::STRING; bool const c_padToWords = true; dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords); - stackDepth -= paramType.getSizeOnStack(); + stackDepth -= type->getSizeOnStack(); } // note that the stack is not cleaned up here m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN; diff --git a/Compiler.h b/Compiler.h index b65476460..f40339b03 100644 --- a/Compiler.h +++ b/Compiler.h @@ -56,10 +56,10 @@ private: std::function const& _resolveFunctionOverride, std::function const& _resolveModifierOverride); void appendFunctionSelector(ContractDefinition const& _contract); - /// Creates code that unpacks the arguments for the given function, from memory if - /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. - unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false); - void appendReturnValuePacker(FunctionDefinition const& _function); + /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. + /// From memory if @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. + unsigned appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); + void appendReturnValuePacker(TypePointers const& _typeParameters); void registerStateVariables(ContractDefinition const& _contract); diff --git a/CompilerContext.cpp b/CompilerContext.cpp index ad1877ba6..4edced940 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -83,9 +83,9 @@ bool CompilerContext::isLocalVariable(Declaration const* _declaration) const return m_localVariables.count(_declaration); } -eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const +eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration) const { - auto res = m_functionEntryLabels.find(&_function); + auto res = m_functionEntryLabels.find(&_declaration); solAssert(res != m_functionEntryLabels.end(), "Function entry label not found."); return res->second.tag(); } diff --git a/CompilerContext.h b/CompilerContext.h index d82dfe51e..aa438cf02 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -58,7 +58,7 @@ public: bool isLocalVariable(Declaration const* _declaration) const; bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; } - eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; + eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration) const; /// @returns the entry label of the given function and takes overrides into account. eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const; ModifierDefinition const& getFunctionModifier(std::string const& _name) const; @@ -115,9 +115,12 @@ private: u256 m_stateVariablesSize = 0; /// Storage offsets of state variables std::map m_stateVariables; - /// Positions of local variables on the stack. + /// Offsets of local variables on the stack (relative to stack base). std::map m_localVariables; - /// Labels pointing to the entry points of funcitons. + /// Sum of stack sizes of local variables + unsigned m_localVariablesSize; + /// Labels pointing to the entry points of functions. + std::map m_functionEntryLabels; /// Labels pointing to the entry points of function overrides. std::map m_virtualFunctionEntryLabels; diff --git a/Parser.cpp b/Parser.cpp index c9ab551e4..1c61aab12 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -109,30 +109,6 @@ ASTPointer Parser::parseImportDirective() return nodeFactory.createNode(url); } -void Parser::addStateVariableAccessor(ASTPointer const& _varDecl, - vector> & _functions) -{ - ASTNodeFactory nodeFactory(*this); - nodeFactory.setLocationEmpty(); - ASTPointer emptyDoc; - - vector> parameters; - auto expression = nodeFactory.createNode(make_shared(_varDecl->getName())); - vector> block_statements = {nodeFactory.createNode(expression)}; - - _functions.push_back(nodeFactory.createNode( - make_shared(_varDecl->getName()), - true, // isPublic - false, // not a Constructor - emptyDoc, // no documentation - nodeFactory.createNode(vector>()), - true, // is constant - nodeFactory.createNode(vector>()), - nodeFactory.createNode(block_statements) - ) - ); -} - ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); @@ -174,9 +150,7 @@ ASTPointer Parser::parseContractDefinition() Token::isElementaryTypeName(currentToken)) { bool const allowVar = false; - stateVariables.push_back(parseVariableDeclaration(allowVar)); - if (visibilityIsPublic) - addStateVariableAccessor(stateVariables.back(), functions); + stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic)); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) @@ -271,12 +245,12 @@ ASTPointer Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ASTPointer Parser::parseVariableDeclaration(bool _allowVar) +ASTPointer Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic) { ASTNodeFactory nodeFactory(*this); ASTPointer type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); - return nodeFactory.createNode(type, expectIdentifierToken()); + return nodeFactory.createNode(type, expectIdentifierToken(), _isPublic); } ASTPointer Parser::parseModifierDefinition() diff --git a/Parser.h b/Parser.h index d911598b6..a8f97c6e3 100644 --- a/Parser.h +++ b/Parser.h @@ -52,7 +52,7 @@ private: ASTPointer parseInheritanceSpecifier(); ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer parseStructDefinition(); - ASTPointer parseVariableDeclaration(bool _allowVar); + ASTPointer parseVariableDeclaration(bool _allowVar, bool _isPublic = false); ASTPointer parseModifierDefinition(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); @@ -78,10 +78,6 @@ private: ///@{ ///@name Helper functions - /// Depending on whether a state Variable is Public, appends an accessor to the contract's functions - void addStateVariableAccessor(ASTPointer const& _varDecl, - std::vector> & _functions); - /// Peeks ahead in the scanner to determine if a variable definition is going to follow bool peekVariableDefinition(); diff --git a/Types.cpp b/Types.cpp index dad3a1455..d5b001636 100644 --- a/Types.cpp +++ b/Types.cpp @@ -489,7 +489,7 @@ MemberList const& ContractType::getMembers() const map> members(IntegerType::AddressMemberList.begin(), IntegerType::AddressMemberList.end()); for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second->getName()] = make_shared(*it.second, false); + members[it.second.second->getName()] = make_shared(*it.second.second, false); m_members.reset(new MemberList(members)); } return *m_members; @@ -512,7 +512,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) - if (it->second->getName() == _functionName) + if (it->second.second->getName() == _functionName) return FixedHash<4>::Arith(it->first); return Invalid256; @@ -593,6 +593,19 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal swap(retParams, m_returnParameterTypes); } +FunctionType::FunctionType(VariableDeclaration const& _varDecl): + m_location(Location::INTERNAL) +{ + TypePointers params; + TypePointers retParams; + // for now, no input parameters LTODO: change for some things like mapping + params.reserve(0); + retParams.reserve(1); + retParams.push_back(_varDecl.getType()); + swap(params, m_parameterTypes); + swap(retParams, m_returnParameterTypes); +} + bool FunctionType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -672,9 +685,9 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::getCanonicalSignature() const +string FunctionType::getCanonicalSignature(std::string const& _name) const { - string ret = "("; + string ret = _name + "("; for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); diff --git a/Types.h b/Types.h index b7f87d760..b3a92021b 100644 --- a/Types.h +++ b/Types.h @@ -353,6 +353,7 @@ public: virtual Category getCategory() const override { return Category::FUNCTION; } explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); + explicit FunctionType(VariableDeclaration const& _varDecl); FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes, Location _location = Location::INTERNAL): FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), @@ -375,7 +376,7 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - std::string getCanonicalSignature() const; + std::string getCanonicalSignature(std::string const &_name) const; bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } From 5c7359aa09c46eb7fc27a70e328adde93d4844ab Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 23 Jan 2015 16:37:06 +0100 Subject: [PATCH 05/25] State variable accessors code is now more organized - FunctionDescription is the abstraction of what should describe a function. It can either be a VariableDeclaration of a FunctionDefinition. - ParamDescription is what FunctionDescription uses to describe its parameters for outside use purposes with a pair of (name, type) strings - Modified code around Solidity and especially interface handler to adapt to this change --- AST.cpp | 115 ++++++++++++++++++++++++++++++++++++++----- AST.h | 54 +++++++++++++++++--- Compiler.cpp | 9 ++-- InterfaceHandler.cpp | 37 +++++++------- Types.cpp | 27 ++++++++-- Types.h | 4 ++ 6 files changed, 199 insertions(+), 47 deletions(-) diff --git a/AST.cpp b/AST.cpp index 7620eeece..e4b5ed7d3 100644 --- a/AST.cpp +++ b/AST.cpp @@ -71,17 +71,19 @@ void ContractDefinition::checkTypeRequirements() FixedHash<4> const& hash = std::get<0>(hashAndFunction); if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( - "Function signature hash collision for " + - std::get<1>(hashAndFunction)>->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); + std::string("Function signature hash collision for ") + + std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); hashes.insert(hash); } } -map, pair> ContractDefinition::getInterfaceFunctions() const +map, FunctionDescription> ContractDefinition::getInterfaceFunctions() const { - vector, FunctionType const*, Declaration const*>>> exportedFunctionList = getInterfaceFunctionList(); - map, pair> exportedFunctions(exportedFunctionList.begin(), - exportedFunctionList.end()); + auto exportedFunctionList = getInterfaceFunctionList(); + + map, FunctionDescription> exportedFunctions; + for (auto const& it: exportedFunctionList) + exportedFunctions[std::get<0>(it)] = std::move(FunctionDescription(std::get<1>(it), std::get<2>(it))); solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -136,12 +138,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, FunctionType const*, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector, std::shared_ptr, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, FunctionType const*, Declaration const*>>()); + m_interfaceFunctionList.reset(new vector, std::shared_ptr, Declaration const*>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) @@ -149,7 +151,7 @@ vector, FunctionType const*, Declaration const*>> const& Cont { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_tuple(hash, FunctionType(*f), f.get())); + m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f), f.get())); } for (ASTPointer const& v: contract->getStateVariables()) @@ -157,8 +159,8 @@ vector, FunctionType const*, Declaration const*>> const& Cont { FunctionType ftype(*v); functionsSeen.insert(v->getName()); - FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())); - m_interfaceFunctionList->push_back(make_tuple(hash, ftype, v.get())); + FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); + m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*v), v.get())); } } } @@ -518,17 +520,104 @@ void Literal::checkTypeRequirements() } -ASTPointer FunctionDescription::getDocumentation() +bool ParamDescription::operator!=(ParamDescription const& _other) const +{ + return m_description.first == _other.getName() && m_description.second == _other.getType(); +} + +std::ostream& ParamDescription::operator<<(std::ostream& os) const +{ + return os << m_description.first << ":" << m_description.second; +} + +std::string ParamDescription::getName() const +{ + return m_description.first; +} + +std::string ParamDescription::getType() const +{ + return m_description.second; +} + + +ASTPointer FunctionDescription::getDocumentation() const { auto function = dynamic_cast(m_description.second); if (function) return function->getDocumentation(); + + return ASTPointer(); } -string FunctionDescription::getSignature() +string FunctionDescription::getSignature() const { return m_description.first->getCanonicalSignature(m_description.second->getName()); } +string FunctionDescription::getName() const +{ + return m_description.second->getName(); +} + +bool FunctionDescription::isConstant() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + return function->isDeclaredConst(); + + return true; +} + +vector const FunctionDescription::getParameters() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + { + vector paramsDescription; + for (auto const& param: function->getParameters()) + paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); + + return paramsDescription; + } + + // else for now let's assume no parameters to accessors + // LTODO: fix this for mapping types + return {}; +} + +vector const FunctionDescription::getReturnParameters() const +{ + auto function = dynamic_cast(m_description.second); + if (function) + { + vector paramsDescription; + for (auto const& param: function->getParameters()) + paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); + + return paramsDescription; + } + + auto vardecl = dynamic_cast(m_description.second); + return {ParamDescription(vardecl->getName(), vardecl->getType()->toString())}; +} + +Declaration const* FunctionDescription::getDeclaration() const +{ + return m_description.second; +} + +shared_ptr FunctionDescription::getFunctionTypeShared() const +{ + return m_description.first; +} + + +FunctionType const* FunctionDescription::getFunctionType() const +{ + return m_description.first.get(); +} + + } } diff --git a/AST.h b/AST.h index 695e504ac..89daf80fb 100755 --- a/AST.h +++ b/AST.h @@ -157,19 +157,59 @@ private: }; +/** +* Generic Parameter description used by @see FunctionDescription to return +* a descripton of its parameters. +*/ +struct ParamDescription +{ + ParamDescription(std::string const& _name, std::string const& _type): + m_description(_name, _type){} + + bool operator!=(ParamDescription const& _other) const; + std::ostream& operator<<(std::ostream& os) const; + + std::string getName() const; + std::string getType() const; + + std::pair m_description; +}; + + /** * Generic function description able to describe both normal functions and * functions that should be made as accessors to state variables */ struct FunctionDescription { - FunctionDescription(FunctionType const *_type, Declaration const* _decl): + FunctionDescription(std::shared_ptr _type, Declaration const* _decl): m_description(_type, _decl){} - ASTPointer getDocumentation(); - std::string getSignature(); + FunctionDescription(): + m_description(nullptr, nullptr){} - std::pair m_description; + /// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc + ASTPointer getDocumentation() const; + /// @returns the canonical signature of the function + std::string getSignature() const; + /// @returns the name of the function, basically that of the declaration + std::string getName() const; + /// @returns whether the function is constant. IF it's an accessor this is always true + bool isConstant() const; + /// @returns the argument parameters of the function + std::vector const getParameters() const; + /// @returns the return parameters of the function + std::vector const getReturnParameters() const; + /// @returns the Declaration AST Node pointer + Declaration const* getDeclaration() const; + /// @returns a created shared pointer with the type of the function + std::shared_ptr makeFunctionType() const; + /// @returns a pointer to the function type + FunctionType const* getFunctionType() const; + /// @returns a shared pointer to the function type + std::shared_ptr getFunctionTypeShared() const; + + std::pair, Declaration const*> m_description; }; @@ -219,7 +259,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, std::pair> getInterfaceFunctions() const; + std::map, FunctionDescription> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -232,7 +272,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, FunctionType const*, Declaration const*>> const& getInterfaceFunctionList() const; + std::vector, std::shared_ptr, Declaration const*>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -242,7 +282,7 @@ private: ASTPointer m_documentation; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, FunctionType const*, Declaration const*>>> m_interfaceFunctionList; + mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/Compiler.cpp b/Compiler.cpp index 13f8282e6..f6f48a8c2 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -177,13 +177,14 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) unsigned argumentSize = 0; for (ASTPointer const& var: _constructor.getParameters()) argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize()); + if (argumentSize > 0) { m_context << u256(argumentSize); m_context.appendProgramSize(); m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls m_context << eth::Instruction::CODECOPY; - appendCalldataUnpacker(_constructor, true); + appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true); } m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor)); m_context << returnTag; @@ -201,7 +202,7 @@ set Compiler::getFunctionsCalled(set void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionType const*, Declaration const*> interfaceFunctions = _contract.getInterfaceFunctions(); + map, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions(); map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -219,11 +220,11 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionType const* functionType = *it.second.first; + FunctionType const* functionType = it.second.getFunctionType(); m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second.second)); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 1adce8cb8..9b6327782 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -45,23 +45,23 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto populateParameters = [](std::vector> const& _vars) + auto populateParameters = [](vector const& _params) { Json::Value params(Json::arrayValue); - for (ASTPointer const& var: _vars) + for (auto const& param: _params) { Json::Value input; - input["name"] = var->getName(); - input["type"] = var->getType()->toString(); + input["name"] = param.getName(); + input["type"] = param.getType(); params.append(input); } return params; }; - method["name"] = it.second->getName(); - method["constant"] = it.second->isDeclaredConst(); - method["inputs"] = populateParameters(it.second->getParameters()); - method["outputs"] = populateParameters(it.second->getReturnParameters()); + method["name"] = it.second.getName(); + method["constant"] = it.second.isConstant(); + method["inputs"] = populateParameters(it.second.getParameters()); + method["outputs"] = populateParameters(it.second.getReturnParameters()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); @@ -72,17 +72,16 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition string ret = "contract " + _contractDef.getName() + "{"; for (auto const& it: _contractDef.getInterfaceFunctions()) { - FunctionDefinition const* f = it.second; - auto populateParameters = [](vector> const& _vars) + auto populateParameters = [](vector const& _params) { string r = ""; - for (ASTPointer const& var: _vars) - r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName(); + for (auto const& param: _params) + r += (r.size() ? "," : "(") + param.getType() + " " + param.getName(); return r.size() ? r + ")" : "()"; }; - ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : ""); - if (f->getReturnParameters().size()) - ret += "returns" + populateParameters(f->getReturnParameters()); + ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : ""); + if (it.second.getReturnParameters().size()) + ret += "returns" + populateParameters(it.second.getReturnParameters()); else if (ret.back() == ' ') ret.pop_back(); ret += "{}"; @@ -98,7 +97,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value user; - auto strPtr = it.second->getDocumentation(); + auto strPtr = it.second.getDocumentation(); if (strPtr) { resetUser(); @@ -106,7 +105,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second->getCanonicalSignature()] = user; + methods[it.second.getSignature()] = user; } } } @@ -139,7 +138,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value method; - auto strPtr = it.second->getDocumentation(); + auto strPtr = it.second.getDocumentation(); if (strPtr) { resetDev(); @@ -162,7 +161,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second->getCanonicalSignature()] = method; + methods[it.second.getSignature()] = method; } } doc["methods"] = methods; diff --git a/Types.cpp b/Types.cpp index d5b001636..c5f1a3ae5 100644 --- a/Types.cpp +++ b/Types.cpp @@ -489,7 +489,7 @@ MemberList const& ContractType::getMembers() const map> members(IntegerType::AddressMemberList.begin(), IntegerType::AddressMemberList.end()); for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second.second->getName()] = make_shared(*it.second.second, false); + members[it.second.getName()] = it.second.getFunctionTypeShared(); m_members.reset(new MemberList(members)); } return *m_members; @@ -511,9 +511,9 @@ shared_ptr const& ContractType::getConstructorType() const u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); - for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it) - if (it->second.second->getName() == _functionName) - return FixedHash<4>::Arith(it->first); + for (auto const& it: m_contract.getInterfaceFunctions()) + if (it.second.getName() == _functionName) + return FixedHash<4>::Arith(it.first); return Invalid256; } @@ -582,28 +582,47 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL) { TypePointers params; + vector paramNames; TypePointers retParams; + vector retParamNames; params.reserve(_function.getParameters().size()); + paramNames.reserve(_function.getParameters().size()); for (ASTPointer const& var: _function.getParameters()) + { + paramNames.push_back(var->getName()); params.push_back(var->getType()); + } retParams.reserve(_function.getReturnParameters().size()); + retParamNames.reserve(_function.getReturnParameters().size()); for (ASTPointer const& var: _function.getReturnParameters()) + { + retParamNames.push_back(var->getName()); retParams.push_back(var->getType()); + } swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_location(Location::INTERNAL) { TypePointers params; + vector paramNames; TypePointers retParams; + vector retParamNames; // for now, no input parameters LTODO: change for some things like mapping params.reserve(0); + paramNames.reserve(0); retParams.reserve(1); + retParamNames.reserve(1); retParams.push_back(_varDecl.getType()); + retParamNames.push_back(_varDecl.getName()); swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); } bool FunctionType::operator==(Type const& _other) const diff --git a/Types.h b/Types.h index b3a92021b..b26157b3e 100644 --- a/Types.h +++ b/Types.h @@ -365,7 +365,9 @@ public: m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} TypePointers const& getParameterTypes() const { return m_parameterTypes; } + std::vector const& getParameterNames() const { return m_parameterNames; } TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } + std::vector const& getReturnParameterNames() const { return m_returnParameterNames; } virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; @@ -390,6 +392,8 @@ private: TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; + std::vector m_parameterNames; + std::vector m_returnParameterNames; Location const m_location; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack From 468f26b1cce189ea47af9f99b8efe0834a3eed8c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 23 Jan 2015 17:55:58 +0100 Subject: [PATCH 06/25] Modifications to Mix to adapt to FunctionDescription --- AST.cpp | 10 ++++++++++ AST.h | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index e4b5ed7d3..ca76d023c 100644 --- a/AST.cpp +++ b/AST.cpp @@ -607,6 +607,16 @@ Declaration const* FunctionDescription::getDeclaration() const return m_description.second; } +VariableDeclaration const* FunctionDescription::getVariableDeclaration() const +{ + return dynamic_cast(m_description.second); +} + +FunctionDefinition const* FunctionDescription::getFunctionDefinition() const +{ + return dynamic_cast(m_description.second); +} + shared_ptr FunctionDescription::getFunctionTypeShared() const { return m_description.first; diff --git a/AST.h b/AST.h index 89daf80fb..bda06a5db 100755 --- a/AST.h +++ b/AST.h @@ -200,8 +200,12 @@ struct FunctionDescription std::vector const getParameters() const; /// @returns the return parameters of the function std::vector const getReturnParameters() const; - /// @returns the Declaration AST Node pointer + /// @returns a generic Declaration AST Node pointer which can be either a FunctionDefinition or a VariableDeclaration Declaration const* getDeclaration() const; + /// @returns the VariableDeclaration AST Node pointer or nullptr if it's not a VariableDeclaration + VariableDeclaration const* getVariableDeclaration() const; + /// @returns the FunctionDefinition AST Node pointer or nullptr if it's not a FunctionDefinition + FunctionDefinition const* getFunctionDefinition() const; /// @returns a created shared pointer with the type of the function std::shared_ptr makeFunctionType() const; /// @returns a pointer to the function type From 3f5eb965840b57a3d8922c39d1e9f75266993db6 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 26 Jan 2015 09:48:29 +0100 Subject: [PATCH 07/25] Various small fixes for Sol Automatic Accessors --- AST.cpp | 14 +------------- AST.h | 3 --- Types.cpp | 2 +- Types.h | 2 +- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/AST.cpp b/AST.cpp index ca76d023c..38adebf9d 100644 --- a/AST.cpp +++ b/AST.cpp @@ -519,17 +519,6 @@ void Literal::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } - -bool ParamDescription::operator!=(ParamDescription const& _other) const -{ - return m_description.first == _other.getName() && m_description.second == _other.getType(); -} - -std::ostream& ParamDescription::operator<<(std::ostream& os) const -{ - return os << m_description.first << ":" << m_description.second; -} - std::string ParamDescription::getName() const { return m_description.first; @@ -540,7 +529,6 @@ std::string ParamDescription::getType() const return m_description.second; } - ASTPointer FunctionDescription::getDocumentation() const { auto function = dynamic_cast(m_description.second); @@ -575,7 +563,7 @@ vector const FunctionDescription::getParameters() const if (function) { vector paramsDescription; - for (auto const& param: function->getParameters()) + for (auto const& param: function->getReturnParameters()) paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); return paramsDescription; diff --git a/AST.h b/AST.h index bda06a5db..d9a95e506 100755 --- a/AST.h +++ b/AST.h @@ -166,9 +166,6 @@ struct ParamDescription ParamDescription(std::string const& _name, std::string const& _type): m_description(_name, _type){} - bool operator!=(ParamDescription const& _other) const; - std::ostream& operator<<(std::ostream& os) const; - std::string getName() const; std::string getType() const; diff --git a/Types.cpp b/Types.cpp index c5f1a3ae5..812271e30 100644 --- a/Types.cpp +++ b/Types.cpp @@ -606,7 +606,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_location(Location::INTERNAL) + m_location(Location::EXTERNAL) { TypePointers params; vector paramNames; diff --git a/Types.h b/Types.h index b26157b3e..2dbed95fa 100644 --- a/Types.h +++ b/Types.h @@ -378,7 +378,7 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - std::string getCanonicalSignature(std::string const &_name) const; + std::string getCanonicalSignature(std::string const& _name) const; bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } From b95048768dbb64fc7d14eb93bdaad7c85b74e7c8 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 26 Jan 2015 14:41:56 +0100 Subject: [PATCH 08/25] Solidity EntryLabel now uses a generic declaration - Instead of a FunctionDefinition --- Compiler.cpp | 10 +++++++--- CompilerContext.cpp | 6 +++--- CompilerContext.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index f6f48a8c2..28a9b3d10 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -43,9 +43,13 @@ void Compiler::compileContract(ContractDefinition const& _contract, for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) { - for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor()) - m_context.addFunction(*function); + for (auto const& it: contract->getInterfaceFunctions()) + { + auto funcDef = it.second.getFunctionDefinition(); + if (funcDef && funcDef->isConstructor()) + continue; + m_context.addFunction(*it.second.getDeclaration()); + } for (ASTPointer const& modifier: contract->getFunctionModifiers()) m_context.addModifier(*modifier); } diff --git a/CompilerContext.cpp b/CompilerContext.cpp index 4edced940..ea349c0d2 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -59,11 +59,11 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla *this << u256(0); } -void CompilerContext::addFunction(FunctionDefinition const& _function) +void CompilerContext::addFunction(Declaration const& _decl) { eth::AssemblyItem tag(m_asm.newTag()); - m_functionEntryLabels.insert(make_pair(&_function, tag)); - m_virtualFunctionEntryLabels.insert(make_pair(_function.getName(), tag)); + m_functionEntryLabels.insert(make_pair(&_decl, tag)); + m_virtualFunctionEntryLabels.insert(make_pair(_decl.getName(), tag)); } void CompilerContext::addModifier(ModifierDefinition const& _modifier) diff --git a/CompilerContext.h b/CompilerContext.h index aa438cf02..42ac9ee82 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -44,7 +44,7 @@ public: void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void addAndInitializeVariable(VariableDeclaration const& _declaration); - void addFunction(FunctionDefinition const& _function); + void addFunction(Declaration const& _decl); /// Adds the given modifier to the list by name if the name is not present already. void addModifier(ModifierDefinition const& _modifier); From 53ef6900b797cde404faac94af3058669187c115 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 26 Jan 2015 16:14:40 +0100 Subject: [PATCH 09/25] Adding isStateVariable attribute to a VarDecl --- AST.h | 6 ++++-- Parser.cpp | 6 +++--- Parser.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AST.h b/AST.h index d9a95e506..d769aa225 100755 --- a/AST.h +++ b/AST.h @@ -430,8 +430,8 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name, bool _isPublic): - Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic) {} + ASTPointer const& _name, bool _isPublic, bool _isStateVar = false): + Declaration(_location, _name), m_typeName(_type), m_isPublic(_isPublic), m_isStateVariable(_isStateVar) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -445,11 +445,13 @@ public: virtual LValueType getLValueType() const override; bool isLocalVariable() const { return !!dynamic_cast(getScope()); } bool isPublic() const { return m_isPublic; } + bool isStateVariable() const { return m_isStateVariable; } private: ASTPointer m_typeName; ///< can be empty ("var") bool m_isPublic; ///< Whether there is an accessor for it or not + bool m_isStateVariable; ///< Whether or not this is a contract state variable std::shared_ptr m_type; ///< derived type, initially empty }; diff --git a/Parser.cpp b/Parser.cpp index 1c61aab12..5cfc8f462 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -150,7 +150,7 @@ ASTPointer Parser::parseContractDefinition() Token::isElementaryTypeName(currentToken)) { bool const allowVar = false; - stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic)); + stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true)); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) @@ -245,12 +245,12 @@ ASTPointer Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ASTPointer Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic) +ASTPointer Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable) { ASTNodeFactory nodeFactory(*this); ASTPointer type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); - return nodeFactory.createNode(type, expectIdentifierToken(), _isPublic); + return nodeFactory.createNode(type, expectIdentifierToken(), _isPublic, _isStateVariable); } ASTPointer Parser::parseModifierDefinition() diff --git a/Parser.h b/Parser.h index a8f97c6e3..d3bff67e5 100644 --- a/Parser.h +++ b/Parser.h @@ -52,7 +52,7 @@ private: ASTPointer parseInheritanceSpecifier(); ASTPointer parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer parseStructDefinition(); - ASTPointer parseVariableDeclaration(bool _allowVar, bool _isPublic = false); + ASTPointer parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false); ASTPointer parseModifierDefinition(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); From 3ec6c0b1cbe1be0a95f711ce74b0e360fabdaa96 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 26 Jan 2015 16:58:54 +0100 Subject: [PATCH 10/25] All interface functions are external. --- AST.cpp | 2 +- Compiler.cpp | 40 ++++++++++++++++++++++++++++++++-------- Compiler.h | 2 ++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/AST.cpp b/AST.cpp index 38adebf9d..bf3d98947 100644 --- a/AST.cpp +++ b/AST.cpp @@ -151,7 +151,7 @@ vector, std::shared_ptr, Declaration cons { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f), f.get())); + m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f, false), f.get())); } for (ASTPointer const& v: contract->getStateVariables()) diff --git a/Compiler.cpp b/Compiler.cpp index 28a9b3d10..c419f484d 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -43,23 +43,30 @@ void Compiler::compileContract(ContractDefinition const& _contract, for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) { - for (auto const& it: contract->getInterfaceFunctions()) - { - auto funcDef = it.second.getFunctionDefinition(); - if (funcDef && funcDef->isConstructor()) - continue; - m_context.addFunction(*it.second.getDeclaration()); - } - for (ASTPointer const& modifier: contract->getFunctionModifiers()) + for (ASTPointer const& function: contract->getDefinedFunctions()) + if (!function->isConstructor()) + m_context.addFunction(*function); + + for (ASTPointer const& vardecl: contract->getStateVariables()) + if (vardecl->isPublic()) + m_context.addFunction(*vardecl); + + for (ASTPointer const& modifier: contract->getFunctionModifiers()) m_context.addModifier(*modifier); } appendFunctionSelector(_contract); for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) + { for (ASTPointer const& function: contract->getDefinedFunctions()) if (!function->isConstructor()) function->accept(*this); + for (ASTPointer const& vardecl: contract->getStateVariables()) + if (vardecl->isPublic()) + generateAccessorCode(*vardecl); + } + // Swap the runtime context with the creation-time context swap(m_context, m_runtimeContext); initializeContext(_contract, _contracts); @@ -285,6 +292,23 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) m_context.addStateVariable(*variable); } +bool Compiler::generateAccessorCode(VariableDeclaration const& _varDecl) +{ + m_context.startNewFunction(); + m_returnTag = m_context.newTag(); + m_breakTags.clear(); + m_continueTags.clear(); + + // TODO: Work in progress + m_context << m_context.getFunctionEntryLabel(_varDecl); + // CompilerUtils(m_context).moveToStackVariable(firstVariable); + m_context.appendJumpTo(m_returnTag); + m_context << m_returnTag; + + // TODO: perhaps return void if there are no checks? + return true; +} + bool Compiler::visit(FunctionDefinition const& _function) { //@todo to simplify this, the calling convention could by changed such that diff --git a/Compiler.h b/Compiler.h index f40339b03..71a12a667 100644 --- a/Compiler.h +++ b/Compiler.h @@ -63,6 +63,8 @@ private: void registerStateVariables(ContractDefinition const& _contract); + bool generateAccessorCode(VariableDeclaration const& _varDecl); + virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; From 6e793b24996610c5a88a0aafb9fad365d9f12b00 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 26 Jan 2015 18:16:47 +0100 Subject: [PATCH 11/25] Work on ExpressionCompiler preparing for Accessors from storage --- ExpressionCompiler.cpp | 55 +++++++++++++++++++++++++++--------------- ExpressionCompiler.h | 8 ++++++ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index bd8c86531..450cc2fec 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -789,6 +789,13 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ return length; } +void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const* _varDecl) +{ + m_currentLValue.fromStateVariable(*_varDecl, _varDecl->getType()); + // TODO + // m_currentLValue.retrieveValueFromStorage(); +} + ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset): m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) @@ -816,21 +823,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo break; } case STORAGE: - if (!_expression.getType()->isValueType()) - break; // no distinction between value and reference for non-value types - if (!_remove) - *m_context << eth::Instruction::DUP1; - if (m_size == 1) - *m_context << eth::Instruction::SLOAD; - else - for (unsigned i = 0; i < m_size; ++i) - { - *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1; - if (i + 1 < m_size) - *m_context << u256(1) << eth::Instruction::ADD; - else - *m_context << eth::Instruction::POP; - } + retrieveValueFromStorage(_expression, _remove); break; case MEMORY: if (!_expression.getType()->isValueType()) @@ -845,6 +838,25 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo } } +void ExpressionCompiler::LValue::retrieveValueFromStorage(Expression const& _expression, bool _remove) const +{ + if (!_expression.getType()->isValueType()) + return; // no distinction between value and reference for non-value types + if (!_remove) + *m_context << eth::Instruction::DUP1; + if (m_size == 1) + *m_context << eth::Instruction::SLOAD; + else + for (unsigned i = 0; i < m_size; ++i) + { + *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1; + if (i + 1 < m_size) + *m_context << u256(1) << eth::Instruction::ADD; + else + *m_context << eth::Instruction::POP; + } +} + void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const { switch (m_type) @@ -951,6 +963,14 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co } } +void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, std::shared_ptr const& _type) +{ + m_type = STORAGE; + solAssert(_type->getStorageSize() <= numeric_limits::max(), "The storage size of " + _type->toString() + " should fit in an unsigned"); + *m_context << m_context->getStorageLocationOfVariable(_varDecl); + m_size = unsigned(_type->getStorageSize()); +} + void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) { if (m_context->isLocalVariable(&_declaration)) @@ -961,10 +981,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D } else if (m_context->isStateVariable(&_declaration)) { - m_type = STORAGE; - solAssert(_identifier.getType()->getStorageSize() <= numeric_limits::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned"); - m_size = unsigned(_identifier.getType()->getStorageSize()); - *m_context << m_context->getStorageLocationOfVariable(_declaration); + fromStateVariable(_declaration, _identifier.getType()); } else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation()) diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 8f784fde9..7189bef99 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -95,6 +95,9 @@ private: unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector> const& _arguments, unsigned _memoryOffset = 0); + /// Appends code for a State Variable accessor function + void appendStateVariableAccessor(VariableDeclaration const* _varDecl); + /** * Helper class to store and retrieve lvalues to and from various locations. * All types except STACK store a reference in a slot on the stack, STACK just @@ -111,6 +114,8 @@ private: /// Set type according to the declaration and retrieve the reference. /// @a _expression is the current expression void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); + /// Convenience function to set type for a state variable and retrieve the reference + void fromStateVariable(Declaration const& _varDecl, std::shared_ptr const& _type); void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; } bool isValid() const { return m_type != NONE; } @@ -125,6 +130,9 @@ private: /// also removes the reference from the stack (note that is does not reset the type to @a NONE). /// @a _expression is the current expression, used for error reporting. void retrieveValue(Expression const& _expression, bool _remove = false) const; + /// Convenience function to retrive Value from Storage. Specific version of + /// @ref retrieveValue + void retrieveValueFromStorage(Expression const& _expression, bool _remove = false) const; /// Stores a value (from the stack directly beneath the reference, which is assumed to /// be on the top of the stack, if any) in the lvalue and removes the reference. /// Also removes the stored value from the stack if @a _move is From ff91ab96ea843bd8ab9a7c57fd799add2ebc9873 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 27 Jan 2015 15:43:28 +0100 Subject: [PATCH 12/25] Fixes after rebasing on develop --- AST.cpp | 4 ++-- AST.h | 4 ++++ ExpressionCompiler.h | 3 +-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/AST.cpp b/AST.cpp index bf3d98947..71bf7c4ab 100644 --- a/AST.cpp +++ b/AST.cpp @@ -563,7 +563,7 @@ vector const FunctionDescription::getParameters() const if (function) { vector paramsDescription; - for (auto const& param: function->getReturnParameters()) + for (auto const& param: function->getParameters()) paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); return paramsDescription; @@ -580,7 +580,7 @@ vector const FunctionDescription::getReturnParameters() const if (function) { vector paramsDescription; - for (auto const& param: function->getParameters()) + for (auto const& param: function->getReturnParameters()) paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); return paramsDescription; diff --git a/AST.h b/AST.h index d769aa225..a99bc39cb 100755 --- a/AST.h +++ b/AST.h @@ -182,6 +182,10 @@ struct FunctionDescription FunctionDescription(std::shared_ptr _type, Declaration const* _decl): m_description(_type, _decl){} + /// constructor for a constructor's function definition. Used only inside mix. + FunctionDescription(Declaration const* _def): + m_description(nullptr, _def){} + FunctionDescription(): m_description(nullptr, nullptr){} diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 7189bef99..bff2cd7ce 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -130,8 +130,7 @@ private: /// also removes the reference from the stack (note that is does not reset the type to @a NONE). /// @a _expression is the current expression, used for error reporting. void retrieveValue(Expression const& _expression, bool _remove = false) const; - /// Convenience function to retrive Value from Storage. Specific version of - /// @ref retrieveValue + /// Convenience function to retrive Value from Storage. Specific version of @ref retrieveValue void retrieveValueFromStorage(Expression const& _expression, bool _remove = false) const; /// Stores a value (from the stack directly beneath the reference, which is assumed to /// be on the top of the stack, if any) in the lvalue and removes the reference. From 2947e038d28d3d732b1db352f71c8c9669df8eef Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 27 Jan 2015 16:55:06 +0100 Subject: [PATCH 13/25] EVM Code for simple accessor function is properly generated --- Compiler.cpp | 12 +++++------- Compiler.h | 2 +- ExpressionCompiler.cpp | 15 ++++++++++----- ExpressionCompiler.h | 6 ++++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index c419f484d..bda18bc68 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -292,21 +292,19 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) m_context.addStateVariable(*variable); } -bool Compiler::generateAccessorCode(VariableDeclaration const& _varDecl) +void Compiler::generateAccessorCode(VariableDeclaration const& _varDecl) { m_context.startNewFunction(); m_returnTag = m_context.newTag(); m_breakTags.clear(); m_continueTags.clear(); - // TODO: Work in progress m_context << m_context.getFunctionEntryLabel(_varDecl); - // CompilerUtils(m_context).moveToStackVariable(firstVariable); - m_context.appendJumpTo(m_returnTag); - m_context << m_returnTag; + ExpressionCompiler::appendStateVariableAccessor(m_context, &_varDecl); - // TODO: perhaps return void if there are no checks? - return true; + uint64_t foo = uint64_t(_varDecl.getType()->getStorageSize()); + m_context << eth::dupInstruction(foo + 1); + m_context << eth::Instruction::JUMP; } bool Compiler::visit(FunctionDefinition const& _function) diff --git a/Compiler.h b/Compiler.h index 71a12a667..144af8eb8 100644 --- a/Compiler.h +++ b/Compiler.h @@ -63,7 +63,7 @@ private: void registerStateVariables(ContractDefinition const& _contract); - bool generateAccessorCode(VariableDeclaration const& _varDecl); + void generateAccessorCode(VariableDeclaration const& _varDecl); virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(IfStatement const& _ifStatement) override; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 450cc2fec..66e2c68cc 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -48,6 +48,12 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type co compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded); } +void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const* _varDecl, bool _optimize) +{ + ExpressionCompiler compiler(_context, _optimize); + compiler.appendStateVariableAccessor(_varDecl); +} + bool ExpressionCompiler::visit(Assignment const& _assignment) { _assignment.getRightHandSide().accept(*this); @@ -792,8 +798,7 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const* _varDecl) { m_currentLValue.fromStateVariable(*_varDecl, _varDecl->getType()); - // TODO - // m_currentLValue.retrieveValueFromStorage(); + m_currentLValue.retrieveValueFromStorage(_varDecl->getType(), true); } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, @@ -823,7 +828,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo break; } case STORAGE: - retrieveValueFromStorage(_expression, _remove); + retrieveValueFromStorage(_expression.getType(), _remove); break; case MEMORY: if (!_expression.getType()->isValueType()) @@ -838,9 +843,9 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo } } -void ExpressionCompiler::LValue::retrieveValueFromStorage(Expression const& _expression, bool _remove) const +void ExpressionCompiler::LValue::retrieveValueFromStorage(std::shared_ptr const& _type, bool _remove) const { - if (!_expression.getType()->isValueType()) + if (!_type->isValueType()) return; // no distinction between value and reference for non-value types if (!_remove) *m_context << eth::Instruction::DUP1; diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index bff2cd7ce..8479344ad 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -53,6 +53,8 @@ public: /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); + /// Appends code for a State Variable accessor function + static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const* _varDecl, bool _optimize = false); private: explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): @@ -130,8 +132,8 @@ private: /// also removes the reference from the stack (note that is does not reset the type to @a NONE). /// @a _expression is the current expression, used for error reporting. void retrieveValue(Expression const& _expression, bool _remove = false) const; - /// Convenience function to retrive Value from Storage. Specific version of @ref retrieveValue - void retrieveValueFromStorage(Expression const& _expression, bool _remove = false) const; + /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue + void retrieveValueFromStorage(std::shared_ptr const& _type, bool _remove = false) const; /// Stores a value (from the stack directly beneath the reference, which is assumed to /// be on the top of the stack, if any) in the lvalue and removes the reference. /// Also removes the stored value from the stack if @a _move is From c6fcb8555f727604c2ca28b685129b29ca74e361 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 28 Jan 2015 13:16:09 +0100 Subject: [PATCH 14/25] Simplify FunctionType's Vardecl constructor --- Types.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Types.cpp b/Types.cpp index 812271e30..fcb10d4b5 100644 --- a/Types.cpp +++ b/Types.cpp @@ -608,17 +608,12 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_location(Location::EXTERNAL) { - TypePointers params; - vector paramNames; - TypePointers retParams; - vector retParamNames; + TypePointers params({}); + vector paramNames({}); + TypePointers retParams({_varDecl.getType()}); + vector retParamNames({ _varDecl.getName()}); // for now, no input parameters LTODO: change for some things like mapping - params.reserve(0); - paramNames.reserve(0); - retParams.reserve(1); - retParamNames.reserve(1); - retParams.push_back(_varDecl.getType()); - retParamNames.push_back(_varDecl.getName()); + swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); From 1b25f85a66d5e05ff0684c33c6d43c3909f10200 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 28 Jan 2015 14:19:47 +0100 Subject: [PATCH 15/25] Explicitly specify insertion to exported functions --- AST.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AST.cpp b/AST.cpp index 71bf7c4ab..a23846a05 100644 --- a/AST.cpp +++ b/AST.cpp @@ -83,7 +83,7 @@ map, FunctionDescription> ContractDefinition::getInterfaceFunctions map, FunctionDescription> exportedFunctions; for (auto const& it: exportedFunctionList) - exportedFunctions[std::get<0>(it)] = std::move(FunctionDescription(std::get<1>(it), std::get<2>(it))); + exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); From b5a786dda19e54fa587b1b693e7a139818d65b9e Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 28 Jan 2015 18:06:45 +0100 Subject: [PATCH 16/25] Various fixes pertaining to State Variable accessors --- AST.cpp | 6 +++--- AST.h | 11 ++++++----- Compiler.cpp | 10 +++++----- CompilerContext.h | 3 --- ExpressionCompiler.cpp | 13 +++++++------ ExpressionCompiler.h | 8 ++++---- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/AST.cpp b/AST.cpp index a23846a05..d95a254e9 100644 --- a/AST.cpp +++ b/AST.cpp @@ -155,7 +155,7 @@ vector, std::shared_ptr, Declaration cons } for (ASTPointer const& v: contract->getStateVariables()) - if (v->isPublic()) + if (v->isPublic() && functionsSeen.count(v->getName()) == 0) { FunctionType ftype(*v); functionsSeen.insert(v->getName()); @@ -519,12 +519,12 @@ void Literal::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } -std::string ParamDescription::getName() const +std::string const& ParamDescription::getName() const { return m_description.first; } -std::string ParamDescription::getType() const +std::string const& ParamDescription::getType() const { return m_description.second; } diff --git a/AST.h b/AST.h index a99bc39cb..f397c13ad 100755 --- a/AST.h +++ b/AST.h @@ -158,16 +158,16 @@ private: /** -* Generic Parameter description used by @see FunctionDescription to return -* a descripton of its parameters. -*/ + * Generic Parameter description used by @see FunctionDescription to return + * a descripton of its parameters. + */ struct ParamDescription { ParamDescription(std::string const& _name, std::string const& _type): m_description(_name, _type){} - std::string getName() const; - std::string getType() const; + std::string const& getName() const; + std::string const& getType() const; std::pair m_description; }; @@ -456,6 +456,7 @@ private: ASTPointer m_typeName; ///< can be empty ("var") bool m_isPublic; ///< Whether there is an accessor for it or not bool m_isStateVariable; ///< Whether or not this is a contract state variable + std::shared_ptr m_type; ///< derived type, initially empty }; diff --git a/Compiler.cpp b/Compiler.cpp index bda18bc68..c7656363a 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -51,7 +51,7 @@ void Compiler::compileContract(ContractDefinition const& _contract, if (vardecl->isPublic()) m_context.addFunction(*vardecl); - for (ASTPointer const& modifier: contract->getFunctionModifiers()) + for (ASTPointer const& modifier: contract->getFunctionModifiers()) m_context.addModifier(*modifier); } @@ -295,15 +295,15 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) void Compiler::generateAccessorCode(VariableDeclaration const& _varDecl) { m_context.startNewFunction(); - m_returnTag = m_context.newTag(); m_breakTags.clear(); m_continueTags.clear(); m_context << m_context.getFunctionEntryLabel(_varDecl); - ExpressionCompiler::appendStateVariableAccessor(m_context, &_varDecl); + ExpressionCompiler::appendStateVariableAccessor(m_context, _varDecl); - uint64_t foo = uint64_t(_varDecl.getType()->getStorageSize()); - m_context << eth::dupInstruction(foo + 1); + unsigned sizeOnStack = _varDecl.getType()->getSizeOnStack(); + solAssert(sizeOnStack <= 15, "Illegal variable stack size detected"); + m_context << eth::dupInstruction(sizeOnStack + 1); m_context << eth::Instruction::JUMP; } diff --git a/CompilerContext.h b/CompilerContext.h index 42ac9ee82..9de3385a6 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -117,10 +117,7 @@ private: std::map m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). std::map m_localVariables; - /// Sum of stack sizes of local variables - unsigned m_localVariablesSize; /// Labels pointing to the entry points of functions. - std::map m_functionEntryLabels; /// Labels pointing to the entry points of function overrides. std::map m_virtualFunctionEntryLabels; diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 66e2c68cc..15ee17fd3 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -48,7 +48,7 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type co compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded); } -void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const* _varDecl, bool _optimize) +void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize) { ExpressionCompiler compiler(_context, _optimize); compiler.appendStateVariableAccessor(_varDecl); @@ -795,10 +795,11 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ return length; } -void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const* _varDecl) +void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - m_currentLValue.fromStateVariable(*_varDecl, _varDecl->getType()); - m_currentLValue.retrieveValueFromStorage(_varDecl->getType(), true); + m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); + solAssert(m_currentLValue.isInStorage(), ""); + m_currentLValue.retrieveValueFromStorage(_varDecl.getType(), true); } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, @@ -843,7 +844,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo } } -void ExpressionCompiler::LValue::retrieveValueFromStorage(std::shared_ptr const& _type, bool _remove) const +void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _type, bool _remove) const { if (!_type->isValueType()) return; // no distinction between value and reference for non-value types @@ -968,7 +969,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co } } -void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, std::shared_ptr const& _type) +void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type) { m_type = STORAGE; solAssert(_type->getStorageSize() <= numeric_limits::max(), "The storage size of " + _type->toString() + " should fit in an unsigned"); diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 8479344ad..b4a64594d 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -54,7 +54,7 @@ public: static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); /// Appends code for a State Variable accessor function - static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const* _varDecl, bool _optimize = false); + static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false); private: explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false): @@ -98,7 +98,7 @@ private: unsigned _memoryOffset = 0); /// Appends code for a State Variable accessor function - void appendStateVariableAccessor(VariableDeclaration const* _varDecl); + void appendStateVariableAccessor(VariableDeclaration const& _varDecl); /** * Helper class to store and retrieve lvalues to and from various locations. @@ -117,7 +117,7 @@ private: /// @a _expression is the current expression void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); /// Convenience function to set type for a state variable and retrieve the reference - void fromStateVariable(Declaration const& _varDecl, std::shared_ptr const& _type); + void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type); void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; } bool isValid() const { return m_type != NONE; } @@ -133,7 +133,7 @@ private: /// @a _expression is the current expression, used for error reporting. void retrieveValue(Expression const& _expression, bool _remove = false) const; /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue - void retrieveValueFromStorage(std::shared_ptr const& _type, bool _remove = false) const; + void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; /// Stores a value (from the stack directly beneath the reference, which is assumed to /// be on the top of the stack, if any) in the lvalue and removes the reference. /// Also removes the stored value from the stack if @a _move is From ae5a6a235c9d0996a712903e0cfd0560b326bbad Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 27 Jan 2015 14:32:59 +0100 Subject: [PATCH 17/25] Super keyword. --- CallGraph.cpp | 105 ---------------------------------- CallGraph.h | 69 ---------------------- Compiler.cpp | 126 +++++++++-------------------------------- Compiler.h | 8 +-- CompilerContext.cpp | 77 ++++++++++++++++++------- CompilerContext.h | 27 +++++---- CompilerStack.cpp | 1 + ExpressionCompiler.cpp | 24 +++++--- GlobalContext.cpp | 9 +++ GlobalContext.h | 2 + Types.cpp | 20 +++++-- Types.h | 13 +++-- 12 files changed, 153 insertions(+), 328 deletions(-) delete mode 100644 CallGraph.cpp delete mode 100644 CallGraph.h diff --git a/CallGraph.cpp b/CallGraph.cpp deleted file mode 100644 index 5f8fc5470..000000000 --- a/CallGraph.cpp +++ /dev/null @@ -1,105 +0,0 @@ - -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Callgraph of functions inside a contract. - */ - -#include -#include - -using namespace std; - -namespace dev -{ -namespace solidity -{ - -void CallGraph::addNode(ASTNode const& _node) -{ - if (!m_nodesSeen.count(&_node)) - { - m_workQueue.push(&_node); - m_nodesSeen.insert(&_node); - } -} - -set const& CallGraph::getCalls() -{ - computeCallGraph(); - return m_functionsSeen; -} - -void CallGraph::computeCallGraph() -{ - while (!m_workQueue.empty()) - { - m_workQueue.front()->accept(*this); - m_workQueue.pop(); - } -} - -bool CallGraph::visit(Identifier const& _identifier) -{ - if (auto fun = dynamic_cast(_identifier.getReferencedDeclaration())) - { - if (m_functionOverrideResolver) - fun = (*m_functionOverrideResolver)(fun->getName()); - solAssert(fun, "Error finding override for function " + fun->getName()); - addNode(*fun); - } - if (auto modifier = dynamic_cast(_identifier.getReferencedDeclaration())) - { - if (m_modifierOverrideResolver) - modifier = (*m_modifierOverrideResolver)(modifier->getName()); - solAssert(modifier, "Error finding override for modifier " + modifier->getName()); - addNode(*modifier); - } - return true; -} - -bool CallGraph::visit(FunctionDefinition const& _function) -{ - m_functionsSeen.insert(&_function); - return true; -} - -bool CallGraph::visit(MemberAccess const& _memberAccess) -{ - // used for "BaseContract.baseContractFunction" - if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE) - { - TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - if (type.getMembers().getMemberType(_memberAccess.getMemberName())) - { - ContractDefinition const& contract = dynamic_cast(*type.getActualType()) - .getContractDefinition(); - for (ASTPointer const& function: contract.getDefinedFunctions()) - if (function->getName() == _memberAccess.getMemberName()) - { - addNode(*function); - return true; - } - } - } - return true; -} - -} -} diff --git a/CallGraph.h b/CallGraph.h deleted file mode 100644 index 9af5cdf97..000000000 --- a/CallGraph.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . -*/ -/** - * @author Christian - * @date 2014 - * Callgraph of functions inside a contract. - */ - -#include -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ - -/** - * Can be used to compute the graph of calls (or rather references) between functions of the same - * contract. Current functionality is limited to computing all functions that are directly - * or indirectly called by some functions. - */ -class CallGraph: private ASTConstVisitor -{ -public: - using FunctionOverrideResolver = std::function; - using ModifierOverrideResolver = std::function; - - CallGraph(FunctionOverrideResolver const& _functionOverrideResolver, - ModifierOverrideResolver const& _modifierOverrideResolver): - m_functionOverrideResolver(&_functionOverrideResolver), - m_modifierOverrideResolver(&_modifierOverrideResolver) {} - - void addNode(ASTNode const& _node); - - std::set const& getCalls(); - -private: - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - - void computeCallGraph(); - - FunctionOverrideResolver const* m_functionOverrideResolver; - ModifierOverrideResolver const* m_modifierOverrideResolver; - std::set m_nodesSeen; - std::set m_functionsSeen; - std::queue m_workQueue; -}; - -} -} diff --git a/Compiler.cpp b/Compiler.cpp index c7656363a..93784adf2 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -28,7 +28,6 @@ #include #include #include -#include using namespace std; @@ -40,31 +39,13 @@ void Compiler::compileContract(ContractDefinition const& _contract, { m_context = CompilerContext(); // clear it just in case initializeContext(_contract, _contracts); - - for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) - { - for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor()) - m_context.addFunction(*function); - - for (ASTPointer const& vardecl: contract->getStateVariables()) - if (vardecl->isPublic()) - m_context.addFunction(*vardecl); - - for (ASTPointer const& modifier: contract->getFunctionModifiers()) - m_context.addModifier(*modifier); - } - appendFunctionSelector(_contract); - for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) + set functions = m_context.getFunctionsWithoutCode(); + while (!functions.empty()) { - for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor()) - function->accept(*this); - - for (ASTPointer const& vardecl: contract->getStateVariables()) - if (vardecl->isPublic()) - generateAccessorCode(*vardecl); + for (Declaration const* function: functions) + function->accept(*this); + functions = m_context.getFunctionsWithoutCode(); } // Swap the runtime context with the creation-time context @@ -77,72 +58,26 @@ void Compiler::initializeContext(ContractDefinition const& _contract, map const& _contracts) { m_context.setCompiledContracts(_contracts); + m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts()); registerStateVariables(_contract); } void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) { - std::vector const& bases = _contract.getLinearizedBaseContracts(); - - // Make all modifiers known to the context. - for (ContractDefinition const* contract: bases) - for (ASTPointer const& modifier: contract->getFunctionModifiers()) - m_context.addModifier(*modifier); - // arguments for base constructors, filled in derived-to-base order map> const*> baseArguments; - set neededFunctions; - set nodesUsedInConstructors; - // Determine the arguments that are used for the base constructors and also which functions - // are needed at compile time. + // Determine the arguments that are used for the base constructors. + std::vector const& bases = _contract.getLinearizedBaseContracts(); for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->getConstructor()) - nodesUsedInConstructors.insert(constructor); for (ASTPointer const& base: contract->getBaseContracts()) { ContractDefinition const* baseContract = dynamic_cast( base->getName()->getReferencedDeclaration()); solAssert(baseContract, ""); if (baseArguments.count(baseContract) == 0) - { baseArguments[baseContract] = &base->getArguments(); - for (ASTPointer const& arg: base->getArguments()) - nodesUsedInConstructors.insert(arg.get()); - } } - } - - auto functionOverrideResolver = [&](string const& _name) -> FunctionDefinition const* - { - for (ContractDefinition const* contract: bases) - for (ASTPointer const& function: contract->getDefinedFunctions()) - if (!function->isConstructor() && function->getName() == _name) - return function.get(); - return nullptr; - }; - auto modifierOverrideResolver = [&](string const& _name) -> ModifierDefinition const* - { - return &m_context.getFunctionModifier(_name); - }; - - neededFunctions = getFunctionsCalled(nodesUsedInConstructors, functionOverrideResolver, - modifierOverrideResolver); - - // First add all overrides (or the functions themselves if there is no override) - for (FunctionDefinition const* fun: neededFunctions) - { - FunctionDefinition const* override = nullptr; - if (!fun->isConstructor()) - override = functionOverrideResolver(fun->getName()); - if (!!override && neededFunctions.count(override)) - m_context.addFunction(*override); - } - // now add the rest - for (FunctionDefinition const* fun: neededFunctions) - if (fun->isConstructor() || functionOverrideResolver(fun->getName()) != fun) - m_context.addFunction(*fun); // Call constructors in base-to-derived order. // The Constructor for the most derived contract is called later. @@ -164,10 +99,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; m_context << u256(0) << eth::Instruction::RETURN; - // note that we have to explicitly include all used functions because of absolute jump - // labels - for (FunctionDefinition const* fun: neededFunctions) - fun->accept(*this); + // note that we have to include the functions again because of absolute jump labels + set functions = m_context.getFunctionsWithoutCode(); + while (!functions.empty()) + { + for (Declaration const* function: functions) + function->accept(*this); + functions = m_context.getFunctionsWithoutCode(); + } } void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor, @@ -201,16 +140,6 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) m_context << returnTag; } -set Compiler::getFunctionsCalled(set const& _nodes, - function const& _resolveFunctionOverrides, - function const& _resolveModifierOverrides) -{ - CallGraph callgraph(_resolveFunctionOverrides, _resolveModifierOverrides); - for (ASTNode const* node: _nodes) - callgraph.addNode(*node); - return callgraph.getCalls(); -} - void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { map, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions(); @@ -292,19 +221,22 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) m_context.addStateVariable(*variable); } -void Compiler::generateAccessorCode(VariableDeclaration const& _varDecl) +bool Compiler::visit(VariableDeclaration const& _variableDeclaration) { - m_context.startNewFunction(); + solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); + + m_context.startFunction(_variableDeclaration); m_breakTags.clear(); m_continueTags.clear(); - m_context << m_context.getFunctionEntryLabel(_varDecl); - ExpressionCompiler::appendStateVariableAccessor(m_context, _varDecl); + m_context << m_context.getFunctionEntryLabel(_variableDeclaration); + ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration); - unsigned sizeOnStack = _varDecl.getType()->getSizeOnStack(); - solAssert(sizeOnStack <= 15, "Illegal variable stack size detected"); - m_context << eth::dupInstruction(sizeOnStack + 1); - m_context << eth::Instruction::JUMP; + unsigned sizeOnStack = _variableDeclaration.getType()->getSizeOnStack(); + solAssert(sizeOnStack <= 15, "Stack too deep."); + m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP; + + return false; } bool Compiler::visit(FunctionDefinition const& _function) @@ -313,7 +245,7 @@ bool Compiler::visit(FunctionDefinition const& _function) // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // although note that this reduces the size of the visible stack - m_context.startNewFunction(); + m_context.startFunction(_function); m_returnTag = m_context.newTag(); m_breakTags.clear(); m_continueTags.clear(); @@ -321,8 +253,6 @@ bool Compiler::visit(FunctionDefinition const& _function) m_currentFunction = &_function; m_modifierDepth = 0; - m_context << m_context.getFunctionEntryLabel(_function); - // stack upon entry: [return address] [arg0] [arg1] ... [argn] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] diff --git a/Compiler.h b/Compiler.h index 144af8eb8..b3eae5b17 100644 --- a/Compiler.h +++ b/Compiler.h @@ -50,11 +50,6 @@ private: void appendBaseConstructorCall(FunctionDefinition const& _constructor, std::vector> const& _arguments); void appendConstructorCall(FunctionDefinition const& _constructor); - /// Recursively searches the call graph and returns all functions referenced inside _nodes. - /// _resolveOverride is called to resolve virtual function overrides. - std::set getFunctionsCalled(std::set const& _nodes, - std::function const& _resolveFunctionOverride, - std::function const& _resolveModifierOverride); void appendFunctionSelector(ContractDefinition const& _contract); /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. @@ -63,8 +58,7 @@ private: void registerStateVariables(ContractDefinition const& _contract); - void generateAccessorCode(VariableDeclaration const& _varDecl); - + virtual bool visit(VariableDeclaration const& _variableDeclaration) override; virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; diff --git a/CompilerContext.cpp b/CompilerContext.cpp index ea349c0d2..52910a556 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -43,6 +43,14 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) m_stateVariablesSize += _declaration.getType()->getStorageSize(); } +void CompilerContext::startFunction(Declaration const& _function) +{ + m_functionsWithCode.insert(&_function); + m_localVariables.clear(); + m_asm.setDeposit(0); + *this << getFunctionEntryLabel(_function); +} + void CompilerContext::addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent) { @@ -59,18 +67,6 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla *this << u256(0); } -void CompilerContext::addFunction(Declaration const& _decl) -{ - eth::AssemblyItem tag(m_asm.newTag()); - m_functionEntryLabels.insert(make_pair(&_decl, tag)); - m_virtualFunctionEntryLabels.insert(make_pair(_decl.getName(), tag)); -} - -void CompilerContext::addModifier(ModifierDefinition const& _modifier) -{ - m_functionModifiers.insert(make_pair(_modifier.getName(), &_modifier)); -} - bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const { auto ret = m_compiledContracts.find(&_contract); @@ -83,25 +79,62 @@ bool CompilerContext::isLocalVariable(Declaration const* _declaration) const return m_localVariables.count(_declaration); } -eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration) const +eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration) { auto res = m_functionEntryLabels.find(&_declaration); - solAssert(res != m_functionEntryLabels.end(), "Function entry label not found."); - return res->second.tag(); + if (res == m_functionEntryLabels.end()) + { + eth::AssemblyItem tag(m_asm.newTag()); + m_functionEntryLabels.insert(make_pair(&_declaration, tag)); + return tag.tag(); + } + else + return res->second.tag(); } -eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const +eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) { - auto res = m_virtualFunctionEntryLabels.find(_function.getName()); - solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found."); - return res->second.tag(); + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + for (ContractDefinition const* contract: m_inheritanceHierarchy) + for (ASTPointer const& function: contract->getDefinedFunctions()) + if (!function->isConstructor() && function->getName() == _function.getName()) + return getFunctionEntryLabel(*function); + solAssert(false, "Virtual function " + _function.getName() + " not found."); + return m_asm.newTag(); // not reached +} + +eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base) +{ + // search for first contract after _base + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base); + solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); + for (++it; it != m_inheritanceHierarchy.end(); ++it) + for (ASTPointer const& function: (*it)->getDefinedFunctions()) + if (!function->isConstructor() && function->getName() == _name) + return getFunctionEntryLabel(*function); + solAssert(false, "Super function " + _name + " not found."); + return m_asm.newTag(); // not reached +} + +set CompilerContext::getFunctionsWithoutCode() +{ + set functions; + for (auto const& it: m_functionEntryLabels) + if (m_functionsWithCode.count(it.first) == 0) + functions.insert(it.first); + return move(functions); } ModifierDefinition const& CompilerContext::getFunctionModifier(string const& _name) const { - auto res = m_functionModifiers.find(_name); - solAssert(res != m_functionModifiers.end(), "Function modifier override not found."); - return *res->second; + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + for (ContractDefinition const* contract: m_inheritanceHierarchy) + for (ASTPointer const& modifier: contract->getFunctionModifiers()) + if (modifier->getName() == _name) + return *modifier.get(); + BOOST_THROW_EXCEPTION(InternalCompilerError() + << errinfo_comment("Function modifier " + _name + " not found.")); } unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const diff --git a/CompilerContext.h b/CompilerContext.h index 9de3385a6..6d6a65b61 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -41,12 +41,8 @@ class CompilerContext public: void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration); - void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void addAndInitializeVariable(VariableDeclaration const& _declaration); - void addFunction(Declaration const& _decl); - /// Adds the given modifier to the list by name if the name is not present already. - void addModifier(ModifierDefinition const& _modifier); void setCompiledContracts(std::map const& _contracts) { m_compiledContracts = _contracts; } bytes const& getCompiledContract(ContractDefinition const& _contract) const; @@ -54,13 +50,22 @@ public: void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; } - bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration) != 0; } bool isLocalVariable(Declaration const* _declaration) const; bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; } - eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration) const; + eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration); + void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } /// @returns the entry label of the given function and takes overrides into account. - eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const; + eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function); + /// @returns the entry label of function with the given name from the most derived class just + /// above _base in the current inheritance hierarchy. + eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base); + /// @returns the set of functions for which we still need to generate code + std::set getFunctionsWithoutCode(); + /// Resets function specific members, inserts the function entry label and marks the function + /// as "having code". + void startFunction(Declaration const& _function); + ModifierDefinition const& getFunctionModifier(std::string const& _name) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const; @@ -119,10 +124,10 @@ private: std::map m_localVariables; /// Labels pointing to the entry points of functions. std::map m_functionEntryLabels; - /// Labels pointing to the entry points of function overrides. - std::map m_virtualFunctionEntryLabels; - /// Mapping to obtain function modifiers by name. Should be filled from derived to base. - std::map m_functionModifiers; + /// Set of functions for which we did not yet generate code. + std::set m_functionsWithCode; + /// List of current inheritance hierarchy from derived to base. + std::vector m_inheritanceHierarchy; }; } diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 0b8218bb3..3ed0d3620 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -94,6 +94,7 @@ void CompilerStack::parse() { m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->getCurrentThis()); + resolver.updateDeclaration(*m_globalContext->getCurrentSuper()); resolver.resolveNamesAndTypes(*contract); m_contracts[contract->getName()].contract = contract; } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 15ee17fd3..d2f709be0 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -366,14 +366,22 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::CONTRACT: { ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - u256 identifier = type.getFunctionIdentifier(member); - if (identifier != Invalid256) + if (type.isSuper()) { - appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); - m_context << identifier; + m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag(); break; } - // fall-through to "integer" otherwise (address) + else + { + u256 identifier = type.getFunctionIdentifier(member); + if (identifier != Invalid256) + { + appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); + m_context << identifier; + break; + } + // fall-through to "integer" otherwise (address) + } } case Type::Category::INTEGER: if (member == "balance") @@ -469,8 +477,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { - if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" - m_context << eth::Instruction::ADDRESS; + if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) + // "this" or "super" + if (!dynamic_cast(*magicVar->getType()).isSuper()) + m_context << eth::Instruction::ADDRESS; } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); diff --git a/GlobalContext.cpp b/GlobalContext.cpp index c7eea92dc..40a498c8e 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -83,5 +83,14 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const } +MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const +{ + if (!m_superPointer[m_currentContract]) + m_superPointer[m_currentContract] = make_shared( + "super", make_shared(*m_currentContract, true)); + return m_superPointer[m_currentContract].get(); + +} + } } diff --git a/GlobalContext.h b/GlobalContext.h index dfdc66623..f861c67d7 100644 --- a/GlobalContext.h +++ b/GlobalContext.h @@ -48,6 +48,7 @@ public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); MagicVariableDeclaration const* getCurrentThis() const; + MagicVariableDeclaration const* getCurrentSuper() const; /// @returns a vector of all implicit global declarations excluding "this". std::vector getDeclarations() const; @@ -56,6 +57,7 @@ private: std::vector> m_magicVariables; ContractDefinition const* m_currentContract = nullptr; std::map> mutable m_thisPointer; + std::map> mutable m_superPointer; }; } diff --git a/Types.cpp b/Types.cpp index fcb10d4b5..3d6c4e96c 100644 --- a/Types.cpp +++ b/Types.cpp @@ -450,7 +450,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.getCategory() == Category::CONTRACT) { auto const& bases = getContractDefinition().getLinearizedBaseContracts(); - return find(bases.begin(), bases.end(), + if (m_super && bases.size() <= 1) + return false; + return find(m_super ? ++bases.begin() : bases.begin(), bases.end(), &dynamic_cast(_convertTo).getContractDefinition()) != bases.end(); } return false; @@ -472,12 +474,12 @@ bool ContractType::operator==(Type const& _other) const if (_other.getCategory() != getCategory()) return false; ContractType const& other = dynamic_cast(_other); - return other.m_contract == m_contract; + return other.m_contract == m_contract && other.m_super == m_super; } string ContractType::toString() const { - return "contract " + m_contract.getName(); + return "contract " + string(m_super ? "super " : "") + m_contract.getName(); } MemberList const& ContractType::getMembers() const @@ -488,8 +490,16 @@ MemberList const& ContractType::getMembers() const // All address members and all interface functions map> members(IntegerType::AddressMemberList.begin(), IntegerType::AddressMemberList.end()); - for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second.getName()] = it.second.getFunctionTypeShared(); + if (m_super) + { + for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) + for (ASTPointer const& function: base->getDefinedFunctions()) + if (!function->isConstructor()) + members.insert(make_pair(function->getName(), make_shared(*function, true))); + } + else + for (auto const& it: m_contract.getInterfaceFunctions()) + members[it.second.getName()] = it.second.getFunctionTypeShared(); m_members.reset(new MemberList(members)); } return *m_members; diff --git a/Types.h b/Types.h index 2dbed95fa..3f6df13ee 100644 --- a/Types.h +++ b/Types.h @@ -277,7 +277,8 @@ class ContractType: public Type { public: virtual Category getCategory() const override { return Category::CONTRACT; } - ContractType(ContractDefinition const& _contract): m_contract(_contract) {} + explicit ContractType(ContractDefinition const& _contract, bool _super = false): + m_contract(_contract), m_super(_super) {} /// Contracts can be implicitly converted to super classes and to addresses. virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; /// Contracts can be converted to themselves and to integers. @@ -289,6 +290,7 @@ public: virtual MemberList const& getMembers() const override; + bool isSuper() const { return m_super; } ContractDefinition const& getContractDefinition() const { return m_contract; } /// Returns the function type of the constructor. Note that the location part of the function type @@ -301,6 +303,9 @@ public: private: ContractDefinition const& m_contract; + /// If true, it is the "super" type of the current contract, i.e. it contains only inherited + /// members. + bool m_super; /// Type of the constructor, @see getConstructorType. Lazily initialized. mutable std::shared_ptr m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. @@ -314,7 +319,7 @@ class StructType: public Type { public: virtual Category getCategory() const override { return Category::STRUCT; } - StructType(StructDefinition const& _struct): m_struct(_struct) {} + explicit StructType(StructDefinition const& _struct): m_struct(_struct) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(Type const& _other) const override; virtual u256 getStorageSize() const override; @@ -448,7 +453,7 @@ class TypeType: public Type { public: virtual Category getCategory() const override { return Category::TYPE; } - TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): + explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): m_actualType(_actualType), m_currentContract(_currentContract) {} TypePointer const& getActualType() const { return m_actualType; } @@ -502,7 +507,7 @@ public: enum class Kind { BLOCK, MSG, TX }; virtual Category getCategory() const override { return Category::MAGIC; } - MagicType(Kind _kind); + explicit MagicType(Kind _kind); virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { From 6ec9586de3ce96fafd3c9c7c6318e13f784e8d8f Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 27 Jan 2015 16:42:28 +0100 Subject: [PATCH 18/25] Style changes. --- ExpressionCompiler.cpp | 12 +++++++----- GlobalContext.cpp | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index d2f709be0..bcd90acfc 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -365,23 +365,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { case Type::Category::CONTRACT: { + bool alsoSearchInteger = false; ContractType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); if (type.isSuper()) - { m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag(); - break; - } else { + // ordinary contract type u256 identifier = type.getFunctionIdentifier(member); if (identifier != Invalid256) { appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); m_context << identifier; - break; } - // fall-through to "integer" otherwise (address) + else + // not found in contract, search in members inherited from address + alsoSearchInteger = true; } + if (!alsoSearchInteger) + break; } case Type::Category::INTEGER: if (member == "balance") diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 40a498c8e..687c9c9d4 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -89,7 +89,6 @@ MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const m_superPointer[m_currentContract] = make_shared( "super", make_shared(*m_currentContract, true)); return m_superPointer[m_currentContract].get(); - } } From 1d437b50833fbe17a1056fd85bfcbc6dc85abf52 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 01:29:43 +0100 Subject: [PATCH 19/25] Exclude standard contracts by default. --- CompilerStack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CompilerStack.h b/CompilerStack.h index cae0f4e27..7ad3405e1 100644 --- a/CompilerStack.h +++ b/CompilerStack.h @@ -60,7 +60,7 @@ class CompilerStack: boost::noncopyable { public: /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. - explicit CompilerStack(bool _addStandardSources = true); + explicit CompilerStack(bool _addStandardSources = false); /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// @returns true if a source object by the name already existed and was replaced. From 11d068bbbb065bc1f99b0fa3979f79aa4e7f2041 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 29 Jan 2015 12:17:37 +0100 Subject: [PATCH 20/25] Common class that contains AST node documentations. --- AST.h | 80 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/AST.h b/AST.h index f397c13ad..f3b18d392 100755 --- a/AST.h +++ b/AST.h @@ -157,6 +157,10 @@ private: }; + +/// Traits and Helpers (@todo: move to their own header) +/// @{ + /** * Generic Parameter description used by @see FunctionDescription to return * a descripton of its parameters. @@ -217,13 +221,44 @@ struct FunctionDescription std::pair, Declaration const*> m_description; }; +/** + * Abstract class that is added to each AST node that can store local variables. + */ +class VariableScope +{ +public: + void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } + std::vector const& getLocalVariables() const { return m_localVariables; } + +private: + std::vector m_localVariables; +}; + +/** + * Abstract class that is added to each AST node that can receive documentation. + */ +class Documented +{ +public: + explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + +/// @} + /** * Definition of a contract. This is the only AST nodes where child nodes are not visited in * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration +class ContractDefinition: public Declaration, public Documented { public: ContractDefinition(Location const& _location, @@ -234,13 +269,12 @@ public: std::vector> const& _stateVariables, std::vector> const& _definedFunctions, std::vector> const& _functionModifiers): - Declaration(_location, _name), + Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions), - m_functionModifiers(_functionModifiers), - m_documentation(_documentation) + m_functionModifiers(_functionModifiers) {} virtual void accept(ASTVisitor& _visitor) override; @@ -258,10 +292,6 @@ public: /// and calls checkTypeRequirements on all its functions. void checkTypeRequirements(); - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } - /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. std::map, FunctionDescription> getInterfaceFunctions() const; @@ -284,7 +314,6 @@ private: std::vector> m_stateVariables; std::vector> m_definedFunctions; std::vector> m_functionModifiers; - ASTPointer m_documentation; std::vector m_linearizedBaseContracts; mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; @@ -355,20 +384,7 @@ private: std::vector> m_parameters; }; -/** - * Abstract class that is added to each AST node that can store local variables. - */ -class VariableScope -{ -public: - void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } - std::vector const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector m_localVariables; -}; - -class FunctionDefinition: public Declaration, public VariableScope +class FunctionDefinition: public Declaration, public VariableScope, public Documented { public: FunctionDefinition(Location const& _location, ASTPointer const& _name, @@ -380,13 +396,13 @@ public: std::vector> const& _modifiers, ASTPointer const& _returnParameters, ASTPointer const& _body): - Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor), + Declaration(_location, _name), Documented(_documentation), + m_isPublic(_isPublic), m_isConstructor(_isConstructor), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_functionModifiers(_modifiers), m_returnParameters(_returnParameters), - m_body(_body), - m_documentation(_documentation) + m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; @@ -401,9 +417,6 @@ public: std::vector> const& getReturnParameters() const { return m_returnParameters->getParameters(); } ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block const& getBody() const { return *m_body; } - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } virtual TypePointer getType(ContractDefinition const*) const override; @@ -423,7 +436,6 @@ private: std::vector> m_functionModifiers; ASTPointer m_returnParameters; ASTPointer m_body; - ASTPointer m_documentation; }; /** @@ -463,7 +475,7 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public Declaration, public VariableScope +class ModifierDefinition: public Declaration, public VariableScope, public Documented { public: ModifierDefinition(Location const& _location, @@ -471,7 +483,7 @@ public: ASTPointer const& _documentation, ASTPointer const& _parameters, ASTPointer const& _body): - Declaration(_location, _name), m_documentation(_documentation), + Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; @@ -483,14 +495,10 @@ public: virtual TypePointer getType(ContractDefinition const* = nullptr) const override; - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } void checkTypeRequirements(); private: - ASTPointer m_documentation; ASTPointer m_parameters; ASTPointer m_body; }; From 71d8bde6d70a029e68b26b4d1798a6f41249dc66 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 10:44:29 +0100 Subject: [PATCH 21/25] No longer exposing retrieveValueFromStorage() as a public function - plus small fix in EndToEndTests --- BaseTypes.h | 4 ++++ ExpressionCompiler.cpp | 22 +++++++++++----------- ExpressionCompiler.h | 10 ++++++---- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/BaseTypes.h b/BaseTypes.h index a8fd77c86..057289ef3 100644 --- a/BaseTypes.h +++ b/BaseTypes.h @@ -41,6 +41,8 @@ struct Location start(_start), end(_end), sourceName(_sourceName) { } Location(): start(-1), end(-1) { } + bool isEmpty() const { return start == -1 && end == -1; } + int start; int end; std::shared_ptr sourceName; @@ -49,6 +51,8 @@ struct Location /// Stream output for Location (used e.g. in boost exceptions). inline std::ostream& operator<<(std::ostream& _out, Location const& _location) { + if (_location.isEmpty()) + return _out << "NO_LOCATION_SPECIFIED"; return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")"; } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index bcd90acfc..5d44c86f3 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -66,7 +66,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) { if (m_currentLValue.storesReferenceOnStack()) m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; - m_currentLValue.retrieveValue(_assignment, true); + m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); if (m_currentLValue.storesReferenceOnStack()) m_context << eth::Instruction::SWAP1; @@ -107,7 +107,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::INC: // ++ (pre- or postfix) case Token::DEC: // -- (pre- or postfix) solAssert(m_currentLValue.isValid(), "LValue not retrieved."); - m_currentLValue.retrieveValue(_unaryOperation); + m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation()); if (!_unaryOperation.isPrefixOperation()) { if (m_currentLValue.storesReferenceOnStack()) @@ -811,7 +811,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); solAssert(m_currentLValue.isInStorage(), ""); - m_currentLValue.retrieveValueFromStorage(_varDecl.getType(), true); + m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); } ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, @@ -826,7 +826,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType m_size = unsigned(_dataType.getSizeOnStack()); } -void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const +void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Location const& _location, bool _remove) const { switch (m_type) { @@ -834,23 +834,23 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo { unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); for (unsigned i = 0; i < m_size; ++i) *m_context << eth::dupInstruction(stackPos + 1); break; } case STORAGE: - retrieveValueFromStorage(_expression.getType(), _remove); + retrieveValueFromStorage(_type, _remove); break; case MEMORY: - if (!_expression.getType()->isValueType()) + if (!_type->isValueType()) break; // no distinction between value and reference for non-value types - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Location type not yet implemented.")); break; default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Unsupported location type.")); break; } @@ -889,7 +889,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool for (unsigned i = 0; i < m_size; ++i) *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; if (!_move) - retrieveValue(_expression); + retrieveValue(_expression.getType(), _expression.getLocation()); break; } case LValue::STORAGE: @@ -976,7 +976,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co { if (!_expression.lvalueRequested()) { - retrieveValue(_expression, true); + retrieveValue(_expression.getType(), _expression.getLocation(), true); reset(); } } diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index b4a64594d..748cc6c6f 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -130,10 +130,9 @@ private: /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// also removes the reference from the stack (note that is does not reset the type to @a NONE). - /// @a _expression is the current expression, used for error reporting. - void retrieveValue(Expression const& _expression, bool _remove = false) const; - /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue - void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; + /// @a _type is the type of the current expression and @ _location its location, used for error reporting. + /// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent + void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const; /// Stores a value (from the stack directly beneath the reference, which is assumed to /// be on the top of the stack, if any) in the lvalue and removes the reference. /// Also removes the stored value from the stack if @a _move is @@ -147,6 +146,9 @@ private: void retrieveValueIfLValueNotRequested(Expression const& _expression); private: + /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue + void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; + CompilerContext* m_context; LValueType m_type = NONE; /// If m_type is STACK, this is base stack offset (@see From 005100c4867f133e86a2675be0dd9370fed3e191 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 16:39:30 +0100 Subject: [PATCH 22/25] Contract Interface Functions now return FunctionType - Enchanced Function Type by declaration so that it can provide all the required information at each place interface functions are consumed - Changed all places where interface functions was used. - Simplified Mix's FunctionDefinition code --- AST.cpp | 21 ++++++++++---------- AST.h | 6 +++--- Compiler.cpp | 6 +++--- InterfaceHandler.cpp | 44 +++++++++++++++++++++++++----------------- Types.cpp | 46 +++++++++++++++++++++++++++++++++++++++----- Types.h | 15 ++++++++++++++- 6 files changed, 98 insertions(+), 40 deletions(-) diff --git a/AST.cpp b/AST.cpp index d95a254e9..bd333b91e 100644 --- a/AST.cpp +++ b/AST.cpp @@ -66,24 +66,25 @@ void ContractDefinition::checkTypeRequirements() // check for hash collisions in function signatures set> hashes; - for (auto const& hashAndFunction: getInterfaceFunctionList()) + for (auto const& it: getInterfaceFunctionList()) { - FixedHash<4> const& hash = std::get<0>(hashAndFunction); + FixedHash<4> const& hash = it.first; if (hashes.count(hash)) BOOST_THROW_EXCEPTION(createTypeError( std::string("Function signature hash collision for ") + - std::get<1>(hashAndFunction)->getCanonicalSignature(std::get<2>(hashAndFunction)->getName()))); + it.second->getCanonicalSignature())); hashes.insert(hash); } } -map, FunctionDescription> ContractDefinition::getInterfaceFunctions() const +map, std::shared_ptr> ContractDefinition::getInterfaceFunctions() const { auto exportedFunctionList = getInterfaceFunctionList(); - map, FunctionDescription> exportedFunctions; + map, std::shared_ptr> exportedFunctions; for (auto const& it: exportedFunctionList) - exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); + // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); + exportedFunctions.insert(it); solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -138,12 +139,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, std::shared_ptr, Declaration const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector, shared_ptr>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, std::shared_ptr, Declaration const*>>()); + m_interfaceFunctionList.reset(new vector, shared_ptr>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) @@ -151,7 +152,7 @@ vector, std::shared_ptr, Declaration cons { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); - m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*f, false), f.get())); + m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*f, false))); } for (ASTPointer const& v: contract->getStateVariables()) @@ -160,7 +161,7 @@ vector, std::shared_ptr, Declaration cons FunctionType ftype(*v); functionsSeen.insert(v->getName()); FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); - m_interfaceFunctionList->push_back(make_tuple(hash, make_shared(*v), v.get())); + m_interfaceFunctionList->push_back(make_pair(hash, make_shared(*v))); } } } diff --git a/AST.h b/AST.h index f3b18d392..e0fb14854 100755 --- a/AST.h +++ b/AST.h @@ -294,7 +294,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, FunctionDescription> getInterfaceFunctions() const; + std::map, std::shared_ptr> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -307,7 +307,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, std::shared_ptr, Declaration const*>> const& getInterfaceFunctionList() const; + std::vector, std::shared_ptr>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -316,7 +316,7 @@ private: std::vector> m_functionModifiers; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, std::shared_ptr, Declaration const*>>> m_interfaceFunctionList; + mutable std::unique_ptr, std::shared_ptr>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/Compiler.cpp b/Compiler.cpp index 93784adf2..33a71bca0 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -142,7 +142,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionDescription> interfaceFunctions = _contract.getInterfaceFunctions(); + map, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); map, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -160,11 +160,11 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionType const* functionType = it.second.getFunctionType(); + FunctionTypePointer functionType = it.second; m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second.getDeclaration())); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 9b6327782..dd569946a 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -45,23 +45,27 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto populateParameters = [](vector const& _params) + auto populateParameters = [](vector const& _paramNames, + vector const& _paramTypes) { Json::Value params(Json::arrayValue); - for (auto const& param: _params) + solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _paramNames.size(); ++i) { Json::Value input; - input["name"] = param.getName(); - input["type"] = param.getType(); + input["name"] = _paramNames[i]; + input["type"] = _paramTypes[i]; params.append(input); } return params; }; - method["name"] = it.second.getName(); - method["constant"] = it.second.isConstant(); - method["inputs"] = populateParameters(it.second.getParameters()); - method["outputs"] = populateParameters(it.second.getReturnParameters()); + method["name"] = it.second->getDeclaration()->getName(); + method["constant"] = it.second->isConstant(); + method["inputs"] = populateParameters(it.second->getParameterNames(), + it.second->getParameterTypeNames()); + method["outputs"] = populateParameters(it.second->getReturnParameterNames(), + it.second->getReturnParameterTypeNames()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); @@ -72,16 +76,20 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition string ret = "contract " + _contractDef.getName() + "{"; for (auto const& it: _contractDef.getInterfaceFunctions()) { - auto populateParameters = [](vector const& _params) + auto populateParameters = [](vector const& _paramNames, + vector const& _paramTypes) { string r = ""; - for (auto const& param: _params) - r += (r.size() ? "," : "(") + param.getType() + " " + param.getName(); + solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); + for (unsigned i = 0; i < _paramNames.size(); ++i) + r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; return r.size() ? r + ")" : "()"; }; - ret += "function " + it.second.getName() + populateParameters(it.second.getParameters()) + (it.second.isConstant() ? "constant " : ""); - if (it.second.getReturnParameters().size()) - ret += "returns" + populateParameters(it.second.getReturnParameters()); + ret += "function " + it.second->getDeclaration()->getName() + + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + + (it.second->isConstant() ? "constant " : ""); + if (it.second->getReturnParameterTypes().size()) + ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames()); else if (ret.back() == ' ') ret.pop_back(); ret += "{}"; @@ -97,7 +105,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value user; - auto strPtr = it.second.getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetUser(); @@ -105,7 +113,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); - methods[it.second.getSignature()] = user; + methods[it.second->getCanonicalSignature()] = user; } } } @@ -138,7 +146,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin for (auto const& it: _contractDef.getInterfaceFunctions()) { Json::Value method; - auto strPtr = it.second.getDocumentation(); + auto strPtr = it.second->getDocumentation(); if (strPtr) { resetDev(); @@ -161,7 +169,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin method["return"] = m_return; if (!method.empty()) // add the function, only if we have any documentation to add - methods[it.second.getSignature()] = method; + methods[it.second->getCanonicalSignature()] = method; } } doc["methods"] = methods; diff --git a/Types.cpp b/Types.cpp index 3d6c4e96c..e7765e1b7 100644 --- a/Types.cpp +++ b/Types.cpp @@ -499,7 +499,7 @@ MemberList const& ContractType::getMembers() const } else for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second.getName()] = it.second.getFunctionTypeShared(); + members[it.second->getDeclaration()->getName()] = it.second; m_members.reset(new MemberList(members)); } return *m_members; @@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second.getName() == _functionName) + if (it.second->getDeclaration()->getName() == _functionName) return FixedHash<4>::Arith(it.first); return Invalid256; @@ -589,12 +589,15 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const } FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): - m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL) + m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL), + m_isConstant(_function.isDeclaredConst()), + m_declaration(&_function) { TypePointers params; vector paramNames; TypePointers retParams; vector retParamNames; + params.reserve(_function.getParameters().size()); paramNames.reserve(_function.getParameters().size()); for (ASTPointer const& var: _function.getParameters()) @@ -616,7 +619,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal } FunctionType::FunctionType(VariableDeclaration const& _varDecl): - m_location(Location::EXTERNAL) + m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) { TypePointers params({}); vector paramNames({}); @@ -711,7 +714,13 @@ MemberList const& FunctionType::getMembers() const string FunctionType::getCanonicalSignature(std::string const& _name) const { - string ret = _name + "("; + std::string funcName = _name; + if (_name == "") + { + solAssert(m_declaration != nullptr, "Function type without name needs a declaration"); + funcName = m_declaration->getName(); + } + string ret = funcName + "("; for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it) ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ","); @@ -734,6 +743,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_gasSet || _setGas, m_valueSet || _setValue); } +vector const FunctionType::getParameterTypeNames() const +{ + vector names; + for (TypePointer const& t: m_parameterTypes) + names.push_back(t->toString()); + + return names; +} + +vector const FunctionType::getReturnParameterTypeNames() const +{ + vector names; + for (TypePointer const& t: m_returnParameterTypes) + names.push_back(t->toString()); + + return names; +} + +ASTPointer FunctionType::getDocumentation() const +{ + auto function = dynamic_cast(m_declaration); + if (function) + return function->getDocumentation(); + + return ASTPointer(); +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) diff --git a/Types.h b/Types.h index 3f6df13ee..e23939699 100644 --- a/Types.h +++ b/Types.h @@ -41,6 +41,7 @@ namespace solidity class Type; // forward class FunctionType; // forward using TypePointer = std::shared_ptr; +using FunctionTypePointer = std::shared_ptr; using TypePointers = std::vector; /** @@ -371,8 +372,10 @@ public: TypePointers const& getParameterTypes() const { return m_parameterTypes; } std::vector const& getParameterNames() const { return m_parameterNames; } + std::vector const getParameterTypeNames() const; TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } std::vector const& getReturnParameterNames() const { return m_returnParameterNames; } + std::vector const getReturnParameterTypeNames() const; virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; @@ -383,7 +386,15 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - std::string getCanonicalSignature(std::string const& _name) const; + /// @returns the canonical signature of this function type given the function name + /// If @a _name is not provided (empty string) then the @c m_declaration member of the + /// function type is used + std::string getCanonicalSignature(std::string const& _name = "") const; + Declaration const* getDeclaration() const { return m_declaration; } + bool isConstant() const { return m_isConstant; } + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer getDocumentation() const; bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -402,7 +413,9 @@ private: Location const m_location; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack + bool m_isConstant; mutable std::unique_ptr m_members; + Declaration const* m_declaration = nullptr; }; /** From dcd47be6ca8b408b384dca9737625873ad279464 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 16:48:39 +0100 Subject: [PATCH 23/25] Removing Function and Param Description - Removing FunctionDescription and ParamDescription. All the data should now be in the FunctionType - Plus using the FunctionTypePointer alias in a few places --- AST.cpp | 102 ++----------------------------------------------------- AST.h | 103 ++------------------------------------------------------ Types.h | 4 +-- 3 files changed, 7 insertions(+), 202 deletions(-) diff --git a/AST.cpp b/AST.cpp index bd333b91e..74d40da43 100644 --- a/AST.cpp +++ b/AST.cpp @@ -77,11 +77,11 @@ void ContractDefinition::checkTypeRequirements() } } -map, std::shared_ptr> ContractDefinition::getInterfaceFunctions() const +map, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const { auto exportedFunctionList = getInterfaceFunctionList(); - map, std::shared_ptr> exportedFunctions; + map, FunctionTypePointer> exportedFunctions; for (auto const& it: exportedFunctionList) // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); exportedFunctions.insert(it); @@ -520,103 +520,5 @@ void Literal::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); } -std::string const& ParamDescription::getName() const -{ - return m_description.first; -} - -std::string const& ParamDescription::getType() const -{ - return m_description.second; -} - -ASTPointer FunctionDescription::getDocumentation() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - return function->getDocumentation(); - - return ASTPointer(); -} - -string FunctionDescription::getSignature() const -{ - return m_description.first->getCanonicalSignature(m_description.second->getName()); -} - -string FunctionDescription::getName() const -{ - return m_description.second->getName(); -} - -bool FunctionDescription::isConstant() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - return function->isDeclaredConst(); - - return true; -} - -vector const FunctionDescription::getParameters() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - { - vector paramsDescription; - for (auto const& param: function->getParameters()) - paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); - - return paramsDescription; - } - - // else for now let's assume no parameters to accessors - // LTODO: fix this for mapping types - return {}; -} - -vector const FunctionDescription::getReturnParameters() const -{ - auto function = dynamic_cast(m_description.second); - if (function) - { - vector paramsDescription; - for (auto const& param: function->getReturnParameters()) - paramsDescription.push_back(ParamDescription(param->getName(), param->getType()->toString())); - - return paramsDescription; - } - - auto vardecl = dynamic_cast(m_description.second); - return {ParamDescription(vardecl->getName(), vardecl->getType()->toString())}; -} - -Declaration const* FunctionDescription::getDeclaration() const -{ - return m_description.second; -} - -VariableDeclaration const* FunctionDescription::getVariableDeclaration() const -{ - return dynamic_cast(m_description.second); -} - -FunctionDefinition const* FunctionDescription::getFunctionDefinition() const -{ - return dynamic_cast(m_description.second); -} - -shared_ptr FunctionDescription::getFunctionTypeShared() const -{ - return m_description.first; -} - - -FunctionType const* FunctionDescription::getFunctionType() const -{ - return m_description.first.get(); -} - - } } diff --git a/AST.h b/AST.h index e0fb14854..9ea6c144b 100755 --- a/AST.h +++ b/AST.h @@ -156,103 +156,6 @@ private: Declaration const* m_scope; }; - - -/// Traits and Helpers (@todo: move to their own header) -/// @{ - -/** - * Generic Parameter description used by @see FunctionDescription to return - * a descripton of its parameters. - */ -struct ParamDescription -{ - ParamDescription(std::string const& _name, std::string const& _type): - m_description(_name, _type){} - - std::string const& getName() const; - std::string const& getType() const; - - std::pair m_description; -}; - - -/** - * Generic function description able to describe both normal functions and - * functions that should be made as accessors to state variables - */ -struct FunctionDescription -{ - FunctionDescription(std::shared_ptr _type, Declaration const* _decl): - m_description(_type, _decl){} - - /// constructor for a constructor's function definition. Used only inside mix. - FunctionDescription(Declaration const* _def): - m_description(nullptr, _def){} - - FunctionDescription(): - m_description(nullptr, nullptr){} - - /// @returns the natspec documentation of the function if existing. Accessor (for now) don't have natspec doc - ASTPointer getDocumentation() const; - /// @returns the canonical signature of the function - std::string getSignature() const; - /// @returns the name of the function, basically that of the declaration - std::string getName() const; - /// @returns whether the function is constant. IF it's an accessor this is always true - bool isConstant() const; - /// @returns the argument parameters of the function - std::vector const getParameters() const; - /// @returns the return parameters of the function - std::vector const getReturnParameters() const; - /// @returns a generic Declaration AST Node pointer which can be either a FunctionDefinition or a VariableDeclaration - Declaration const* getDeclaration() const; - /// @returns the VariableDeclaration AST Node pointer or nullptr if it's not a VariableDeclaration - VariableDeclaration const* getVariableDeclaration() const; - /// @returns the FunctionDefinition AST Node pointer or nullptr if it's not a FunctionDefinition - FunctionDefinition const* getFunctionDefinition() const; - /// @returns a created shared pointer with the type of the function - std::shared_ptr makeFunctionType() const; - /// @returns a pointer to the function type - FunctionType const* getFunctionType() const; - /// @returns a shared pointer to the function type - std::shared_ptr getFunctionTypeShared() const; - - std::pair, Declaration const*> m_description; -}; - -/** - * Abstract class that is added to each AST node that can store local variables. - */ -class VariableScope -{ -public: - void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } - std::vector const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector m_localVariables; -}; - -/** - * Abstract class that is added to each AST node that can receive documentation. - */ -class Documented -{ -public: - explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} - - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() const { return m_documentation; } - -protected: - ASTPointer m_documentation; -}; - -/// @} - - /** * Definition of a contract. This is the only AST nodes where child nodes are not visited in * document order. It first visits all struct declarations, then all variable declarations and @@ -294,7 +197,7 @@ public: /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map, std::shared_ptr> getInterfaceFunctions() const; + std::map, FunctionTypePointer> getInterfaceFunctions() const; /// List of all (direct and indirect) base contracts in order from derived to base, including /// the contract itself. Available after name resolution @@ -307,7 +210,7 @@ public: private: void checkIllegalOverrides() const; - std::vector, std::shared_ptr>> const& getInterfaceFunctionList() const; + std::vector, FunctionTypePointer>> const& getInterfaceFunctionList() const; std::vector> m_baseContracts; std::vector> m_definedStructs; @@ -316,7 +219,7 @@ private: std::vector> m_functionModifiers; std::vector m_linearizedBaseContracts; - mutable std::unique_ptr, std::shared_ptr>>> m_interfaceFunctionList; + mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode diff --git a/Types.h b/Types.h index e23939699..b8ad7449e 100644 --- a/Types.h +++ b/Types.h @@ -296,7 +296,7 @@ public: /// Returns the function type of the constructor. Note that the location part of the function type /// is not used, as this type cannot be the type of a variable or expression. - std::shared_ptr const& getConstructorType() const; + FunctionTypePointer const& getConstructorType() const; /// @returns the identifier of the function with the given name or Invalid256 if such a name does /// not exist. @@ -308,7 +308,7 @@ private: /// members. bool m_super; /// Type of the constructor, @see getConstructorType. Lazily initialized. - mutable std::shared_ptr m_constructorType; + mutable FunctionTypePointer m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. mutable std::unique_ptr m_members; }; From 04190798eba7a2996b28aee6d74b1c636f575a02 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 17:28:14 +0100 Subject: [PATCH 24/25] Minor fixes plus a rebase merge fix --- AST.cpp | 5 ++--- AST.h | 31 +++++++++++++++++++++++++++++++ Compiler.cpp | 2 +- InterfaceHandler.cpp | 1 + Types.cpp | 7 +++++-- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/AST.cpp b/AST.cpp index 74d40da43..bc6be6008 100644 --- a/AST.cpp +++ b/AST.cpp @@ -83,7 +83,6 @@ map, FunctionTypePointer> ContractDefinition::getInterfaceFunctions map, FunctionTypePointer> exportedFunctions; for (auto const& it: exportedFunctionList) - // exportedFunctions.insert(make_pair(std::get<0>(it), FunctionDescription(std::get<1>(it), std::get<2>(it)))); exportedFunctions.insert(it); solAssert(exportedFunctionList.size() == exportedFunctions.size(), @@ -139,12 +138,12 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector, shared_ptr>> const& ContractDefinition::getInterfaceFunctionList() const +vector, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set functionsSeen; - m_interfaceFunctionList.reset(new vector, shared_ptr>>()); + m_interfaceFunctionList.reset(new vector, FunctionTypePointer>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer const& f: contract->getDefinedFunctions()) diff --git a/AST.h b/AST.h index 9ea6c144b..6c207290c 100755 --- a/AST.h +++ b/AST.h @@ -156,6 +156,37 @@ private: Declaration const* m_scope; }; +/** + * Abstract class that is added to each AST node that can store local variables. + */ +class VariableScope +{ +public: + void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } + std::vector const& getLocalVariables() const { return m_localVariables; } + +private: + std::vector m_localVariables; +}; + +/** + * Abstract class that is added to each AST node that can receive documentation. + */ +class Documented +{ +public: + explicit Documented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + +/// @} + /** * Definition of a contract. This is the only AST nodes where child nodes are not visited in * document order. It first visits all struct declarations, then all variable declarations and diff --git a/Compiler.cpp b/Compiler.cpp index 33a71bca0..79672ca10 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -160,7 +160,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionTypePointer functionType = it.second; + FunctionTypePointer const& functionType = it.second; m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index dd569946a..4d3ec4d50 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -60,6 +60,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio return params; }; + solAssert(it.second->getDeclaration(), "All function interface types should contain a declaration"); method["name"] = it.second->getDeclaration()->getName(); method["constant"] = it.second->isConstant(); method["inputs"] = populateParameters(it.second->getParameterNames(), diff --git a/Types.cpp b/Types.cpp index e7765e1b7..38caaf8ff 100644 --- a/Types.cpp +++ b/Types.cpp @@ -591,7 +591,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL), m_isConstant(_function.isDeclaredConst()), - m_declaration(&_function) + m_declaration(&_function) { TypePointers params; vector paramNames; @@ -641,6 +641,9 @@ bool FunctionType::operator==(Type const& _other) const if (m_location != other.m_location) return false; + if (m_isConstant != other.isConstant()) + return false; + if (m_parameterTypes.size() != other.m_parameterTypes.size() || m_returnParameterTypes.size() != other.m_returnParameterTypes.size()) return false; @@ -763,7 +766,7 @@ vector const FunctionType::getReturnParameterTypeNames() const ASTPointer FunctionType::getDocumentation() const { - auto function = dynamic_cast(m_declaration); + auto function = dynamic_cast(m_declaration); if (function) return function->getDocumentation(); From 3701543ae8dd8ffbfd58e5648d45699468f10a55 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 29 Jan 2015 18:44:14 +0100 Subject: [PATCH 25/25] FunctionType now returns const ref for Declaration --- Compiler.cpp | 2 +- InterfaceHandler.cpp | 6 ++---- Types.cpp | 4 ++-- Types.h | 7 ++++++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Compiler.cpp b/Compiler.cpp index 79672ca10..1fa31ce7d 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -164,7 +164,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) m_context << callDataUnpackerEntryPoints.at(it.first); eth::AssemblyItem returnTag = m_context.pushNewTag(); appendCalldataUnpacker(functionType->getParameterTypes()); - m_context.appendJumpTo(m_context.getFunctionEntryLabel(*it.second->getDeclaration())); + m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration())); m_context << returnTag; appendReturnValuePacker(functionType->getReturnParameterTypes()); } diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 4d3ec4d50..92cd51562 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -59,9 +59,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(ContractDefinitio } return params; }; - - solAssert(it.second->getDeclaration(), "All function interface types should contain a declaration"); - method["name"] = it.second->getDeclaration()->getName(); + method["name"] = it.second->getDeclaration().getName(); method["constant"] = it.second->isConstant(); method["inputs"] = populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()); @@ -86,7 +84,7 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i]; return r.size() ? r + ")" : "()"; }; - ret += "function " + it.second->getDeclaration()->getName() + + ret += "function " + it.second->getDeclaration().getName() + populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) + (it.second->isConstant() ? "constant " : ""); if (it.second->getReturnParameterTypes().size()) diff --git a/Types.cpp b/Types.cpp index 38caaf8ff..bebb4be17 100644 --- a/Types.cpp +++ b/Types.cpp @@ -499,7 +499,7 @@ MemberList const& ContractType::getMembers() const } else for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second->getDeclaration()->getName()] = it.second; + members[it.second->getDeclaration().getName()] = it.second; m_members.reset(new MemberList(members)); } return *m_members; @@ -522,7 +522,7 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const { auto interfaceFunctions = m_contract.getInterfaceFunctions(); for (auto const& it: m_contract.getInterfaceFunctions()) - if (it.second->getDeclaration()->getName() == _functionName) + if (it.second->getDeclaration().getName() == _functionName) return FixedHash<4>::Arith(it.first); return Invalid256; diff --git a/Types.h b/Types.h index b8ad7449e..4b4d17d0a 100644 --- a/Types.h +++ b/Types.h @@ -390,7 +390,12 @@ public: /// If @a _name is not provided (empty string) then the @c m_declaration member of the /// function type is used std::string getCanonicalSignature(std::string const& _name = "") const; - Declaration const* getDeclaration() const { return m_declaration; } + Declaration const& getDeclaration() const + { + solAssert(m_declaration, "Requested declaration from a FunctionType that has none"); + return *m_declaration; + } + bool hasDeclaration() const { return !!m_declaration; } bool isConstant() const { return m_isConstant; } /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation