diff options
author | Gav Wood <i@gavwood.com> | 2015-01-30 07:26:39 +0800 |
---|---|---|
committer | Gav Wood <i@gavwood.com> | 2015-01-30 07:26:39 +0800 |
commit | a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3 (patch) | |
tree | f529a8317ed06e9d7bcd23571cf8366f7b7278b1 | |
parent | 0d0c47f5889e7d9461c31a86beb88a3698d139a0 (diff) | |
parent | 3701543ae8dd8ffbfd58e5648d45699468f10a55 (diff) | |
download | dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.gz dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.bz2 dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.lz dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.xz dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.tar.zst dexon-solidity-a604202f33f1f7dc3abda1080e1bc02b2a2cbcb3.zip |
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
-rw-r--r-- | AST.cpp | 39 | ||||
-rwxr-xr-x | AST.h | 95 | ||||
-rw-r--r-- | BaseTypes.h | 4 | ||||
-rw-r--r-- | CallGraph.cpp | 105 | ||||
-rw-r--r-- | CallGraph.h | 69 | ||||
-rw-r--r-- | Compiler.cpp | 165 | ||||
-rw-r--r-- | Compiler.h | 14 | ||||
-rw-r--r-- | CompilerContext.cpp | 79 | ||||
-rw-r--r-- | CompilerContext.h | 31 | ||||
-rw-r--r-- | CompilerStack.cpp | 1 | ||||
-rw-r--r-- | CompilerStack.h | 2 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 107 | ||||
-rw-r--r-- | ExpressionCompiler.h | 15 | ||||
-rw-r--r-- | GlobalContext.cpp | 8 | ||||
-rw-r--r-- | GlobalContext.h | 2 | ||||
-rw-r--r-- | InterfaceHandler.cpp | 38 | ||||
-rw-r--r-- | Parser.cpp | 6 | ||||
-rw-r--r-- | Parser.h | 2 | ||||
-rw-r--r-- | Types.cpp | 98 | ||||
-rw-r--r-- | Types.h | 44 |
20 files changed, 469 insertions, 455 deletions
@@ -66,21 +66,25 @@ void ContractDefinition::checkTypeRequirements() // check for hash collisions in function signatures set<FixedHash<4>> hashes; - for (auto const& hashAndFunction: getInterfaceFunctionList()) + for (auto const& it: getInterfaceFunctionList()) { - FixedHash<4> const& hash = hashAndFunction.first; + FixedHash<4> const& hash = it.first; if (hashes.count(hash)) - BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " + - hashAndFunction.second->getCanonicalSignature())); + BOOST_THROW_EXCEPTION(createTypeError( + std::string("Function signature hash collision for ") + + it.second->getCanonicalSignature())); hashes.insert(hash); } } -map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const +map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const { - vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList(); - map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(), - exportedFunctionList.end()); + auto exportedFunctionList = getInterfaceFunctionList(); + + map<FixedHash<4>, FunctionTypePointer> exportedFunctions; + for (auto const& it: exportedFunctionList) + exportedFunctions.insert(it); + solAssert(exportedFunctionList.size() == exportedFunctions.size(), "Hash collision at Function Definition Hash calculation"); @@ -134,20 +138,31 @@ void ContractDefinition::checkIllegalOverrides() const } } -vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const +vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const { if (!m_interfaceFunctionList) { set<string> functionsSeen; - m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionDefinition const*>>()); + m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); for (ContractDefinition const* contract: getLinearizedBaseContracts()) + { for (ASTPointer<FunctionDefinition> 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_pair(hash, make_shared<FunctionType>(*f, false))); } + + for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) + if (v->isPublic() && functionsSeen.count(v->getName()) == 0) + { + FunctionType ftype(*v); + functionsSeen.insert(v->getName()); + FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName()))); + m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v))); + } + } } return *m_interfaceFunctionList; } @@ -219,7 +234,7 @@ void FunctionDefinition::checkTypeRequirements() string FunctionDefinition::getCanonicalSignature() const { - return getName() + FunctionType(*this).getCanonicalSignature(); + return FunctionType(*this).getCanonicalSignature(getName()); } Declaration::LValueType VariableDeclaration::getLValueType() const @@ -157,11 +157,42 @@ private: }; /** + * 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<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; } + +private: + std::vector<VariableDeclaration const*> m_localVariables; +}; + +/** + * Abstract class that is added to each AST node that can receive documentation. + */ +class Documented +{ +public: + explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } + +protected: + ASTPointer<ASTString> 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, @@ -172,13 +203,12 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, std::vector<ASTPointer<ModifierDefinition>> 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; @@ -196,13 +226,9 @@ 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<ASTString> const& getDocumentation() const { return m_documentation; } - /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. - std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const; + std::map<FixedHash<4>, 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 @@ -215,17 +241,16 @@ public: private: void checkIllegalOverrides() const; - std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> const& getInterfaceFunctionList() const; + std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const; std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts; std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; - ASTPointer<ASTString> m_documentation; std::vector<ContractDefinition const*> m_linearizedBaseContracts; - mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>>> m_interfaceFunctionList; + mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; }; class InheritanceSpecifier: public ASTNode @@ -293,20 +318,7 @@ private: std::vector<ASTPointer<VariableDeclaration>> 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<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; } - -private: - std::vector<VariableDeclaration const*> m_localVariables; -}; - -class FunctionDefinition: public Declaration, public VariableScope +class FunctionDefinition: public Declaration, public VariableScope, public Documented { public: FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, @@ -318,13 +330,13 @@ public: std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, ASTPointer<ParameterList> const& _returnParameters, ASTPointer<Block> 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; @@ -339,9 +351,6 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); } ASTPointer<ParameterList> 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<ASTString> const& getDocumentation() const { return m_documentation; } virtual TypePointer getType(ContractDefinition const*) const override; @@ -361,7 +370,6 @@ private: std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; ASTPointer<ParameterList> m_returnParameters; ASTPointer<Block> m_body; - ASTPointer<ASTString> m_documentation; }; /** @@ -372,8 +380,8 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, - ASTPointer<ASTString> const& _name): - Declaration(_location, _name), m_typeName(_type) {} + ASTPointer<ASTString> 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; @@ -385,9 +393,15 @@ public: void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } virtual LValueType getLValueType() const override; + bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } + bool isPublic() const { return m_isPublic; } + bool isStateVariable() const { return m_isStateVariable; } + private: - ASTPointer<TypeName> m_typeName; ///< can be empty ("var") + ASTPointer<TypeName> 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<Type const> m_type; ///< derived type, initially empty }; @@ -395,7 +409,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, @@ -403,7 +417,7 @@ public: ASTPointer<ASTString> const& _documentation, ASTPointer<ParameterList> const& _parameters, ASTPointer<Block> 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; @@ -415,14 +429,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<ASTString> const& getDocumentation() const { return m_documentation; } void checkTypeRequirements(); private: - ASTPointer<ASTString> m_documentation; ASTPointer<ParameterList> m_parameters; ASTPointer<Block> m_body; }; @@ -1076,5 +1086,6 @@ private: /// @} + } } diff --git a/BaseTypes.h b/BaseTypes.h index a8fd77c8..057289ef 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<std::string const> 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/CallGraph.cpp b/CallGraph.cpp deleted file mode 100644 index 5f8fc547..00000000 --- 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 <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2014 - * Callgraph of functions inside a contract. - */ - -#include <libsolidity/AST.h> -#include <libsolidity/CallGraph.h> - -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<FunctionDefinition const*> 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<FunctionDefinition const*>(_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<ModifierDefinition const*>(_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<TypeType const&>(*_memberAccess.getExpression().getType()); - if (type.getMembers().getMemberType(_memberAccess.getMemberName())) - { - ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType()) - .getContractDefinition(); - for (ASTPointer<FunctionDefinition> 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 9af5cdf9..00000000 --- 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 <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2014 - * Callgraph of functions inside a contract. - */ - -#include <set> -#include <queue> -#include <functional> -#include <boost/range/iterator_range.hpp> -#include <libsolidity/ASTVisitor.h> - -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<FunctionDefinition const*(std::string const&)>; - using ModifierOverrideResolver = std::function<ModifierDefinition const*(std::string const&)>; - - CallGraph(FunctionOverrideResolver const& _functionOverrideResolver, - ModifierOverrideResolver const& _modifierOverrideResolver): - m_functionOverrideResolver(&_functionOverrideResolver), - m_modifierOverrideResolver(&_modifierOverrideResolver) {} - - void addNode(ASTNode const& _node); - - std::set<FunctionDefinition const*> 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<ASTNode const*> m_nodesSeen; - std::set<FunctionDefinition const*> m_functionsSeen; - std::queue<ASTNode const*> m_workQueue; -}; - -} -} diff --git a/Compiler.cpp b/Compiler.cpp index 5190f93f..1fa31ce7 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -28,7 +28,6 @@ #include <libsolidity/Compiler.h> #include <libsolidity/ExpressionCompiler.h> #include <libsolidity/CompilerUtils.h> -#include <libsolidity/CallGraph.h> using namespace std; @@ -40,22 +39,15 @@ void Compiler::compileContract(ContractDefinition const& _contract, { m_context = CompilerContext(); // clear it just in case initializeContext(_contract, _contracts); - - for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) + appendFunctionSelector(_contract); + set<Declaration const*> functions = m_context.getFunctionsWithoutCode(); + while (!functions.empty()) { - for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions()) - if (!function->isConstructor()) - m_context.addFunction(*function); - for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers()) - m_context.addModifier(*modifier); + for (Declaration const* function: functions) + function->accept(*this); + functions = m_context.getFunctionsWithoutCode(); } - appendFunctionSelector(_contract); - for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts()) - for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions()) - if (!function->isConstructor()) - function->accept(*this); - // Swap the runtime context with the creation-time context swap(m_context, m_runtimeContext); initializeContext(_contract, _contracts); @@ -66,72 +58,26 @@ void Compiler::initializeContext(ContractDefinition const& _contract, map<ContractDefinition const*, bytes const*> const& _contracts) { m_context.setCompiledContracts(_contracts); + m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts()); registerStateVariables(_contract); } void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) { - std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); - - // Make all modifiers known to the context. - for (ContractDefinition const* contract: bases) - for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers()) - m_context.addModifier(*modifier); - // arguments for base constructors, filled in derived-to-base order map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments; - set<FunctionDefinition const*> neededFunctions; - set<ASTNode const*> 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<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts(); for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->getConstructor()) - nodesUsedInConstructors.insert(constructor); for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts()) { ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( base->getName()->getReferencedDeclaration()); solAssert(baseContract, ""); if (baseArguments.count(baseContract) == 0) - { baseArguments[baseContract] = &base->getArguments(); - for (ASTPointer<Expression> const& arg: base->getArguments()) - nodesUsedInConstructors.insert(arg.get()); - } } - } - - auto functionOverrideResolver = [&](string const& _name) -> FunctionDefinition const* - { - for (ContractDefinition const* contract: bases) - for (ASTPointer<FunctionDefinition> 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. @@ -153,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<Declaration const*> 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, @@ -177,31 +127,22 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor) unsigned argumentSize = 0; for (ASTPointer<VariableDeclaration> 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; } -set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes, - function<FunctionDefinition const*(string const&)> const& _resolveFunctionOverrides, - function<ModifierDefinition const*(string const&)> const& _resolveModifierOverrides) -{ - CallGraph callgraph(_resolveFunctionOverrides, _resolveModifierOverrides); - for (ASTNode const* node: _nodes) - callgraph.addNode(*node); - return callgraph.getCalls(); -} - void Compiler::appendFunctionSelector(ContractDefinition const& _contract) { - map<FixedHash<4>, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions(); + map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions(); map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints; // retrieve the function signature hash from the calldata @@ -209,7 +150,6 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) CompilerUtils(m_context).loadFromMemory(0, 4, false, true); // stack now is: 1 0 <funhash> - // 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 +160,28 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract) for (auto const& it: interfaceFunctions) { - FunctionDefinition const& function = *it.second; + FunctionTypePointer const& functionType = it.second; 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->getDeclaration())); 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<VariableDeclaration> 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 +189,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<ASTPointer<VariableDeclaration>> 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; @@ -282,13 +221,31 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) m_context.addStateVariable(*variable); } +bool Compiler::visit(VariableDeclaration const& _variableDeclaration) +{ + 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(_variableDeclaration); + ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration); + + 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) { //@todo to simplify this, the calling convention could by changed such that // 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(); @@ -296,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] @@ -50,19 +50,15 @@ private: void appendBaseConstructorCall(FunctionDefinition const& _constructor, std::vector<ASTPointer<Expression>> 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<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes, - std::function<FunctionDefinition const*(std::string const&)> const& _resolveFunctionOverride, - std::function<ModifierDefinition const*(std::string const&)> 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); + 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 ad1877ba..52910a55 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(FunctionDefinition const& _function) -{ - eth::AssemblyItem tag(m_asm.newTag()); - m_functionEntryLabels.insert(make_pair(&_function, tag)); - m_virtualFunctionEntryLabels.insert(make_pair(_function.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(FunctionDefinition const& _function) const +eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration) +{ + auto res = m_functionEntryLabels.find(&_declaration); + 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) +{ + solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); + for (ContractDefinition const* contract: m_inheritanceHierarchy) + for (ASTPointer<FunctionDefinition> 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) { - auto res = m_functionEntryLabels.find(&_function); - solAssert(res != m_functionEntryLabels.end(), "Function entry label not found."); - return res->second.tag(); + // 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<FunctionDefinition> 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 } -eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const +set<Declaration const*> CompilerContext::getFunctionsWithoutCode() { - auto res = m_virtualFunctionEntryLabels.find(_function.getName()); - solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found."); - return res->second.tag(); + set<Declaration const*> 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<ModifierDefinition> 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 d82dfe51..6d6a65b6 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(FunctionDefinition const& _function); - /// 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<ContractDefinition const*, bytes const*> 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(FunctionDefinition const& _function) const; + eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration); + void setInheritanceHierarchy(std::vector<ContractDefinition const*> 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<Declaration const*> 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; @@ -115,14 +120,14 @@ private: u256 m_stateVariablesSize = 0; /// Storage offsets of state variables std::map<Declaration const*, u256> m_stateVariables; - /// Positions of local variables on the stack. + /// Offsets of local variables on the stack (relative to stack base). std::map<Declaration const*, unsigned> m_localVariables; - /// Labels pointing to the entry points of funcitons. + /// Labels pointing to the entry points of functions. std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; - /// Labels pointing to the entry points of function overrides. - std::map<std::string, eth::AssemblyItem> m_virtualFunctionEntryLabels; - /// Mapping to obtain function modifiers by name. Should be filled from derived to base. - std::map<std::string, ModifierDefinition const*> m_functionModifiers; + /// Set of functions for which we did not yet generate code. + std::set<Declaration const*> m_functionsWithCode; + /// List of current inheritance hierarchy from derived to base. + std::vector<ContractDefinition const*> m_inheritanceHierarchy; }; } diff --git a/CompilerStack.cpp b/CompilerStack.cpp index 28687e90..0ec88ebd 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/CompilerStack.h b/CompilerStack.h index 66b05d42..a6c3df8e 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. diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index bd8c8653..5d44c86f 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); @@ -60,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; @@ -101,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()) @@ -359,15 +365,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { case Type::Category::CONTRACT: { + bool alsoSearchInteger = false; ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType()); - u256 identifier = type.getFunctionIdentifier(member); - if (identifier != Invalid256) + if (type.isSuper()) + m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag(); + else { - appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); - m_context << identifier; - break; + // ordinary contract type + u256 identifier = type.getFunctionIdentifier(member); + if (identifier != Invalid256) + { + appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true); + m_context << identifier; + } + else + // not found in contract, search in members inherited from address + alsoSearchInteger = true; } - // fall-through to "integer" otherwise (address) + if (!alsoSearchInteger) + break; } case Type::Category::INTEGER: if (member == "balance") @@ -463,8 +479,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) Declaration const* declaration = _identifier.getReferencedDeclaration(); if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(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<ContractType const&>(*magicVar->getType()).isSuper()) + m_context << eth::Instruction::ADDRESS; } else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); @@ -789,6 +807,13 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ return length; } +void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) +{ + m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType()); + solAssert(m_currentLValue.isInStorage(), ""); + m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true); +} + ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset): m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) @@ -801,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) { @@ -809,42 +834,47 @@ 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: - 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(_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; } } +void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _type, bool _remove) const +{ + if (!_type->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) @@ -859,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: @@ -946,11 +976,19 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co { if (!_expression.lvalueRequested()) { - retrieveValue(_expression, true); + retrieveValue(_expression.getType(), _expression.getLocation(), true); reset(); } } +void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type) +{ + m_type = STORAGE; + solAssert(_type->getStorageSize() <= numeric_limits<unsigned>::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 +999,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D } else if (m_context->isStateVariable(&_declaration)) { - m_type = STORAGE; - solAssert(_identifier.getType()->getStorageSize() <= numeric_limits<unsigned>::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 8f784fde..748cc6c6 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): @@ -95,6 +97,9 @@ private: unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> 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 +116,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, TypePointer const& _type); void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; } bool isValid() const { return m_type != NONE; } @@ -123,8 +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; + /// @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 @@ -138,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 diff --git a/GlobalContext.cpp b/GlobalContext.cpp index c7eea92d..687c9c9d 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -83,5 +83,13 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const } +MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const +{ + if (!m_superPointer[m_currentContract]) + m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( + "super", make_shared<ContractType>(*m_currentContract, true)); + return m_superPointer[m_currentContract].get(); +} + } } diff --git a/GlobalContext.h b/GlobalContext.h index dfdc6662..f861c67d 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<Declaration const*> getDeclarations() const; @@ -56,6 +57,7 @@ private: std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables; ContractDefinition const* m_currentContract = nullptr; std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer; + std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer; }; } diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index 1adce8cb..92cd5156 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -45,23 +45,26 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars) + auto populateParameters = [](vector<string> const& _paramNames, + vector<string> const& _paramTypes) { Json::Value params(Json::arrayValue); - for (ASTPointer<VariableDeclaration> const& var: _vars) + 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"] = var->getName(); - input["type"] = var->getType()->toString(); + input["name"] = _paramNames[i]; + input["type"] = _paramTypes[i]; 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->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<std::string>(new std::string(m_writer.write(methods))); @@ -72,17 +75,20 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition string ret = "contract " + _contractDef.getName() + "{"; for (auto const& it: _contractDef.getInterfaceFunctions()) { - FunctionDefinition const* f = it.second; - auto populateParameters = [](vector<ASTPointer<VariableDeclaration>> const& _vars) + auto populateParameters = [](vector<string> const& _paramNames, + vector<string> const& _paramTypes) { string r = ""; - for (ASTPointer<VariableDeclaration> const& var: _vars) - r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->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 " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : ""); - if (f->getReturnParameters().size()) - ret += "returns" + populateParameters(f->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 += "{}"; @@ -150,7 +150,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() Token::isElementaryTypeName(currentToken)) { bool const allowVar = false; - stateVariables.push_back(parseVariableDeclaration(allowVar)); + stateVariables.push_back(parseVariableDeclaration(allowVar, visibilityIsPublic, true)); expectToken(Token::SEMICOLON); } else if (currentToken == Token::MODIFIER) @@ -245,12 +245,12 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition() return nodeFactory.createNode<StructDefinition>(name, members); } -ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) +ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar, bool _isPublic, bool _isStateVariable) { ASTNodeFactory nodeFactory(*this); ASTPointer<TypeName> type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); - return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); + return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(), _isPublic, _isStateVariable); } ASTPointer<ModifierDefinition> Parser::parseModifierDefinition() @@ -52,7 +52,7 @@ private: ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); - ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar); + ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar, bool _isPublic = false, bool _isStateVar = false); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<Identifier> parseIdentifier(); @@ -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<ContractType const&>(_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<ContractType const&>(_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<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(), IntegerType::AddressMemberList.end()); - for (auto const& it: m_contract.getInterfaceFunctions()) - members[it.second->getName()] = make_shared<FunctionType>(*it.second, false); + if (m_super) + { + for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) + for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions()) + if (!function->isConstructor()) + members.insert(make_pair(function->getName(), make_shared<FunctionType>(*function, true))); + } + else + for (auto const& it: m_contract.getInterfaceFunctions()) + members[it.second->getDeclaration().getName()] = it.second; m_members.reset(new MemberList(members)); } return *m_members; @@ -511,9 +521,9 @@ shared_ptr<FunctionType const> 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->getName() == _functionName) - return FixedHash<4>::Arith(it->first); + for (auto const& it: m_contract.getInterfaceFunctions()) + if (it.second->getDeclaration().getName() == _functionName) + return FixedHash<4>::Arith(it.first); return Invalid256; } @@ -579,18 +589,48 @@ 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<string> paramNames; TypePointers retParams; + vector<string> retParamNames; + params.reserve(_function.getParameters().size()); + paramNames.reserve(_function.getParameters().size()); for (ASTPointer<VariableDeclaration> 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<VariableDeclaration> 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::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl) +{ + TypePointers params({}); + vector<string> paramNames({}); + TypePointers retParams({_varDecl.getType()}); + vector<string> retParamNames({ _varDecl.getName()}); + // for now, no input parameters LTODO: change for some things like mapping + swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); } bool FunctionType::operator==(Type const& _other) const @@ -601,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; @@ -672,9 +715,15 @@ MemberList const& FunctionType::getMembers() const } } -string FunctionType::getCanonicalSignature() const +string FunctionType::getCanonicalSignature(std::string const& _name) const { - string ret = "("; + 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() ? "" : ","); @@ -697,6 +746,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_gasSet || _setGas, m_valueSet || _setValue); } +vector<string> const FunctionType::getParameterTypeNames() const +{ + vector<string> names; + for (TypePointer const& t: m_parameterTypes) + names.push_back(t->toString()); + + return names; +} + +vector<string> const FunctionType::getReturnParameterTypeNames() const +{ + vector<string> names; + for (TypePointer const& t: m_returnParameterTypes) + names.push_back(t->toString()); + + return names; +} + +ASTPointer<ASTString> FunctionType::getDocumentation() const +{ + auto function = dynamic_cast<Documented const*>(m_declaration); + if (function) + return function->getDocumentation(); + + return ASTPointer<ASTString>(); +} + bool MappingType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -41,6 +41,7 @@ namespace solidity class Type; // forward class FunctionType; // forward using TypePointer = std::shared_ptr<Type const>; +using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; /** @@ -277,7 +278,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,11 +291,12 @@ 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 /// is not used, as this type cannot be the type of a variable or expression. - std::shared_ptr<FunctionType const> 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. @@ -301,8 +304,11 @@ 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<FunctionType const> m_constructorType; + mutable FunctionTypePointer m_constructorType; /// List of member types, will be lazy-initialized because of recursive references. mutable std::unique_ptr<MemberList> m_members; }; @@ -314,7 +320,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; @@ -353,6 +359,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), @@ -364,7 +371,11 @@ public: m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {} TypePointers const& getParameterTypes() const { return m_parameterTypes; } + std::vector<std::string> const& getParameterNames() const { return m_parameterNames; } + std::vector<std::string> const getParameterTypeNames() const; TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; } + std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; } + std::vector<std::string> const getReturnParameterTypeNames() const; virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; @@ -375,7 +386,20 @@ public: virtual MemberList const& getMembers() const override; Location const& getLocation() const { return m_location; } - std::string getCanonicalSignature() 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 + { + 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 + ASTPointer<ASTString> getDocumentation() const; bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -389,10 +413,14 @@ private: TypePointers m_parameterTypes; TypePointers m_returnParameterTypes; + std::vector<std::string> m_parameterNames; + std::vector<std::string> 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 + bool m_isConstant; mutable std::unique_ptr<MemberList> m_members; + Declaration const* m_declaration = nullptr; }; /** @@ -443,7 +471,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; } @@ -452,6 +480,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 +506,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; @@ -495,7 +525,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 { |