diff options
author | chriseth <c@ethdev.com> | 2015-10-21 06:21:52 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-10-21 06:46:01 +0800 |
commit | e3dffb611fe1736e3ffa170e6d8dc4dee17366bd (patch) | |
tree | b2df13e7c4c16c01b6cdc7cd5c15932031185d95 /libsolidity/ast | |
parent | d41f8b7ce702c3b25c48d27e2e895ccdcd04e4e0 (diff) | |
download | dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.gz dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.zst dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.zip |
File reorganisation.
Diffstat (limited to 'libsolidity/ast')
-rw-r--r-- | libsolidity/ast/AST.cpp | 365 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 1354 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.cpp | 28 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 140 | ||||
-rw-r--r-- | libsolidity/ast/ASTForward.h | 96 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 470 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 136 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 569 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.h | 145 | ||||
-rw-r--r-- | libsolidity/ast/ASTUtils.cpp | 48 | ||||
-rw-r--r-- | libsolidity/ast/ASTUtils.h | 54 | ||||
-rw-r--r-- | libsolidity/ast/ASTVisitor.h | 294 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 721 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 1921 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 996 |
15 files changed, 7337 insertions, 0 deletions
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp new file mode 100644 index 00000000..71d80a36 --- /dev/null +++ b/libsolidity/ast/AST.cpp @@ -0,0 +1,365 @@ +/* + 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 + * Solidity abstract syntax tree. + */ + +#include <algorithm> +#include <functional> +#include <libsolidity/interface/Utils.h> +#include <libsolidity/ast/AST.h> +#include <libsolidity/ast/ASTVisitor.h> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/ast/AST_accept.h> + +#include <libdevcore/SHA3.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +ASTNode::ASTNode(SourceLocation const& _location): + m_location(_location) +{ +} + +ASTNode::~ASTNode() +{ + delete m_annotation; +} + +ASTAnnotation& ASTNode::annotation() const +{ + if (!m_annotation) + m_annotation = new ASTAnnotation(); + return *m_annotation; +} + +Error ASTNode::createTypeError(string const& _description) const +{ + return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); +} + +map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const +{ + auto exportedFunctionList = interfaceFunctionList(); + + 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" + ); + + return exportedFunctions; +} + +FunctionDefinition const* ContractDefinition::constructor() const +{ + for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) + if (f->isConstructor()) + return f.get(); + return nullptr; +} + +FunctionDefinition const* ContractDefinition::fallbackFunction() const +{ + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) + for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) + if (f->name().empty()) + return f.get(); + return nullptr; +} + +vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const +{ + if (!m_interfaceEvents) + { + set<string> eventsSeen; + m_interfaceEvents.reset(new vector<ASTPointer<EventDefinition>>()); + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) + for (ASTPointer<EventDefinition> const& e: contract->events()) + if (eventsSeen.count(e->name()) == 0) + { + eventsSeen.insert(e->name()); + m_interfaceEvents->push_back(e); + } + } + return *m_interfaceEvents; +} + +vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList() const +{ + if (!m_interfaceFunctionList) + { + set<string> functionsSeen; + set<string> signaturesSeen; + m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) + { + vector<FunctionTypePointer> functions; + for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) + if (f->isPartOfExternalInterface()) + functions.push_back(make_shared<FunctionType>(*f, false)); + for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + functions.push_back(make_shared<FunctionType>(*v)); + for (FunctionTypePointer const& fun: functions) + { + if (!fun->interfaceFunctionType()) + // Fails hopefully because we already registered the error + continue; + string functionSignature = fun->externalSignature(); + if (signaturesSeen.count(functionSignature) == 0) + { + signaturesSeen.insert(functionSignature); + FixedHash<4> hash(dev::sha3(functionSignature)); + m_interfaceFunctionList->push_back(make_pair(hash, fun)); + } + } + } + } + return *m_interfaceFunctionList; +} + +string const& ContractDefinition::devDocumentation() const +{ + return m_devDocumentation; +} + +string const& ContractDefinition::userDocumentation() const +{ + return m_userDocumentation; +} + +void ContractDefinition::setDevDocumentation(string const& _devDocumentation) +{ + m_devDocumentation = _devDocumentation; +} + +void ContractDefinition::setUserDocumentation(string const& _userDocumentation) +{ + m_userDocumentation = _userDocumentation; +} + + +vector<Declaration const*> const& ContractDefinition::inheritableMembers() const +{ + if (!m_inheritableMembers) + { + set<string> memberSeen; + m_inheritableMembers.reset(new vector<Declaration const*>()); + auto addInheritableMember = [&](Declaration const* _decl) + { + if (memberSeen.count(_decl->name()) == 0 && _decl->isVisibleInDerivedContracts()) + { + memberSeen.insert(_decl->name()); + m_inheritableMembers->push_back(_decl); + } + }; + + for (ASTPointer<FunctionDefinition> const& f: definedFunctions()) + addInheritableMember(f.get()); + + for (ASTPointer<VariableDeclaration> const& v: stateVariables()) + addInheritableMember(v.get()); + + for (ASTPointer<StructDefinition> const& s: definedStructs()) + addInheritableMember(s.get()); + } + return *m_inheritableMembers; +} + +TypePointer ContractDefinition::type(ContractDefinition const* m_currentContract) const +{ + return make_shared<TypeType>(make_shared<ContractType>(*this), m_currentContract); +} + +ContractDefinitionAnnotation& ContractDefinition::annotation() const +{ + if (!m_annotation) + m_annotation = new ContractDefinitionAnnotation(); + return static_cast<ContractDefinitionAnnotation&>(*m_annotation); +} + +TypeNameAnnotation& TypeName::annotation() const +{ + if (!m_annotation) + m_annotation = new TypeNameAnnotation(); + return static_cast<TypeNameAnnotation&>(*m_annotation); +} + +TypePointer StructDefinition::type(ContractDefinition const*) const +{ + return make_shared<TypeType>(make_shared<StructType>(*this)); +} + +TypeDeclarationAnnotation& StructDefinition::annotation() const +{ + if (!m_annotation) + m_annotation = new TypeDeclarationAnnotation(); + return static_cast<TypeDeclarationAnnotation&>(*m_annotation); +} + +TypePointer EnumValue::type(ContractDefinition const*) const +{ + auto parentDef = dynamic_cast<EnumDefinition const*>(scope()); + solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); + return make_shared<EnumType>(*parentDef); +} + +TypePointer EnumDefinition::type(ContractDefinition const*) const +{ + return make_shared<TypeType>(make_shared<EnumType>(*this)); +} + +TypeDeclarationAnnotation& EnumDefinition::annotation() const +{ + if (!m_annotation) + m_annotation = new TypeDeclarationAnnotation(); + return static_cast<TypeDeclarationAnnotation&>(*m_annotation); +} + +TypePointer FunctionDefinition::type(ContractDefinition const*) const +{ + return make_shared<FunctionType>(*this); +} + +string FunctionDefinition::externalSignature() const +{ + return FunctionType(*this).externalSignature(); +} + +TypePointer ModifierDefinition::type(ContractDefinition const*) const +{ + return make_shared<ModifierType>(*this); +} + +TypePointer EventDefinition::type(ContractDefinition const*) const +{ + return make_shared<FunctionType>(*this); +} + +UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const +{ + if (!m_annotation) + m_annotation = new UserDefinedTypeNameAnnotation(); + return static_cast<UserDefinedTypeNameAnnotation&>(*m_annotation); +} + +bool VariableDeclaration::isLValue() const +{ + // External function parameters and constant declared variables are Read-Only + return !isExternalCallableParameter() && !m_isConstant; +} + +bool VariableDeclaration::isCallableParameter() const +{ + auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); + if (!callable) + return false; + for (auto const& variable: callable->parameters()) + if (variable.get() == this) + return true; + if (callable->returnParameterList()) + for (auto const& variable: callable->returnParameterList()->parameters()) + if (variable.get() == this) + return true; + return false; +} + +bool VariableDeclaration::isExternalCallableParameter() const +{ + auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); + if (!callable || callable->visibility() != Declaration::Visibility::External) + return false; + for (auto const& variable: callable->parameters()) + if (variable.get() == this) + return true; + return false; +} + +bool VariableDeclaration::canHaveAutoType() const +{ + auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); + return (!!callable && !isCallableParameter()); +} + +TypePointer VariableDeclaration::type(ContractDefinition const*) const +{ + return annotation().type; +} + +VariableDeclarationAnnotation& VariableDeclaration::annotation() const +{ + if (!m_annotation) + m_annotation = new VariableDeclarationAnnotation(); + return static_cast<VariableDeclarationAnnotation&>(*m_annotation); +} + +ReturnAnnotation& Return::annotation() const +{ + if (!m_annotation) + m_annotation = new ReturnAnnotation(); + return static_cast<ReturnAnnotation&>(*m_annotation); +} + +VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const +{ + if (!m_annotation) + m_annotation = new VariableDeclarationStatementAnnotation(); + return static_cast<VariableDeclarationStatementAnnotation&>(*m_annotation); +} + +ExpressionAnnotation& Expression::annotation() const +{ + if (!m_annotation) + m_annotation = new ExpressionAnnotation(); + return static_cast<ExpressionAnnotation&>(*m_annotation); +} + +MemberAccessAnnotation& MemberAccess::annotation() const +{ + if (!m_annotation) + m_annotation = new MemberAccessAnnotation(); + return static_cast<MemberAccessAnnotation&>(*m_annotation); +} + +BinaryOperationAnnotation& BinaryOperation::annotation() const +{ + if (!m_annotation) + m_annotation = new BinaryOperationAnnotation(); + return static_cast<BinaryOperationAnnotation&>(*m_annotation); +} + +FunctionCallAnnotation& FunctionCall::annotation() const +{ + if (!m_annotation) + m_annotation = new FunctionCallAnnotation(); + return static_cast<FunctionCallAnnotation&>(*m_annotation); +} + +IdentifierAnnotation& Identifier::annotation() const +{ + if (!m_annotation) + m_annotation = new IdentifierAnnotation(); + return static_cast<IdentifierAnnotation&>(*m_annotation); +} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h new file mode 100644 index 00000000..3fe447eb --- /dev/null +++ b/libsolidity/ast/AST.h @@ -0,0 +1,1354 @@ +/* + 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 + * Solidity abstract syntax tree. + */ + +#pragma once + + +#include <string> +#include <vector> +#include <memory> +#include <boost/noncopyable.hpp> +#include <libevmasm/SourceLocation.h> +#include <libsolidity/interface/Utils.h> +#include <libsolidity/ast/ASTForward.h> +#include <libsolidity/parsing/Token.h> +#include <libsolidity/ast/Types.h> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/ast/ASTAnnotations.h> + +namespace dev +{ +namespace solidity +{ + +class ASTVisitor; +class ASTConstVisitor; + + +/** + * The root (abstract) class of the AST inheritance tree. + * It is possible to traverse all direct and indirect children of an AST node by calling + * accept, providing an ASTVisitor. + */ +class ASTNode: private boost::noncopyable +{ +public: + explicit ASTNode(SourceLocation const& _location); + virtual ~ASTNode(); + + virtual void accept(ASTVisitor& _visitor) = 0; + virtual void accept(ASTConstVisitor& _visitor) const = 0; + template <class T> + static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) + { + for (ASTPointer<T>& element: _list) + element->accept(_visitor); + } + template <class T> + static void listAccept(std::vector<ASTPointer<T>> const& _list, ASTConstVisitor& _visitor) + { + for (ASTPointer<T> const& element: _list) + element->accept(_visitor); + } + + /// Returns the source code location of this node. + SourceLocation const& location() const { return m_location; } + + /// Creates a @ref TypeError exception and decorates it with the location of the node and + /// the given description + Error createTypeError(std::string const& _description) const; + + ///@todo make this const-safe by providing a different way to access the annotation + virtual ASTAnnotation& annotation() const; + + ///@{ + ///@name equality operators + /// Equality relies on the fact that nodes cannot be copied. + bool operator==(ASTNode const& _other) const { return this == &_other; } + bool operator!=(ASTNode const& _other) const { return !operator==(_other); } + ///@} + +protected: + /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). + mutable ASTAnnotation* m_annotation = nullptr; + +private: + SourceLocation m_location; +}; + +/** + * Source unit containing import directives and contract definitions. + */ +class SourceUnit: public ASTNode +{ +public: + SourceUnit(SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes): + ASTNode(_location), m_nodes(_nodes) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; } + +private: + std::vector<ASTPointer<ASTNode>> m_nodes; +}; + +/** + * Import directive for referencing other files / source objects. + * Example: import "abc.sol" + * Source objects are identified by a string which can be a file name but does not have to be. + */ +class ImportDirective: public ASTNode +{ +public: + ImportDirective(SourceLocation const& _location, ASTPointer<ASTString> const& _identifier): + ASTNode(_location), m_identifier(_identifier) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + ASTString const& identifier() const { return *m_identifier; } + +private: + ASTPointer<ASTString> m_identifier; +}; + +/** + * Abstract AST class for a declaration (contract, function, struct, variable). + */ +class Declaration: public ASTNode +{ +public: + /// Visibility ordered from restricted to unrestricted. + enum class Visibility { Default, Private, Internal, Public, External }; + + Declaration( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + Visibility _visibility = Visibility::Default + ): + ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} + + /// @returns the declared name. + ASTString const& name() const { return *m_name; } + Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; } + bool isPublic() const { return visibility() >= Visibility::Public; } + virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } + bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } + + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. + /// Available only after name and type resolution step. + Declaration const* scope() const { return m_scope; } + void setScope(Declaration const* _scope) { m_scope = _scope; } + + virtual bool isLValue() const { return false; } + virtual bool isPartOfExternalInterface() const { return false; } + + /// @returns the type of expressions referencing this declaration. + /// The current contract has to be given since this context can change the type, especially of + /// contract types. + /// This can only be called once types of variable declarations have already been resolved. + virtual TypePointer type(ContractDefinition const* m_currentContract = nullptr) const = 0; + +protected: + virtual Visibility defaultVisibility() const { return Visibility::Public; } + +private: + ASTPointer<ASTString> m_name; + Visibility m_visibility; + 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<VariableDeclaration const*> const& localVariables() 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& documentation() const { return m_documentation; } + +protected: + ASTPointer<ASTString> m_documentation; +}; + +/** + * Abstract class that is added to AST nodes that can be marked as not being fully implemented + */ +class ImplementationOptional +{ +public: + explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {} + + /// @return whether this node is fully implemented or not + bool isImplemented() const { return m_implemented; } + +protected: + bool m_implemented; +}; + +/// @} + +/** + * Definition of a contract or library. 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, public Documented +{ +public: + ContractDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + ASTPointer<ASTString> const& _documentation, + std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, + std::vector<ASTPointer<StructDefinition>> const& _definedStructs, + std::vector<ASTPointer<EnumDefinition>> const& _definedEnums, + std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, + std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions, + std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers, + std::vector<ASTPointer<EventDefinition>> const& _events, + bool _isLibrary + ): + Declaration(_location, _name), + Documented(_documentation), + m_baseContracts(_baseContracts), + m_definedStructs(_definedStructs), + m_definedEnums(_definedEnums), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions), + m_functionModifiers(_functionModifiers), + m_events(_events), + m_isLibrary(_isLibrary) + {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; } + std::vector<ASTPointer<StructDefinition>> const& definedStructs() const { return m_definedStructs; } + std::vector<ASTPointer<EnumDefinition>> const& definedEnums() const { return m_definedEnums; } + std::vector<ASTPointer<VariableDeclaration>> const& stateVariables() const { return m_stateVariables; } + std::vector<ASTPointer<ModifierDefinition>> const& functionModifiers() const { return m_functionModifiers; } + std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; } + std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; } + std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const; + bool isLibrary() const { return m_isLibrary; } + + /// @returns a map of canonical function signatures to FunctionDefinitions + /// as intended for use by the ABI. + std::map<FixedHash<4>, FunctionTypePointer> interfaceFunctions() const; + std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const; + + /// @returns a list of the inheritable members of this contract + std::vector<Declaration const*> const& inheritableMembers() const; + + /// Returns the constructor or nullptr if no constructor was specified. + FunctionDefinition const* constructor() const; + /// Returns the fallback function or nullptr if no fallback function was specified. + FunctionDefinition const* fallbackFunction() const; + + std::string const& userDocumentation() const; + void setUserDocumentation(std::string const& _userDocumentation); + + std::string const& devDocumentation() const; + void setDevDocumentation(std::string const& _devDocumentation); + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + + virtual ContractDefinitionAnnotation& annotation() const override; + +private: + std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts; + std::vector<ASTPointer<StructDefinition>> m_definedStructs; + std::vector<ASTPointer<EnumDefinition>> m_definedEnums; + std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; + std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; + std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers; + std::vector<ASTPointer<EventDefinition>> m_events; + bool m_isLibrary; + + // parsed Natspec documentation of the contract. + std::string m_userDocumentation; + std::string m_devDocumentation; + + std::vector<ContractDefinition const*> m_linearizedBaseContracts; + mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList; + mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents; + mutable std::unique_ptr<std::vector<Declaration const*>> m_inheritableMembers; +}; + +class InheritanceSpecifier: public ASTNode +{ +public: + InheritanceSpecifier( + SourceLocation const& _location, + ASTPointer<Identifier> const& _baseName, + std::vector<ASTPointer<Expression>> _arguments + ): + ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Identifier const& name() const { return *m_baseName; } + std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } + +private: + ASTPointer<Identifier> m_baseName; + std::vector<ASTPointer<Expression>> m_arguments; +}; + +class StructDefinition: public Declaration +{ +public: + StructDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + std::vector<ASTPointer<VariableDeclaration>> const& _members + ): + Declaration(_location, _name), m_members(_members) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; } + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + + virtual TypeDeclarationAnnotation& annotation() const override; + +private: + std::vector<ASTPointer<VariableDeclaration>> m_members; +}; + +class EnumDefinition: public Declaration +{ +public: + EnumDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + std::vector<ASTPointer<EnumValue>> const& _members + ): + Declaration(_location, _name), m_members(_members) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; } + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + + virtual TypeDeclarationAnnotation& annotation() const override; + +private: + std::vector<ASTPointer<EnumValue>> m_members; +}; + +/** + * Declaration of an Enum Value + */ +class EnumValue: public Declaration +{ +public: + EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name): + Declaration(_location, _name) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; +}; + +/** + * Parameter list, used as function parameter list and return list. + * None of the parameters is allowed to contain mappings (not even recursively + * inside structs). + */ +class ParameterList: public ASTNode +{ +public: + ParameterList( + SourceLocation const& _location, + std::vector<ASTPointer<VariableDeclaration>> const& _parameters + ): + ASTNode(_location), m_parameters(_parameters) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters; } + +private: + std::vector<ASTPointer<VariableDeclaration>> m_parameters; +}; + +/** + * Base class for all nodes that define function-like objects, i.e. FunctionDefinition, + * EventDefinition and ModifierDefinition. + */ +class CallableDeclaration: public Declaration, public VariableScope +{ +public: + CallableDeclaration( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + Declaration::Visibility _visibility, + ASTPointer<ParameterList> const& _parameters, + ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>() + ): + Declaration(_location, _name, _visibility), + m_parameters(_parameters), + m_returnParameters(_returnParameters) + { + } + + std::vector<ASTPointer<VariableDeclaration>> const& parameters() const { return m_parameters->parameters(); } + ParameterList const& parameterList() const { return *m_parameters; } + ASTPointer<ParameterList> const& returnParameterList() const { return m_returnParameters; } + +protected: + ASTPointer<ParameterList> m_parameters; + ASTPointer<ParameterList> m_returnParameters; +}; + +class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional +{ +public: + FunctionDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + Declaration::Visibility _visibility, + bool _isConstructor, + ASTPointer<ASTString> const& _documentation, + ASTPointer<ParameterList> const& _parameters, + bool _isDeclaredConst, + std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, + ASTPointer<ParameterList> const& _returnParameters, + ASTPointer<Block> const& _body + ): + CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), + Documented(_documentation), + ImplementationOptional(_body != nullptr), + m_isConstructor(_isConstructor), + m_isDeclaredConst(_isDeclaredConst), + m_functionModifiers(_modifiers), + m_body(_body) + {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + bool isConstructor() const { return m_isConstructor; } + bool isDeclaredConst() const { return m_isDeclaredConst; } + std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } + std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } + Block const& body() const { return *m_body; } + + virtual bool isVisibleInContract() const override + { + return Declaration::isVisibleInContract() && !isConstructor() && !name().empty(); + } + virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); } + + /// @returns the external signature of the function + /// That consists of the name of the function followed by the types of the + /// arguments separated by commas all enclosed in parentheses without any spaces. + std::string externalSignature() const; + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + +private: + bool m_isConstructor; + bool m_isDeclaredConst; + std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; + ASTPointer<Block> m_body; +}; + +/** + * Declaration of a variable. This can be used in various places, e.g. in function parameter + * lists, struct definitions and even function bodys. + */ +class VariableDeclaration: public Declaration +{ +public: + enum Location { Default, Storage, Memory }; + + VariableDeclaration( + SourceLocation const& _sourceLocation, + ASTPointer<TypeName> const& _type, + ASTPointer<ASTString> const& _name, + ASTPointer<Expression> _value, + Visibility _visibility, + bool _isStateVar = false, + bool _isIndexed = false, + bool _isConstant = false, + Location _referenceLocation = Location::Default + ): + Declaration(_sourceLocation, _name, _visibility), + m_typeName(_type), + m_value(_value), + m_isStateVariable(_isStateVar), + m_isIndexed(_isIndexed), + m_isConstant(_isConstant), + m_location(_referenceLocation) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + TypeName* typeName() const { return m_typeName.get(); } + ASTPointer<Expression> const& value() const { return m_value; } + + virtual bool isLValue() const override; + virtual bool isPartOfExternalInterface() const override { return isPublic(); } + + bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); } + /// @returns true if this variable is a parameter or return parameter of a function. + bool isCallableParameter() const; + /// @returns true if this variable is a parameter (not return parameter) of an external function. + bool isExternalCallableParameter() const; + /// @returns true if the type of the variable does not need to be specified, i.e. it is declared + /// in the body of a function or modifier. + bool canHaveAutoType() const; + bool isStateVariable() const { return m_isStateVariable; } + bool isIndexed() const { return m_isIndexed; } + bool isConstant() const { return m_isConstant; } + Location referenceLocation() const { return m_location; } + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + + virtual VariableDeclarationAnnotation& annotation() const override; + +protected: + Visibility defaultVisibility() const override { return Visibility::Internal; } + +private: + ASTPointer<TypeName> m_typeName; ///< can be empty ("var") + /// Initially assigned value, can be missing. For local variables, this is stored inside + /// VariableDeclarationStatement and not here. + ASTPointer<Expression> m_value; + bool m_isStateVariable; ///< Whether or not this is a contract state variable + bool m_isIndexed; ///< Whether this is an indexed variable (used by events). + bool m_isConstant; ///< Whether the variable is a compile-time constant. + Location m_location; ///< Location of the variable if it is of reference type. +}; + +/** + * Definition of a function modifier. + */ +class ModifierDefinition: public CallableDeclaration, public Documented +{ +public: + ModifierDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + ASTPointer<ASTString> const& _documentation, + ASTPointer<ParameterList> const& _parameters, + ASTPointer<Block> const& _body + ): + CallableDeclaration(_location, _name, Visibility::Default, _parameters), + Documented(_documentation), + m_body(_body) + { + } + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Block const& body() const { return *m_body; } + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + +private: + ASTPointer<Block> m_body; +}; + +/** + * Invocation/usage of a modifier in a function header or a base constructor call. + */ +class ModifierInvocation: public ASTNode +{ +public: + ModifierInvocation( + SourceLocation const& _location, + ASTPointer<Identifier> const& _name, + std::vector<ASTPointer<Expression>> _arguments + ): + ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + ASTPointer<Identifier> const& name() const { return m_modifierName; } + std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } + +private: + ASTPointer<Identifier> m_modifierName; + std::vector<ASTPointer<Expression>> m_arguments; +}; + +/** + * Definition of a (loggable) event. + */ +class EventDefinition: public CallableDeclaration, public Documented +{ +public: + EventDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + ASTPointer<ASTString> const& _documentation, + ASTPointer<ParameterList> const& _parameters, + bool _anonymous = false + ): + CallableDeclaration(_location, _name, Visibility::Default, _parameters), + Documented(_documentation), + m_anonymous(_anonymous) + { + } + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + bool isAnonymous() const { return m_anonymous; } + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + +private: + bool m_anonymous = false; +}; + +/** + * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global + * functions when such an identifier is encountered. Will never have a valid location in the source code. + */ +class MagicVariableDeclaration: public Declaration +{ +public: + MagicVariableDeclaration(ASTString const& _name, std::shared_ptr<Type const> const& _type): + Declaration(SourceLocation(), std::make_shared<ASTString>(_name)), m_type(_type) {} + virtual void accept(ASTVisitor&) override + { + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); + } + virtual void accept(ASTConstVisitor&) const override + { + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); + } + + virtual TypePointer type(ContractDefinition const*) const override { return m_type; } + +private: + std::shared_ptr<Type const> m_type; +}; + +/// Types +/// @{ + +/** + * Abstract base class of a type name, can be any built-in or user-defined type. + */ +class TypeName: public ASTNode +{ +public: + explicit TypeName(SourceLocation const& _location): ASTNode(_location) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + virtual TypeNameAnnotation& annotation() const override; +}; + +/** + * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings, + * contracts, functions, etc. + */ +class ElementaryTypeName: public TypeName +{ +public: + ElementaryTypeName(SourceLocation const& _location, Token::Value _type): + TypeName(_location), m_type(_type) + { + solAssert(Token::isElementaryTypeName(_type), ""); + } + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Token::Value typeName() const { return m_type; } + +private: + Token::Value m_type; +}; + +/** + * Name referring to a user-defined type (i.e. a struct, contract, etc.). + */ +class UserDefinedTypeName: public TypeName +{ +public: + UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath): + TypeName(_location), m_namePath(_namePath) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTString> const& namePath() const { return m_namePath; } + + virtual UserDefinedTypeNameAnnotation& annotation() const override; + +private: + std::vector<ASTString> m_namePath; +}; + +/** + * A mapping type. Its source form is "mapping('keyType' => 'valueType')" + */ +class Mapping: public TypeName +{ +public: + Mapping( + SourceLocation const& _location, + ASTPointer<ElementaryTypeName> const& _keyType, + ASTPointer<TypeName> const& _valueType + ): + TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + ElementaryTypeName const& keyType() const { return *m_keyType; } + TypeName const& valueType() const { return *m_valueType; } + +private: + ASTPointer<ElementaryTypeName> m_keyType; + ASTPointer<TypeName> m_valueType; +}; + +/** + * An array type, can be "typename[]" or "typename[<expression>]". + */ +class ArrayTypeName: public TypeName +{ +public: + ArrayTypeName( + SourceLocation const& _location, + ASTPointer<TypeName> const& _baseType, + ASTPointer<Expression> const& _length + ): + TypeName(_location), m_baseType(_baseType), m_length(_length) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + TypeName const& baseType() const { return *m_baseType; } + Expression const* length() const { return m_length.get(); } + +private: + ASTPointer<TypeName> m_baseType; + ASTPointer<Expression> m_length; ///< Length of the array, might be empty. +}; + +/// @} + +/// Statements +/// @{ + + +/** + * Abstract base class for statements. + */ +class Statement: public ASTNode +{ +public: + explicit Statement(SourceLocation const& _location): ASTNode(_location) {} +}; + +/** + * Brace-enclosed block containing zero or more statements. + */ +class Block: public Statement +{ +public: + Block( + SourceLocation const& _location, + std::vector<ASTPointer<Statement>> const& _statements + ): + Statement(_location), m_statements(_statements) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + +private: + std::vector<ASTPointer<Statement>> m_statements; +}; + +/** + * Special placeholder statement denoted by "_" used in function modifiers. This is replaced by + * the original function when the modifier is applied. + */ +class PlaceholderStatement: public Statement +{ +public: + explicit PlaceholderStatement(SourceLocation const& _location): Statement(_location) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; +}; + +/** + * If-statement with an optional "else" part. Note that "else if" is modeled by having a new + * if-statement as the false (else) body. + */ +class IfStatement: public Statement +{ +public: + IfStatement( + SourceLocation const& _location, + ASTPointer<Expression> const& _condition, + ASTPointer<Statement> const& _trueBody, + ASTPointer<Statement> const& _falseBody + ): + Statement(_location), + m_condition(_condition), + m_trueBody(_trueBody), + m_falseBody(_falseBody) + {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& condition() const { return *m_condition; } + Statement const& trueStatement() const { return *m_trueBody; } + /// @returns the "else" part of the if statement or nullptr if there is no "else" part. + Statement const* falseStatement() const { return m_falseBody.get(); } + +private: + ASTPointer<Expression> m_condition; + ASTPointer<Statement> m_trueBody; + ASTPointer<Statement> m_falseBody; ///< "else" part, optional +}; + +/** + * Statement in which a break statement is legal (abstract class). + */ +class BreakableStatement: public Statement +{ +public: + explicit BreakableStatement(SourceLocation const& _location): Statement(_location) {} +}; + +class WhileStatement: public BreakableStatement +{ +public: + WhileStatement( + SourceLocation const& _location, + ASTPointer<Expression> const& _condition, + ASTPointer<Statement> const& _body + ): + BreakableStatement(_location), m_condition(_condition), m_body(_body) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& condition() const { return *m_condition; } + Statement const& body() const { return *m_body; } + +private: + ASTPointer<Expression> m_condition; + ASTPointer<Statement> m_body; +}; + +/** + * For loop statement + */ +class ForStatement: public BreakableStatement +{ +public: + ForStatement( + SourceLocation const& _location, + ASTPointer<Statement> const& _initExpression, + ASTPointer<Expression> const& _conditionExpression, + ASTPointer<ExpressionStatement> const& _loopExpression, + ASTPointer<Statement> const& _body + ): + BreakableStatement(_location), + m_initExpression(_initExpression), + m_condExpression(_conditionExpression), + m_loopExpression(_loopExpression), + m_body(_body) + {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Statement const* initializationExpression() const { return m_initExpression.get(); } + Expression const* condition() const { return m_condExpression.get(); } + ExpressionStatement const* loopExpression() const { return m_loopExpression.get(); } + Statement const& body() const { return *m_body; } + +private: + /// For statement's initialization expresion. for(XXX; ; ). Can be empty + ASTPointer<Statement> m_initExpression; + /// For statement's condition expresion. for(; XXX ; ). Can be empty + ASTPointer<Expression> m_condExpression; + /// For statement's loop expresion. for(;;XXX). Can be empty + ASTPointer<ExpressionStatement> m_loopExpression; + /// The body of the loop + ASTPointer<Statement> m_body; +}; + +class Continue: public Statement +{ +public: + explicit Continue(SourceLocation const& _location): Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; +}; + +class Break: public Statement +{ +public: + explicit Break(SourceLocation const& _location): Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; +}; + +class Return: public Statement +{ +public: + Return(SourceLocation const& _location, ASTPointer<Expression> _expression): + Statement(_location), m_expression(_expression) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const* expression() const { return m_expression.get(); } + + virtual ReturnAnnotation& annotation() const override; + +private: + ASTPointer<Expression> m_expression; ///< value to return, optional +}; + +/** + * @brief The Throw statement to throw that triggers a solidity exception(jump to ErrorTag) + */ +class Throw: public Statement +{ +public: + explicit Throw(SourceLocation const& _location): Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; +}; + +/** + * Definition of a variable as a statement inside a function. It requires a type name (which can + * also be "var") but the actual assignment can be missing. + * Examples: var a = 2; uint256 a; + * As a second form, multiple variables can be declared, cannot have a type and must be assigned + * right away. If the first or last component is unnamed, it can "consume" an arbitrary number + * of components. + * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d(); + */ +class VariableDeclarationStatement: public Statement +{ +public: + VariableDeclarationStatement( + SourceLocation const& _location, + std::vector<ASTPointer<VariableDeclaration>> const& _variables, + ASTPointer<Expression> const& _initialValue + ): + Statement(_location), m_variables(_variables), m_initialValue(_initialValue) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + VariableDeclarationStatementAnnotation& annotation() const override; + + std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; } + Expression const* initialValue() const { return m_initialValue.get(); } + +private: + /// List of variables, some of which can be empty pointers (unnamed components). + std::vector<ASTPointer<VariableDeclaration>> m_variables; + /// The assigned expression / initial value. + ASTPointer<Expression> m_initialValue; +}; + +/** + * A statement that contains only an expression (i.e. an assignment, function call, ...). + */ +class ExpressionStatement: public Statement +{ +public: + ExpressionStatement( + SourceLocation const& _location, + ASTPointer<Expression> _expression + ): + Statement(_location), m_expression(_expression) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& expression() const { return *m_expression; } + +private: + ASTPointer<Expression> m_expression; +}; + +/// @} + +/// Expressions +/// @{ + +/** + * An expression, i.e. something that has a value (which can also be of type "void" in case + * of some function calls). + * @abstract + */ +class Expression: public ASTNode +{ +public: + explicit Expression(SourceLocation const& _location): ASTNode(_location) {} + + ExpressionAnnotation& annotation() const override; +}; + +/// Assignment, can also be a compound assignment. +/// Examples: (a = 7 + 8) or (a *= 2) +class Assignment: public Expression +{ +public: + Assignment( + SourceLocation const& _location, + ASTPointer<Expression> const& _leftHandSide, + Token::Value _assignmentOperator, + ASTPointer<Expression> const& _rightHandSide + ): + Expression(_location), + m_leftHandSide(_leftHandSide), + m_assigmentOperator(_assignmentOperator), + m_rightHandSide(_rightHandSide) + { + solAssert(Token::isAssignmentOp(_assignmentOperator), ""); + } + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& leftHandSide() const { return *m_leftHandSide; } + Token::Value assignmentOperator() const { return m_assigmentOperator; } + Expression const& rightHandSide() const { return *m_rightHandSide; } + +private: + ASTPointer<Expression> m_leftHandSide; + Token::Value m_assigmentOperator; + ASTPointer<Expression> m_rightHandSide; +}; + +/** + * Tuple or just parenthesized expression. + * Examples: (1, 2), (x,), (x), () + * Individual components might be empty shared pointers (as in the second example). + * The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple + * Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple. + */ +class TupleExpression: public Expression +{ +public: + TupleExpression( + SourceLocation const& _location, + std::vector<ASTPointer<Expression>> const& _components + ): + Expression(_location), m_components(_components) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<Expression>> const& components() const { return m_components; } + +private: + std::vector<ASTPointer<Expression>> m_components; +}; + +/** + * Operation involving a unary operator, pre- or postfix. + * Examples: ++i, delete x or !true + */ +class UnaryOperation: public Expression +{ +public: + UnaryOperation( + SourceLocation const& _location, + Token::Value _operator, + ASTPointer<Expression> const& _subExpression, + bool _isPrefix + ): + Expression(_location), + m_operator(_operator), + m_subExpression(_subExpression), + m_isPrefix(_isPrefix) + { + solAssert(Token::isUnaryOp(_operator), ""); + } + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Token::Value getOperator() const { return m_operator; } + bool isPrefixOperation() const { return m_isPrefix; } + Expression const& subExpression() const { return *m_subExpression; } + +private: + Token::Value m_operator; + ASTPointer<Expression> m_subExpression; + bool m_isPrefix; +}; + +/** + * Operation involving a binary operator. + * Examples: 1 + 2, true && false or 1 <= 4 + */ +class BinaryOperation: public Expression +{ +public: + BinaryOperation( + SourceLocation const& _location, + ASTPointer<Expression> const& _left, + Token::Value _operator, + ASTPointer<Expression> const& _right + ): + Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) + { + solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), ""); + } + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& leftExpression() const { return *m_left; } + Expression const& rightExpression() const { return *m_right; } + Token::Value getOperator() const { return m_operator; } + + BinaryOperationAnnotation& annotation() const override; + +private: + ASTPointer<Expression> m_left; + Token::Value m_operator; + ASTPointer<Expression> m_right; +}; + +/** + * Can be ordinary function call, type cast or struct construction. + */ +class FunctionCall: public Expression +{ +public: + FunctionCall( + SourceLocation const& _location, + ASTPointer<Expression> const& _expression, + std::vector<ASTPointer<Expression>> const& _arguments, + std::vector<ASTPointer<ASTString>> const& _names + ): + Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& expression() const { return *m_expression; } + std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; } + std::vector<ASTPointer<ASTString>> const& names() const { return m_names; } + + virtual FunctionCallAnnotation& annotation() const override; + +private: + ASTPointer<Expression> m_expression; + std::vector<ASTPointer<Expression>> m_arguments; + std::vector<ASTPointer<ASTString>> m_names; +}; + +/** + * Expression that creates a new contract, e.g. the "new SomeContract" part in "new SomeContract(1, 2)". + */ +class NewExpression: public Expression +{ +public: + NewExpression( + SourceLocation const& _location, + ASTPointer<Identifier> const& _contractName + ): + Expression(_location), m_contractName(_contractName) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Identifier const& contractName() const { return *m_contractName; } + +private: + ASTPointer<Identifier> m_contractName; +}; + +/** + * Access to a member of an object. Example: x.name + */ +class MemberAccess: public Expression +{ +public: + MemberAccess( + SourceLocation const& _location, + ASTPointer<Expression> _expression, + ASTPointer<ASTString> const& _memberName + ): + Expression(_location), m_expression(_expression), m_memberName(_memberName) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + Expression const& expression() const { return *m_expression; } + ASTString const& memberName() const { return *m_memberName; } + + virtual MemberAccessAnnotation& annotation() const override; + +private: + ASTPointer<Expression> m_expression; + ASTPointer<ASTString> m_memberName; +}; + +/** + * Index access to an array. Example: a[2] + */ +class IndexAccess: public Expression +{ +public: + IndexAccess( + SourceLocation const& _location, + ASTPointer<Expression> const& _base, + ASTPointer<Expression> const& _index + ): + Expression(_location), m_base(_base), m_index(_index) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Expression const& baseExpression() const { return *m_base; } + Expression const* indexExpression() const { return m_index.get(); } + +private: + ASTPointer<Expression> m_base; + ASTPointer<Expression> m_index; +}; + +/** + * Primary expression, i.e. an expression that cannot be divided any further. Examples are literals + * or variable references. + */ +class PrimaryExpression: public Expression +{ +public: + PrimaryExpression(SourceLocation const& _location): Expression(_location) {} +}; + +/** + * An identifier, i.e. a reference to a declaration by name like a variable or function. + */ +class Identifier: public PrimaryExpression +{ +public: + Identifier( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name + ): + PrimaryExpression(_location), m_name(_name) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + ASTString const& name() const { return *m_name; } + + virtual IdentifierAnnotation& annotation() const override; + +private: + ASTPointer<ASTString> m_name; +}; + +/** + * An elementary type name expression is used in expressions like "a = uint32(2)" to change the + * type of an expression explicitly. Here, "uint32" is the elementary type name expression and + * "uint32(2)" is a @ref FunctionCall. + */ +class ElementaryTypeNameExpression: public PrimaryExpression +{ +public: + ElementaryTypeNameExpression(SourceLocation const& _location, Token::Value _typeToken): + PrimaryExpression(_location), m_typeToken(_typeToken) + { + solAssert(Token::isElementaryTypeName(_typeToken), ""); + } + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Token::Value typeToken() const { return m_typeToken; } + +private: + Token::Value m_typeToken; +}; + +/** + * A literal string or number. @see ExpressionCompiler::endVisit() is used to actually parse its value. + */ +class Literal: public PrimaryExpression +{ +public: + enum class SubDenomination + { + None = Token::Illegal, + Wei = Token::SubWei, + Szabo = Token::SubSzabo, + Finney = Token::SubFinney, + Ether = Token::SubEther, + Second = Token::SubSecond, + Minute = Token::SubMinute, + Hour = Token::SubHour, + Day = Token::SubDay, + Week = Token::SubWeek, + Year = Token::SubYear + }; + Literal( + SourceLocation const& _location, + Token::Value _token, + ASTPointer<ASTString> const& _value, + SubDenomination _sub = SubDenomination::None + ): + PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Token::Value token() const { return m_token; } + /// @returns the non-parsed value of the literal + ASTString const& value() const { return *m_value; } + + SubDenomination subDenomination() const { return m_subDenomination; } + +private: + Token::Value m_token; + ASTPointer<ASTString> m_value; + SubDenomination m_subDenomination; +}; + +/// @} + + +} +} diff --git a/libsolidity/ast/ASTAnnotations.cpp b/libsolidity/ast/ASTAnnotations.cpp new file mode 100644 index 00000000..416e6b44 --- /dev/null +++ b/libsolidity/ast/ASTAnnotations.cpp @@ -0,0 +1,28 @@ +/* + 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 2015 + * Object containing the type and other annotations for the AST nodes. + */ + +#include <libsolidity/ast/ASTAnnotations.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h new file mode 100644 index 00000000..d112b1ef --- /dev/null +++ b/libsolidity/ast/ASTAnnotations.h @@ -0,0 +1,140 @@ +/* + 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 2015 + * Object containing the type and other annotations for the AST nodes. + */ + +#pragma once + +#include <map> +#include <memory> +#include <vector> +#include <set> +#include <libsolidity/ast/ASTForward.h> + +namespace dev +{ +namespace solidity +{ + +class Type; +using TypePointer = std::shared_ptr<Type const>; + +struct ASTAnnotation +{ + virtual ~ASTAnnotation() {} +}; + +struct TypeDeclarationAnnotation: ASTAnnotation +{ + /// The name of this type, prefixed by proper namespaces if globally accessible. + std::string canonicalName; +}; + +struct ContractDefinitionAnnotation: TypeDeclarationAnnotation +{ + /// Whether all functions are implemented. + bool isFullyImplemented = true; + /// List of all (direct and indirect) base contracts in order from derived to + /// base, including the contract itself. + std::vector<ContractDefinition const*> linearizedBaseContracts; + /// List of contracts this contract creates, i.e. which need to be compiled first. + /// Also includes all contracts from @a linearizedBaseContracts. + std::set<ContractDefinition const*> contractDependencies; +}; + +struct VariableDeclarationAnnotation: ASTAnnotation +{ + /// Type of variable (type of identifier referencing this variable). + TypePointer type; +}; + +struct ReturnAnnotation: ASTAnnotation +{ + /// Reference to the return parameters of the function. + ParameterList const* functionReturnParameters = nullptr; +}; + +struct TypeNameAnnotation: ASTAnnotation +{ + /// Type declared by this type name, i.e. type of a variable where this type name is used. + /// Set during reference resolution stage. + TypePointer type; +}; + +struct UserDefinedTypeNameAnnotation: TypeNameAnnotation +{ + /// Referenced declaration, set during reference resolution stage. + Declaration const* referencedDeclaration = nullptr; +}; + +struct VariableDeclarationStatementAnnotation: ASTAnnotation +{ + /// Information about which component of the value is assigned to which variable. + /// The pointer can be null to signify that the component is discarded. + std::vector<VariableDeclaration const*> assignments; +}; + +struct ExpressionAnnotation: ASTAnnotation +{ + /// Inferred type of the expression. + TypePointer type; + /// Whether it is an LValue (i.e. something that can be assigned to). + bool isLValue = false; + /// Whether the expression is used in a context where the LValue is actually required. + bool lValueRequested = false; + /// Types of arguments if the expression is a function that is called - used + /// for overload resolution. + std::shared_ptr<std::vector<TypePointer>> argumentTypes; +}; + +struct IdentifierAnnotation: ExpressionAnnotation +{ + /// Stores a reference to the current contract. + /// This is needed because types of base contracts change depending on the context. + ContractDefinition const* contractScope = nullptr; + /// Referenced declaration, set at latest during overload resolution stage. + Declaration const* referencedDeclaration = nullptr; + /// List of possible declarations it could refer to. + std::vector<Declaration const*> overloadedDeclarations; +}; + +struct MemberAccessAnnotation: ExpressionAnnotation +{ + /// Referenced declaration, set at latest during overload resolution stage. + Declaration const* referencedDeclaration = nullptr; +}; + +struct BinaryOperationAnnotation: ExpressionAnnotation +{ + /// The common type that is used for the operation, not necessarily the result type (which + /// e.g. for comparisons is bool). + TypePointer commonType; +}; + +struct FunctionCallAnnotation: ExpressionAnnotation +{ + /// Whether this is an explicit type conversion. + bool isTypeConversion = false; + /// Whether this is a struct constructor call. + bool isStructConstructorCall = false; +}; + +} +} diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h new file mode 100644 index 00000000..02dd054a --- /dev/null +++ b/libsolidity/ast/ASTForward.h @@ -0,0 +1,96 @@ +/* + 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 + * Forward-declarations of AST classes. + */ + +#pragma once + +#include <string> +#include <memory> +#include <vector> + +// Forward-declare all AST node types + +namespace dev +{ +namespace solidity +{ + +class ASTNode; +class SourceUnit; +class ImportDirective; +class Declaration; +class ContractDefinition; +class InheritanceSpecifier; +class StructDefinition; +class EnumDefinition; +class EnumValue; +class ParameterList; +class FunctionDefinition; +class VariableDeclaration; +class ModifierDefinition; +class ModifierInvocation; +class EventDefinition; +class MagicVariableDeclaration; +class TypeName; +class ElementaryTypeName; +class UserDefinedTypeName; +class Mapping; +class ArrayTypeName; +class Statement; +class Block; +class PlaceholderStatement; +class IfStatement; +class BreakableStatement; +class WhileStatement; +class ForStatement; +class Continue; +class Break; +class Return; +class Throw; +class VariableDeclarationStatement; +class ExpressionStatement; +class Expression; +class Assignment; +class TupleExpression; +class UnaryOperation; +class BinaryOperation; +class FunctionCall; +class NewExpression; +class MemberAccess; +class IndexAccess; +class PrimaryExpression; +class Identifier; +class ElementaryTypeNameExpression; +class Literal; + +class VariableScope; + +// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do +// not do reference counting but point to a special memory area that is completely released +// explicitly. +template <class T> +using ASTPointer = std::shared_ptr<T>; + +using ASTString = std::string; + + +} +} diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp new file mode 100644 index 00000000..f208c3c9 --- /dev/null +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -0,0 +1,470 @@ +/* + 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 Lefteris <lefteris@ethdev.com> + * @date 2015 + * Converts the AST into json format + */ + +#include <libsolidity/ast/ASTJsonConverter.h> +#include <boost/algorithm/string/join.hpp> +#include <libsolidity/ast/AST.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val) +{ + // special handling for booleans + if (_key == "const" || _key == "public" || _key == "local" || + _key == "lvalue" || _key == "local_lvalue" || _key == "prefix") + _obj[_key] = (_val == "1") ? true : false; + else + // else simply add it as a string + _obj[_key] = _val; +} + +void ASTJsonConverter::addJsonNode(string const& _nodeName, + initializer_list<pair<string const, string const>> _list, + bool _hasChildren = false) +{ + Json::Value node; + + node["name"] = _nodeName; + if (_list.size() != 0) + { + Json::Value attrs; + for (auto& e: _list) + addKeyValue(attrs, e.first, e.second); + node["attributes"] = attrs; + } + + m_jsonNodePtrs.top()->append(node); + + if (_hasChildren) + { + Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1]; + Json::Value children(Json::arrayValue); + addedNode["children"] = children; + m_jsonNodePtrs.push(&addedNode["children"]); + } +} + +ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast) +{ + Json::Value children(Json::arrayValue); + + m_astJson["name"] = "root"; + m_astJson["children"] = children; + m_jsonNodePtrs.push(&m_astJson["children"]); +} + +void ASTJsonConverter::print(ostream& _stream) +{ + process(); + _stream << m_astJson; +} + +Json::Value const& ASTJsonConverter::json() +{ + process(); + return m_astJson; +} + +bool ASTJsonConverter::visit(ImportDirective const& _node) +{ + addJsonNode("Import", { make_pair("file", _node.identifier())}); + return true; +} + +bool ASTJsonConverter::visit(ContractDefinition const& _node) +{ + addJsonNode("Contract", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(StructDefinition const& _node) +{ + addJsonNode("Struct", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(ParameterList const&) +{ + addJsonNode("ParameterList", {}, true); + return true; +} + +bool ASTJsonConverter::visit(FunctionDefinition const& _node) +{ + addJsonNode("Function", + { make_pair("name", _node.name()), + make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())), + make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) }, + true); + return true; +} + +bool ASTJsonConverter::visit(VariableDeclaration const& _node) +{ + addJsonNode("VariableDeclaration", { + make_pair("name", _node.name()), + make_pair("name", _node.name()), + }, true); + return true; +} + +bool ASTJsonConverter::visit(TypeName const&) +{ + return true; +} + +bool ASTJsonConverter::visit(ElementaryTypeName const& _node) +{ + addJsonNode("ElementaryTypeName", { make_pair("name", Token::toString(_node.typeName())) }); + return true; +} + +bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) +{ + addJsonNode("UserDefinedTypeName", { + make_pair("name", boost::algorithm::join(_node.namePath(), ".")) + }); + return true; +} + +bool ASTJsonConverter::visit(Mapping const&) +{ + addJsonNode("Mapping", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Block const&) +{ + addJsonNode("Block", {}, true); + return true; +} + +bool ASTJsonConverter::visit(IfStatement const&) +{ + addJsonNode("IfStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(WhileStatement const&) +{ + addJsonNode("WhileStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(ForStatement const&) +{ + addJsonNode("ForStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Continue const&) +{ + addJsonNode("Continue", {}); + return true; +} + +bool ASTJsonConverter::visit(Break const&) +{ + addJsonNode("Break", {}); + return true; +} + +bool ASTJsonConverter::visit(Return const&) +{ + addJsonNode("Return", {}, true);; + return true; +} + +bool ASTJsonConverter::visit(Throw const&) +{ + addJsonNode("Throw", {}, true);; + return true; +} + +bool ASTJsonConverter::visit(VariableDeclarationStatement const&) +{ + addJsonNode("VariableDefinition", {}, true); + return true; +} + +bool ASTJsonConverter::visit(ExpressionStatement const&) +{ + addJsonNode("ExpressionStatement", {}, true); + return true; +} + +bool ASTJsonConverter::visit(Assignment const& _node) +{ + addJsonNode("Assignment", + { make_pair("operator", Token::toString(_node.assignmentOperator())), + make_pair("type", type(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(TupleExpression const&) +{ + addJsonNode("TupleExpression",{}, true); + return true; +} + +bool ASTJsonConverter::visit(UnaryOperation const& _node) +{ + addJsonNode("UnaryOperation", + { make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())), + make_pair("operator", Token::toString(_node.getOperator())), + make_pair("type", type(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(BinaryOperation const& _node) +{ + addJsonNode("BinaryOperation", { + make_pair("operator", Token::toString(_node.getOperator())), + make_pair("type", type(_node)) + }, true); + return true; +} + +bool ASTJsonConverter::visit(FunctionCall const& _node) +{ + addJsonNode("FunctionCall", { + make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)), + make_pair("type", type(_node)) + }, true); + return true; +} + +bool ASTJsonConverter::visit(NewExpression const& _node) +{ + addJsonNode("NewExpression", { make_pair("type", type(_node)) }, true); + return true; +} + +bool ASTJsonConverter::visit(MemberAccess const& _node) +{ + addJsonNode("MemberAccess", + { make_pair("member_name", _node.memberName()), + make_pair("type", type(_node)) }, + true); + return true; +} + +bool ASTJsonConverter::visit(IndexAccess const& _node) +{ + addJsonNode("IndexAccess", { make_pair("type", type(_node)) }, true); + return true; +} + +bool ASTJsonConverter::visit(Identifier const& _node) +{ + addJsonNode("Identifier", + { make_pair("value", _node.name()), make_pair("type", type(_node)) }); + return true; +} + +bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) +{ + addJsonNode("ElementaryTypenameExpression", + { make_pair("value", Token::toString(_node.typeToken())), make_pair("type", type(_node)) }); + return true; +} + +bool ASTJsonConverter::visit(Literal const& _node) +{ + char const* tokenString = Token::toString(_node.token()); + addJsonNode("Literal", + { make_pair("string", (tokenString) ? tokenString : "null"), + make_pair("value", _node.value()), + make_pair("type", type(_node)) }); + return true; +} + +void ASTJsonConverter::endVisit(ImportDirective const&) +{ +} + +void ASTJsonConverter::endVisit(ContractDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(StructDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ParameterList const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(FunctionDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(VariableDeclaration const&) +{ +} + +void ASTJsonConverter::endVisit(TypeName const&) +{ +} + +void ASTJsonConverter::endVisit(ElementaryTypeName const&) +{ +} + +void ASTJsonConverter::endVisit(UserDefinedTypeName const&) +{ +} + +void ASTJsonConverter::endVisit(Mapping const&) +{ +} + +void ASTJsonConverter::endVisit(Block const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(IfStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(WhileStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ForStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Continue const&) +{ +} + +void ASTJsonConverter::endVisit(Break const&) +{ +} + +void ASTJsonConverter::endVisit(Return const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Throw const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(VariableDeclarationStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ExpressionStatement const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Assignment const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(TupleExpression const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(UnaryOperation const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(BinaryOperation const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(FunctionCall const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(NewExpression const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(MemberAccess const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(IndexAccess const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(Identifier const&) +{ +} + +void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&) +{ +} + +void ASTJsonConverter::endVisit(Literal const&) +{ +} + +void ASTJsonConverter::process() +{ + if (!processed) + m_ast->accept(*this); + processed = true; +} + +string ASTJsonConverter::type(Expression const& _expression) +{ + return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown"; +} + +string ASTJsonConverter::type(VariableDeclaration const& _varDecl) +{ + return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown"; +} + +} +} diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h new file mode 100644 index 00000000..de891cc6 --- /dev/null +++ b/libsolidity/ast/ASTJsonConverter.h @@ -0,0 +1,136 @@ +/* + 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 Lefteris <lefteris@ethdev.com> + * @date 2015 + * Converts the AST into json format + */ + +#pragma once + +#include <ostream> +#include <stack> +#include <libsolidity/ast/ASTVisitor.h> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/Utils.h> +#include <libsolidity/ast/ASTAnnotations.h> +#include <json/json.h> + +namespace dev +{ +namespace solidity +{ + +/** + * Converter of the AST into JSON format + */ +class ASTJsonConverter: public ASTConstVisitor +{ +public: + /// Create a converter to JSON for the given abstract syntax tree. + explicit ASTJsonConverter(ASTNode const& _ast); + /// Output the json representation of the AST to _stream. + void print(std::ostream& _stream); + Json::Value const& json(); + + bool visit(ImportDirective const& _node) override; + bool visit(ContractDefinition const& _node) override; + bool visit(StructDefinition const& _node) override; + bool visit(ParameterList const& _node) override; + bool visit(FunctionDefinition const& _node) override; + bool visit(VariableDeclaration const& _node) override; + bool visit(TypeName const& _node) override; + bool visit(ElementaryTypeName const& _node) override; + bool visit(UserDefinedTypeName const& _node) override; + bool visit(Mapping const& _node) override; + bool visit(Block const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; + bool visit(Continue const& _node) override; + bool visit(Break const& _node) override; + bool visit(Return const& _node) override; + bool visit(Throw const& _node) override; + bool visit(VariableDeclarationStatement const& _node) override; + bool visit(ExpressionStatement const& _node) override; + bool visit(Assignment const& _node) override; + bool visit(TupleExpression const& _node) override; + bool visit(UnaryOperation const& _node) override; + bool visit(BinaryOperation const& _node) override; + bool visit(FunctionCall const& _node) override; + bool visit(NewExpression const& _node) override; + bool visit(MemberAccess const& _node) override; + bool visit(IndexAccess const& _node) override; + bool visit(Identifier const& _node) override; + bool visit(ElementaryTypeNameExpression const& _node) override; + bool visit(Literal const& _node) override; + + void endVisit(ImportDirective const&) override; + void endVisit(ContractDefinition const&) override; + void endVisit(StructDefinition const&) override; + void endVisit(ParameterList const&) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(VariableDeclaration const&) override; + void endVisit(TypeName const&) override; + void endVisit(ElementaryTypeName const&) override; + void endVisit(UserDefinedTypeName const&) override; + void endVisit(Mapping const&) override; + void endVisit(Block const&) override; + void endVisit(IfStatement const&) override; + void endVisit(WhileStatement const&) override; + void endVisit(ForStatement const&) override; + void endVisit(Continue const&) override; + void endVisit(Break const&) override; + void endVisit(Return const&) override; + void endVisit(Throw const&) override; + void endVisit(VariableDeclarationStatement const&) override; + void endVisit(ExpressionStatement const&) override; + void endVisit(Assignment const&) override; + void endVisit(TupleExpression const&) override; + void endVisit(UnaryOperation const&) override; + void endVisit(BinaryOperation const&) override; + void endVisit(FunctionCall const&) override; + void endVisit(NewExpression const&) override; + void endVisit(MemberAccess const&) override; + void endVisit(IndexAccess const&) override; + void endVisit(Identifier const&) override; + void endVisit(ElementaryTypeNameExpression const&) override; + void endVisit(Literal const&) override; + +private: + void process(); + void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val); + void addJsonNode(std::string const& _nodeName, + std::initializer_list<std::pair<std::string const, std::string const>> _list, + bool _hasChildren); + std::string type(Expression const& _expression); + std::string type(VariableDeclaration const& _varDecl); + inline void goUp() + { + solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error."); + m_jsonNodePtrs.pop(); + } + + bool processed = false; + Json::Value m_astJson; + std::stack<Json::Value*> m_jsonNodePtrs; + std::string m_source; + ASTNode const* m_ast; +}; + +} +} diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp new file mode 100644 index 00000000..9253e0bf --- /dev/null +++ b/libsolidity/ast/ASTPrinter.cpp @@ -0,0 +1,569 @@ +/* + 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 + * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. + */ + +#include <libsolidity/ast/ASTPrinter.h> +#include <boost/algorithm/string/join.hpp> +#include <libsolidity/ast/AST.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +ASTPrinter::ASTPrinter( + ASTNode const& _ast, + string const& _source, + GasEstimator::ASTGasConsumption const& _gasCosts +): m_indentation(0), m_source(_source), m_ast(&_ast), m_gasCosts(_gasCosts) +{ +} + +void ASTPrinter::print(ostream& _stream) +{ + m_ostream = &_stream; + m_ast->accept(*this); + m_ostream = nullptr; +} + + +bool ASTPrinter::visit(ImportDirective const& _node) +{ + writeLine("ImportDirective \"" + _node.identifier() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ContractDefinition const& _node) +{ + writeLine("ContractDefinition \"" + _node.name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(InheritanceSpecifier const& _node) +{ + writeLine("InheritanceSpecifier \"" + _node.name().name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(StructDefinition const& _node) +{ + writeLine("StructDefinition \"" + _node.name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(EnumDefinition const& _node) +{ + writeLine("EnumDefinition \"" + _node.name() + "\""); + return goDeeper(); +} + +bool ASTPrinter::visit(EnumValue const& _node) +{ + writeLine("EnumValue \"" + _node.name() + "\""); + return goDeeper(); +} + +bool ASTPrinter::visit(ParameterList const& _node) +{ + writeLine("ParameterList"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(FunctionDefinition const& _node) +{ + writeLine("FunctionDefinition \"" + _node.name() + "\"" + + (_node.isPublic() ? " - public" : "") + + (_node.isDeclaredConst() ? " - const" : "")); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(VariableDeclaration const& _node) +{ + writeLine("VariableDeclaration \"" + _node.name() + "\""); + *m_ostream << indentation() << ( + _node.annotation().type ? + string(" Type: ") + _node.annotation().type->toString() : + string(" Type unknown.") + ) << "\n"; + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ModifierDefinition const& _node) +{ + writeLine("ModifierDefinition \"" + _node.name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ModifierInvocation const& _node) +{ + writeLine("ModifierInvocation \"" + _node.name()->name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(EventDefinition const& _node) +{ + writeLine("EventDefinition \"" + _node.name() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(TypeName const& _node) +{ + writeLine("TypeName"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ElementaryTypeName const& _node) +{ + writeLine(string("ElementaryTypeName ") + Token::toString(_node.typeName())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(UserDefinedTypeName const& _node) +{ + writeLine("UserDefinedTypeName \"" + boost::algorithm::join(_node.namePath(), ".") + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Mapping const& _node) +{ + writeLine("Mapping"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ArrayTypeName const& _node) +{ + writeLine("ArrayTypeName"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Block const& _node) +{ + writeLine("Block"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(PlaceholderStatement const& _node) +{ + writeLine("PlaceholderStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(IfStatement const& _node) +{ + writeLine("IfStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(WhileStatement const& _node) +{ + writeLine("WhileStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ForStatement const& _node) +{ + writeLine("ForStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Continue const& _node) +{ + writeLine("Continue"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Break const& _node) +{ + writeLine("Break"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Return const& _node) +{ + writeLine("Return"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Throw const& _node) +{ + writeLine("Throw"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(VariableDeclarationStatement const& _node) +{ + writeLine("VariableDeclarationStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ExpressionStatement const& _node) +{ + writeLine("ExpressionStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Assignment const& _node) +{ + writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator())); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(TupleExpression const& _node) +{ + writeLine(string("TupleExpression")); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(UnaryOperation const& _node) +{ + writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + + ") " + Token::toString(_node.getOperator())); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(BinaryOperation const& _node) +{ + writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(FunctionCall const& _node) +{ + writeLine("FunctionCall"); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(NewExpression const& _node) +{ + writeLine("NewExpression"); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(MemberAccess const& _node) +{ + writeLine("MemberAccess to member " + _node.memberName()); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(IndexAccess const& _node) +{ + writeLine("IndexAccess"); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Identifier const& _node) +{ + writeLine(string("Identifier ") + _node.name()); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node) +{ + writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.typeToken())); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Literal const& _node) +{ + char const* tokenString = Token::toString(_node.token()); + if (!tokenString) + tokenString = "[no token]"; + writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value()); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + +void ASTPrinter::endVisit(ImportDirective const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ContractDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(InheritanceSpecifier const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(StructDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(EnumDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(EnumValue const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ParameterList const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(FunctionDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(VariableDeclaration const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ModifierDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ModifierInvocation const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(EventDefinition const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(TypeName const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ElementaryTypeName const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(UserDefinedTypeName const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Mapping const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ArrayTypeName const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Block const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(PlaceholderStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(IfStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(WhileStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ForStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Continue const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Break const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Return const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Throw const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(VariableDeclarationStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ExpressionStatement const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Assignment const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(TupleExpression const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(UnaryOperation const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(BinaryOperation const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(FunctionCall const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(NewExpression const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(MemberAccess const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(IndexAccess const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Identifier const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ElementaryTypeNameExpression const&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Literal const&) +{ + m_indentation--; +} + +void ASTPrinter::printSourcePart(ASTNode const& _node) +{ + if (m_gasCosts.count(&_node)) + *m_ostream << indentation() << " Gas costs: " << m_gasCosts.at(&_node) << endl; + if (!m_source.empty()) + { + SourceLocation const& location(_node.location()); + *m_ostream << indentation() << " Source: " + << escaped(m_source.substr(location.start, location.end - location.start), false) << endl; + } +} + +void ASTPrinter::printType(Expression const& _expression) +{ + if (_expression.annotation().type) + *m_ostream << indentation() << " Type: " << _expression.annotation().type->toString() << "\n"; + else + *m_ostream << indentation() << " Type unknown.\n"; +} + +string ASTPrinter::indentation() const +{ + return string(m_indentation * 2, ' '); +} + +void ASTPrinter::writeLine(string const& _line) +{ + *m_ostream << indentation() << _line << endl; +} + +} +} diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h new file mode 100644 index 00000000..d9b5e252 --- /dev/null +++ b/libsolidity/ast/ASTPrinter.h @@ -0,0 +1,145 @@ +/* + 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 + * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. + */ + +#pragma once + +#include <ostream> +#include <libsolidity/ast/ASTVisitor.h> +#include <libsolidity/interface/GasEstimator.h> + +namespace dev +{ +namespace solidity +{ + +/** + * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes. + */ +class ASTPrinter: public ASTConstVisitor +{ +public: + /// Create a printer for the given abstract syntax tree. If the source is specified, + /// the corresponding parts of the source are printed with each node. + ASTPrinter( + ASTNode const& _ast, + std::string const& _source = std::string(), + GasEstimator::ASTGasConsumption const& _gasCosts = GasEstimator::ASTGasConsumption() + ); + /// Output the string representation of the AST to _stream. + void print(std::ostream& _stream); + + bool visit(ImportDirective const& _node) override; + bool visit(ContractDefinition const& _node) override; + bool visit(InheritanceSpecifier const& _node) override; + bool visit(StructDefinition const& _node) override; + bool visit(EnumDefinition const& _node) override; + bool visit(EnumValue const& _node) override; + bool visit(ParameterList const& _node) override; + bool visit(FunctionDefinition const& _node) override; + bool visit(VariableDeclaration const& _node) override; + bool visit(ModifierDefinition const& _node) override; + bool visit(ModifierInvocation const& _node) override; + bool visit(EventDefinition const& _node) override; + bool visit(TypeName const& _node) override; + bool visit(ElementaryTypeName const& _node) override; + bool visit(UserDefinedTypeName const& _node) override; + bool visit(Mapping const& _node) override; + bool visit(ArrayTypeName const& _node) override; + bool visit(Block const& _node) override; + bool visit(PlaceholderStatement const& _node) override; + bool visit(IfStatement const& _node) override; + bool visit(WhileStatement const& _node) override; + bool visit(ForStatement const& _node) override; + bool visit(Continue const& _node) override; + bool visit(Break const& _node) override; + bool visit(Return const& _node) override; + bool visit(Throw const& _node) override; + bool visit(VariableDeclarationStatement const& _node) override; + bool visit(ExpressionStatement const& _node) override; + bool visit(Assignment const& _node) override; + bool visit(TupleExpression const& _node) override; + bool visit(UnaryOperation const& _node) override; + bool visit(BinaryOperation const& _node) override; + bool visit(FunctionCall const& _node) override; + bool visit(NewExpression const& _node) override; + bool visit(MemberAccess const& _node) override; + bool visit(IndexAccess const& _node) override; + bool visit(Identifier const& _node) override; + bool visit(ElementaryTypeNameExpression const& _node) override; + bool visit(Literal const& _node) override; + + void endVisit(ImportDirective const&) override; + void endVisit(ContractDefinition const&) override; + void endVisit(InheritanceSpecifier const&) override; + void endVisit(StructDefinition const&) override; + void endVisit(EnumDefinition const&) override; + void endVisit(EnumValue const&) override; + void endVisit(ParameterList const&) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(VariableDeclaration const&) override; + void endVisit(ModifierDefinition const&) override; + void endVisit(ModifierInvocation const&) override; + void endVisit(EventDefinition const&) override; + void endVisit(TypeName const&) override; + void endVisit(ElementaryTypeName const&) override; + void endVisit(UserDefinedTypeName const&) override; + void endVisit(Mapping const&) override; + void endVisit(ArrayTypeName const&) override; + void endVisit(Block const&) override; + void endVisit(PlaceholderStatement const&) override; + void endVisit(IfStatement const&) override; + void endVisit(WhileStatement const&) override; + void endVisit(ForStatement const&) override; + void endVisit(Continue const&) override; + void endVisit(Break const&) override; + void endVisit(Return const&) override; + void endVisit(Throw const&) override; + void endVisit(VariableDeclarationStatement const&) override; + void endVisit(ExpressionStatement const&) override; + void endVisit(Assignment const&) override; + void endVisit(TupleExpression const&) override; + void endVisit(UnaryOperation const&) override; + void endVisit(BinaryOperation const&) override; + void endVisit(FunctionCall const&) override; + void endVisit(NewExpression const&) override; + void endVisit(MemberAccess const&) override; + void endVisit(IndexAccess const&) override; + void endVisit(Identifier const&) override; + void endVisit(ElementaryTypeNameExpression const&) override; + void endVisit(Literal const&) override; + +private: + void printSourcePart(ASTNode const& _node); + void printType(Expression const& _expression); + std::string indentation() const; + void writeLine(std::string const& _line); + bool goDeeper() { m_indentation++; return true; } + + int m_indentation; + std::string m_source; + ASTNode const* m_ast; + GasEstimator::ASTGasConsumption m_gasCosts; + std::ostream* m_ostream; +}; + +} +} diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp new file mode 100644 index 00000000..e9b70b62 --- /dev/null +++ b/libsolidity/ast/ASTUtils.cpp @@ -0,0 +1,48 @@ +/* + 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 2015 + * Utilities to work with the AST. + */ + +#include <libsolidity/ast/ASTUtils.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + + +ASTNode const* LocationFinder::leastUpperBound() +{ + m_bestMatch = nullptr; + for (ASTNode const* rootNode: m_rootNodes) + rootNode->accept(*this); + + return m_bestMatch; +} + +bool LocationFinder::visitNode(const ASTNode& _node) +{ + if (_node.location().contains(m_location)) + { + m_bestMatch = &_node; + return true; + } + return false; +} diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h new file mode 100644 index 00000000..237537ec --- /dev/null +++ b/libsolidity/ast/ASTUtils.h @@ -0,0 +1,54 @@ +/* + 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 2015 + * Utilities to work with the AST. + */ + +#pragma once + +#include <libevmasm/SourceLocation.h> +#include <libsolidity/ast/ASTVisitor.h> + +namespace dev +{ +namespace solidity +{ + +class LocationFinder: private ASTConstVisitor +{ +public: + LocationFinder(SourceLocation const& _location, std::vector<ASTNode const*> _rootNodes): + m_rootNodes(_rootNodes), m_location(_location) + { + } + + /// @returns the "closest" (in the sense of most-leafward) AST node which is a descendant of + /// _node and whose source location contains _location. + ASTNode const* leastUpperBound(); + +private: + bool visitNode(ASTNode const& _node); + + std::vector<ASTNode const*> m_rootNodes; + SourceLocation m_location; + ASTNode const* m_bestMatch = nullptr; +}; + +} +} diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h new file mode 100644 index 00000000..14c09fb4 --- /dev/null +++ b/libsolidity/ast/ASTVisitor.h @@ -0,0 +1,294 @@ +/* + 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 + * AST visitor base class. + */ + +#pragma once + +#include <string> +#include <functional> +#include <vector> +#include <libsolidity/ast/AST.h> + +namespace dev +{ +namespace solidity +{ + +/** + * Visitor interface for the abstract syntax tree. This class is tightly bound to the + * implementation of @ref ASTNode::accept and its overrides. After a call to + * @ref ASTNode::accept, the function visit for the appropriate parameter is called and then + * (if it returns true) this continues recursively for all child nodes in document order + * (there is an exception for contracts). After all child nodes have been visited, endVisit is + * called for the node. + */ +class ASTVisitor +{ +public: + virtual bool visit(SourceUnit& _node) { return visitNode(_node); } + virtual bool visit(ImportDirective& _node) { return visitNode(_node); } + virtual bool visit(ContractDefinition& _node) { return visitNode(_node); } + virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); } + virtual bool visit(StructDefinition& _node) { return visitNode(_node); } + virtual bool visit(EnumDefinition& _node) { return visitNode(_node); } + virtual bool visit(EnumValue& _node) { return visitNode(_node); } + virtual bool visit(ParameterList& _node) { return visitNode(_node); } + virtual bool visit(FunctionDefinition& _node) { return visitNode(_node); } + virtual bool visit(VariableDeclaration& _node) { return visitNode(_node); } + virtual bool visit(ModifierDefinition& _node) { return visitNode(_node); } + virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); } + virtual bool visit(EventDefinition& _node) { return visitNode(_node); } + virtual bool visit(TypeName& _node) { return visitNode(_node); } + virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); } + virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } + virtual bool visit(Mapping& _node) { return visitNode(_node); } + virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); } + virtual bool visit(Block& _node) { return visitNode(_node); } + virtual bool visit(PlaceholderStatement& _node) { return visitNode(_node); } + virtual bool visit(IfStatement& _node) { return visitNode(_node); } + virtual bool visit(WhileStatement& _node) { return visitNode(_node); } + virtual bool visit(ForStatement& _node) { return visitNode(_node); } + virtual bool visit(Continue& _node) { return visitNode(_node); } + virtual bool visit(Break& _node) { return visitNode(_node); } + virtual bool visit(Return& _node) { return visitNode(_node); } + virtual bool visit(Throw& _node) { return visitNode(_node); } + virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } + virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } + virtual bool visit(Assignment& _node) { return visitNode(_node); } + virtual bool visit(TupleExpression& _node) { return visitNode(_node); } + virtual bool visit(UnaryOperation& _node) { return visitNode(_node); } + virtual bool visit(BinaryOperation& _node) { return visitNode(_node); } + virtual bool visit(FunctionCall& _node) { return visitNode(_node); } + virtual bool visit(NewExpression& _node) { return visitNode(_node); } + virtual bool visit(MemberAccess& _node) { return visitNode(_node); } + virtual bool visit(IndexAccess& _node) { return visitNode(_node); } + virtual bool visit(Identifier& _node) { return visitNode(_node); } + virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } + virtual bool visit(Literal& _node) { return visitNode(_node); } + + virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } + virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); } + virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); } + virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(EnumValue& _node) { endVisitNode(_node); } + virtual void endVisit(ParameterList& _node) { endVisitNode(_node); } + virtual void endVisit(FunctionDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(VariableDeclaration& _node) { endVisitNode(_node); } + virtual void endVisit(ModifierDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); } + virtual void endVisit(EventDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(TypeName& _node) { endVisitNode(_node); } + virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); } + virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); } + virtual void endVisit(Mapping& _node) { endVisitNode(_node); } + virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); } + virtual void endVisit(Block& _node) { endVisitNode(_node); } + virtual void endVisit(PlaceholderStatement& _node) { endVisitNode(_node); } + virtual void endVisit(IfStatement& _node) { endVisitNode(_node); } + virtual void endVisit(WhileStatement& _node) { endVisitNode(_node); } + virtual void endVisit(ForStatement& _node) { endVisitNode(_node); } + virtual void endVisit(Continue& _node) { endVisitNode(_node); } + virtual void endVisit(Break& _node) { endVisitNode(_node); } + virtual void endVisit(Return& _node) { endVisitNode(_node); } + virtual void endVisit(Throw& _node) { endVisitNode(_node); } + virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } + virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } + virtual void endVisit(Assignment& _node) { endVisitNode(_node); } + virtual void endVisit(TupleExpression& _node) { endVisitNode(_node); } + virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); } + virtual void endVisit(BinaryOperation& _node) { endVisitNode(_node); } + virtual void endVisit(FunctionCall& _node) { endVisitNode(_node); } + virtual void endVisit(NewExpression& _node) { endVisitNode(_node); } + virtual void endVisit(MemberAccess& _node) { endVisitNode(_node); } + virtual void endVisit(IndexAccess& _node) { endVisitNode(_node); } + virtual void endVisit(Identifier& _node) { endVisitNode(_node); } + virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } + virtual void endVisit(Literal& _node) { endVisitNode(_node); } + +protected: + /// Generic function called by default for each node, to be overridden by derived classes + /// if behaviour unspecific to a node type is desired. + virtual bool visitNode(ASTNode&) { return true; } + /// Generic function called by default for each node, to be overridden by derived classes + /// if behaviour unspecific to a node type is desired. + virtual void endVisitNode(ASTNode&) { } +}; + +class ASTConstVisitor +{ +public: + virtual bool visit(SourceUnit const& _node) { return visitNode(_node); } + virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } + virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); } + virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); } + virtual bool visit(StructDefinition const& _node) { return visitNode(_node); } + virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); } + virtual bool visit(EnumValue const& _node) { return visitNode(_node); } + virtual bool visit(ParameterList const& _node) { return visitNode(_node); } + virtual bool visit(FunctionDefinition const& _node) { return visitNode(_node); } + virtual bool visit(VariableDeclaration const& _node) { return visitNode(_node); } + virtual bool visit(ModifierDefinition const& _node) { return visitNode(_node); } + virtual bool visit(ModifierInvocation const& _node) { return visitNode(_node); } + virtual bool visit(EventDefinition const& _node) { return visitNode(_node); } + virtual bool visit(TypeName const& _node) { return visitNode(_node); } + virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); } + virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); } + virtual bool visit(Mapping const& _node) { return visitNode(_node); } + virtual bool visit(ArrayTypeName const& _node) { return visitNode(_node); } + virtual bool visit(Block const& _node) { return visitNode(_node); } + virtual bool visit(PlaceholderStatement const& _node) { return visitNode(_node); } + virtual bool visit(IfStatement const& _node) { return visitNode(_node); } + virtual bool visit(WhileStatement const& _node) { return visitNode(_node); } + virtual bool visit(ForStatement const& _node) { return visitNode(_node); } + virtual bool visit(Continue const& _node) { return visitNode(_node); } + virtual bool visit(Break const& _node) { return visitNode(_node); } + virtual bool visit(Return const& _node) { return visitNode(_node); } + virtual bool visit(Throw const& _node) { return visitNode(_node); } + virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } + virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } + virtual bool visit(Assignment const& _node) { return visitNode(_node); } + virtual bool visit(TupleExpression const& _node) { return visitNode(_node); } + virtual bool visit(UnaryOperation const& _node) { return visitNode(_node); } + virtual bool visit(BinaryOperation const& _node) { return visitNode(_node); } + virtual bool visit(FunctionCall const& _node) { return visitNode(_node); } + virtual bool visit(NewExpression const& _node) { return visitNode(_node); } + virtual bool visit(MemberAccess const& _node) { return visitNode(_node); } + virtual bool visit(IndexAccess const& _node) { return visitNode(_node); } + virtual bool visit(Identifier const& _node) { return visitNode(_node); } + virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } + virtual bool visit(Literal const& _node) { return visitNode(_node); } + + virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } + virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); } + virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); } + virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); } + virtual void endVisit(ParameterList const& _node) { endVisitNode(_node); } + virtual void endVisit(FunctionDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(VariableDeclaration const& _node) { endVisitNode(_node); } + virtual void endVisit(ModifierDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); } + virtual void endVisit(EventDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeName const& _node) { endVisitNode(_node); } + virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); } + virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); } + virtual void endVisit(Mapping const& _node) { endVisitNode(_node); } + virtual void endVisit(ArrayTypeName const& _node) { endVisitNode(_node); } + virtual void endVisit(Block const& _node) { endVisitNode(_node); } + virtual void endVisit(PlaceholderStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(IfStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(WhileStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(ForStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(Continue const& _node) { endVisitNode(_node); } + virtual void endVisit(Break const& _node) { endVisitNode(_node); } + virtual void endVisit(Return const& _node) { endVisitNode(_node); } + virtual void endVisit(Throw const& _node) { endVisitNode(_node); } + virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } + virtual void endVisit(Assignment const& _node) { endVisitNode(_node); } + virtual void endVisit(TupleExpression const& _node) { endVisitNode(_node); } + virtual void endVisit(UnaryOperation const& _node) { endVisitNode(_node); } + virtual void endVisit(BinaryOperation const& _node) { endVisitNode(_node); } + virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); } + virtual void endVisit(NewExpression const& _node) { endVisitNode(_node); } + virtual void endVisit(MemberAccess const& _node) { endVisitNode(_node); } + virtual void endVisit(IndexAccess const& _node) { endVisitNode(_node); } + virtual void endVisit(Identifier const& _node) { endVisitNode(_node); } + virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } + virtual void endVisit(Literal const& _node) { endVisitNode(_node); } + +protected: + /// Generic function called by default for each node, to be overridden by derived classes + /// if behaviour unspecific to a node type is desired. + virtual bool visitNode(ASTNode const&) { return true; } + /// Generic function called by default for each node, to be overridden by derived classes + /// if behaviour unspecific to a node type is desired. + virtual void endVisitNode(ASTNode const&) { } +}; + +/** + * Utility class that accepts std::functions and calls them for visitNode and endVisitNode. + */ +class SimpleASTVisitor: public ASTConstVisitor +{ +public: + SimpleASTVisitor( + std::function<bool(ASTNode const&)> _onVisit, + std::function<void(ASTNode const&)> _onEndVisit + ): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {} + +protected: + virtual bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; } + virtual void endVisitNode(ASTNode const& _n) override { m_onEndVisit(_n); } + +private: + std::function<bool(ASTNode const&)> m_onVisit; + std::function<void(ASTNode const&)> m_onEndVisit; +}; + +/** + * Utility class that visits the AST in depth-first order and calls a function on each node and each edge. + * Child nodes are only visited if the node callback of the parent returns true. + * The node callback of a parent is called before any edge or node callback involving the children. + * The edge callbacks of all children are called before the edge callback of the parent. + * This way, the node callback can be used as an initializing callback and the edge callbacks can be + * used to compute a "reduce" function. + */ +class ASTReduce: public ASTConstVisitor +{ +public: + /** + * Constructs a new ASTReduce object with the given callback functions. + * @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper + * @param _onEdge called for each edge with (parent, child) + */ + ASTReduce( + std::function<bool(ASTNode const&)> _onNode, + std::function<void(ASTNode const&, ASTNode const&)> _onEdge + ): m_onNode(_onNode), m_onEdge(_onEdge) + { + } + +protected: + bool visitNode(ASTNode const& _node) override + { + m_parents.push_back(&_node); + return m_onNode(_node); + } + void endVisitNode(ASTNode const& _node) override + { + m_parents.pop_back(); + if (!m_parents.empty()) + m_onEdge(*m_parents.back(), _node); + } + +private: + std::vector<ASTNode const*> m_parents; + std::function<bool(ASTNode const&)> m_onNode; + std::function<void(ASTNode const&, ASTNode const&)> m_onEdge; +}; + +} +} diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h new file mode 100644 index 00000000..12a26ea7 --- /dev/null +++ b/libsolidity/ast/AST_accept.h @@ -0,0 +1,721 @@ +/* + 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 + * Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that + * file with these mechanical implementations. + */ + +#pragma once + +#include <libsolidity/ast/AST.h> +#include <libsolidity/ast/ASTVisitor.h> + +namespace dev +{ +namespace solidity +{ + +void SourceUnit::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_nodes, _visitor); + _visitor.endVisit(*this); +} + +void SourceUnit::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_nodes, _visitor); + _visitor.endVisit(*this); +} + +void ImportDirective::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ImportDirective::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ContractDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + listAccept(m_baseContracts, _visitor); + listAccept(m_definedStructs, _visitor); + listAccept(m_definedEnums, _visitor); + listAccept(m_stateVariables, _visitor); + listAccept(m_events, _visitor); + listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); + } + _visitor.endVisit(*this); +} + +void ContractDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + listAccept(m_baseContracts, _visitor); + listAccept(m_definedStructs, _visitor); + listAccept(m_definedEnums, _visitor); + listAccept(m_stateVariables, _visitor); + listAccept(m_events, _visitor); + listAccept(m_functionModifiers, _visitor); + listAccept(m_definedFunctions, _visitor); + } + _visitor.endVisit(*this); +} + +void InheritanceSpecifier::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_baseName->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_baseName->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void EnumDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void EnumDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void EnumValue::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void EnumValue::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void StructDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void StructDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_members, _visitor); + _visitor.endVisit(*this); +} + +void ParameterList::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_parameters, _visitor); + _visitor.endVisit(*this); +} + +void ParameterList::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_parameters, _visitor); + _visitor.endVisit(*this); +} + +void FunctionDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + if (m_returnParameters) + m_returnParameters->accept(_visitor); + listAccept(m_functionModifiers, _visitor); + if (m_body) + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void FunctionDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + if (m_returnParameters) + m_returnParameters->accept(_visitor); + listAccept(m_functionModifiers, _visitor); + if (m_body) + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDeclaration::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_typeName) + m_typeName->accept(_visitor); + if (m_value) + m_value->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDeclaration::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_typeName) + m_typeName->accept(_visitor); + if (m_value) + m_value->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ModifierDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ModifierDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_parameters->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ModifierInvocation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_modifierName->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void ModifierInvocation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_modifierName->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void EventDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + +void EventDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_parameters->accept(_visitor); + _visitor.endVisit(*this); +} + +void TypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void TypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void UserDefinedTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Mapping::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_keyType->accept(_visitor); + m_valueType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Mapping::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_keyType->accept(_visitor); + m_valueType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ArrayTypeName::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_baseType->accept(_visitor); + if (m_length) + m_length->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ArrayTypeName::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_baseType->accept(_visitor); + if (m_length) + m_length->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Block::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + listAccept(m_statements, _visitor); + _visitor.endVisit(*this); +} + +void Block::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + listAccept(m_statements, _visitor); + _visitor.endVisit(*this); +} + +void PlaceholderStatement::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void IfStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_trueBody->accept(_visitor); + if (m_falseBody) + m_falseBody->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void IfStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_trueBody->accept(_visitor); + if (m_falseBody) + m_falseBody->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void WhileStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void WhileStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_condition->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_initExpression) + m_initExpression->accept(_visitor); + if (m_condExpression) + m_condExpression->accept(_visitor); + if (m_loopExpression) + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_initExpression) + m_initExpression->accept(_visitor); + if (m_condExpression) + m_condExpression->accept(_visitor); + if (m_loopExpression) + m_loopExpression->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Continue::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Continue::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Break::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Break::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Return::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void Return::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void Throw::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Throw::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ExpressionStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void ExpressionStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + if (m_expression) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void VariableDeclarationStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + for (ASTPointer<VariableDeclaration> const& var: m_variables) + if (var) + var->accept(_visitor); + if (m_initialValue) + m_initialValue->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + for (ASTPointer<VariableDeclaration> const& var: m_variables) + if (var) + var->accept(_visitor); + if (m_initialValue) + m_initialValue->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Assignment::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_leftHandSide->accept(_visitor); + m_rightHandSide->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Assignment::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_leftHandSide->accept(_visitor); + m_rightHandSide->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TupleExpression::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + for (auto const& component: m_components) + if (component) + component->accept(_visitor); + _visitor.endVisit(*this); +} + +void TupleExpression::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + for (auto const& component: m_components) + if (component) + component->accept(_visitor); + _visitor.endVisit(*this); +} + +void UnaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_subExpression->accept(_visitor); + _visitor.endVisit(*this); +} + +void UnaryOperation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_subExpression->accept(_visitor); + _visitor.endVisit(*this); +} + +void BinaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_left->accept(_visitor); + m_right->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void BinaryOperation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_left->accept(_visitor); + m_right->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void FunctionCall::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void FunctionCall::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void NewExpression::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_contractName->accept(_visitor); + _visitor.endVisit(*this); +} + +void NewExpression::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_contractName->accept(_visitor); + _visitor.endVisit(*this); +} + +void MemberAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void MemberAccess::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_expression->accept(_visitor); + _visitor.endVisit(*this); +} + +void IndexAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_base->accept(_visitor); + if (m_index) + m_index->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void IndexAccess::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_base->accept(_visitor); + if (m_index) + m_index->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Identifier::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Identifier::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Literal::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Literal::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +} +} diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp new file mode 100644 index 00000000..0253e843 --- /dev/null +++ b/libsolidity/ast/Types.cpp @@ -0,0 +1,1921 @@ +/* + 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 + * Solidity data types + */ + +#include <libsolidity/ast/Types.h> +#include <limits> +#include <boost/range/adaptor/reversed.hpp> +#include <libdevcore/CommonIO.h> +#include <libdevcore/CommonData.h> +#include <libdevcore/SHA3.h> +#include <libsolidity/interface/Utils.h> +#include <libsolidity/ast/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +void StorageOffsets::computeOffsets(TypePointers const& _types) +{ + bigint slotOffset = 0; + unsigned byteOffset = 0; + map<size_t, pair<u256, unsigned>> offsets; + for (size_t i = 0; i < _types.size(); ++i) + { + TypePointer const& type = _types[i]; + if (!type->canBeStored()) + continue; + if (byteOffset + type->storageBytes() > 32) + { + // would overflow, go to next slot + ++slotOffset; + byteOffset = 0; + } + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Object too large for storage.")); + offsets[i] = make_pair(u256(slotOffset), byteOffset); + solAssert(type->storageSize() >= 1, "Invalid storage size."); + if (type->storageSize() == 1 && byteOffset + type->storageBytes() <= 32) + byteOffset += type->storageBytes(); + else + { + slotOffset += type->storageSize(); + byteOffset = 0; + } + } + if (byteOffset > 0) + ++slotOffset; + if (slotOffset >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Object too large for storage.")); + m_storageSize = u256(slotOffset); + swap(m_offsets, offsets); +} + +pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const +{ + if (m_offsets.count(_index)) + return &m_offsets.at(_index); + else + return nullptr; +} + +MemberList& MemberList::operator=(MemberList&& _other) +{ + assert(&_other != this); + + m_memberTypes = std::move(_other.m_memberTypes); + m_storageOffsets = std::move(_other.m_storageOffsets); + return *this; +} + +std::pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const +{ + if (!m_storageOffsets) + { + TypePointers memberTypes; + memberTypes.reserve(m_memberTypes.size()); + for (auto const& member: m_memberTypes) + memberTypes.push_back(member.type); + m_storageOffsets.reset(new StorageOffsets()); + m_storageOffsets->computeOffsets(memberTypes); + } + for (size_t index = 0; index < m_memberTypes.size(); ++index) + if (m_memberTypes[index].name == _name) + return m_storageOffsets->offset(index); + return nullptr; +} + +u256 const& MemberList::storageSize() const +{ + // trigger lazy computation + memberStorageOffset(""); + return m_storageOffsets->storageSize(); +} + +TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) +{ + char const* tokenCstr = Token::toString(_typeToken); + solAssert(Token::isElementaryTypeName(_typeToken), + "Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : "")); + + if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32) + { + int offset = _typeToken - Token::Int; + int bytes = offset % 33; + if (bytes == 0 && _typeToken != Token::Bytes1) + bytes = 32; + int modifier = offset / 33; + switch(modifier) + { + case 0: + return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Signed); + case 1: + return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned); + case 2: + return make_shared<FixedBytesType>(bytes + 1); + default: + solAssert(false, "Unexpected modifier value. Should never happen"); + return TypePointer(); + } + } + else if (_typeToken == Token::Byte) + return make_shared<FixedBytesType>(1); + else if (_typeToken == Token::Address) + return make_shared<IntegerType>(0, IntegerType::Modifier::Address); + else if (_typeToken == Token::Bool) + return make_shared<BoolType>(); + else if (_typeToken == Token::Bytes) + return make_shared<ArrayType>(DataLocation::Storage); + else if (_typeToken == Token::String) + return make_shared<ArrayType>(DataLocation::Storage, true); + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment( + "Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type." + )); +} + +TypePointer Type::fromElementaryTypeName(string const& _name) +{ + return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name)); +} + +TypePointer Type::forLiteral(Literal const& _literal) +{ + switch (_literal.token()) + { + case Token::TrueLiteral: + case Token::FalseLiteral: + return make_shared<BoolType>(); + case Token::Number: + if (!IntegerConstantType::isValidLiteral(_literal)) + return TypePointer(); + return make_shared<IntegerConstantType>(_literal); + case Token::StringLiteral: + return make_shared<StringLiteralType>(_literal); + default: + return shared_ptr<Type>(); + } +} + +TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) +{ + if (_b->isImplicitlyConvertibleTo(*_a)) + return _a; + else if (_a->isImplicitlyConvertibleTo(*_b)) + return _b; + else + return TypePointer(); +} + +const MemberList Type::EmptyMemberList; + +IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): + m_bits(_bits), m_modifier(_modifier) +{ + if (isAddress()) + m_bits = 160; + solAssert(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, + "Invalid bit number for integer type: " + dev::toString(_bits)); +} + +bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (convertTo.m_bits < m_bits) + return false; + if (isAddress()) + return convertTo.isAddress(); + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.m_bits > m_bits; +} + +bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.category() == category() || + _convertTo.category() == Category::Contract || + _convertTo.category() == Category::Enum || + _convertTo.category() == Category::FixedBytes; +} + +TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" is ok for all integer types + if (_operator == Token::Delete) + return make_shared<TupleType>(); + // no further unary operators for addresses + else if (isAddress()) + return TypePointer(); + // for non-address integers, we allow +, -, ++ and -- + else if (_operator == Token::Add || _operator == Token::Sub || + _operator == Token::Inc || _operator == Token::Dec || + _operator == Token::After || _operator == Token::BitNot) + return shared_from_this(); + else + return TypePointer(); +} + +bool IntegerType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + IntegerType const& other = dynamic_cast<IntegerType const&>(_other); + return other.m_bits == m_bits && other.m_modifier == m_modifier; +} + +string IntegerType::toString(bool) const +{ + if (isAddress()) + return "address"; + string prefix = isSigned() ? "int" : "uint"; + return prefix + dev::toString(m_bits); +} + +TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (_other->category() != Category::IntegerConstant && _other->category() != category()) + return TypePointer(); + auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other)); + + if (!commonType) + return TypePointer(); + + // All integer types can be compared + if (Token::isCompareOp(_operator)) + return commonType; + if (Token::isBooleanOp(_operator)) + return TypePointer(); + // Nothing else can be done with addresses + if (commonType->isAddress()) + return TypePointer(); + + return commonType; +} + +const MemberList IntegerType::AddressMemberList({ + {"balance", make_shared<IntegerType >(256)}, + {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, + {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} +}); + +bool IntegerConstantType::isValidLiteral(const Literal& _literal) +{ + try + { + bigint x(_literal.value()); + } + catch (...) + { + return false; + } + return true; +} + +IntegerConstantType::IntegerConstantType(Literal const& _literal) +{ + m_value = bigint(_literal.value()); + + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + case Literal::SubDenomination::None: + break; + case Literal::SubDenomination::Szabo: + m_value *= bigint("1000000000000"); + break; + case Literal::SubDenomination::Finney: + m_value *= bigint("1000000000000000"); + break; + case Literal::SubDenomination::Ether: + m_value *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + m_value *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + m_value *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + m_value *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + m_value *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + m_value *= bigint("31536000"); + break; + } +} + +bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (auto targetType = dynamic_cast<IntegerType const*>(&_convertTo)) + { + if (m_value == 0) + return true; + int forSignBit = (targetType->isSigned() ? 1 : 0); + if (m_value > 0) + { + if (m_value <= (u256(-1) >> (256 - targetType->numBits() + forSignBit))) + return true; + } + else if (targetType->isSigned() && -m_value <= (u256(1) << (targetType->numBits() - forSignBit))) + return true; + return false; + } + else if (_convertTo.category() == Category::FixedBytes) + { + FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); + return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + } + else + return false; +} + +bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + TypePointer intType = integerType(); + return intType && intType->isExplicitlyConvertibleTo(_convertTo); +} + +TypePointer IntegerConstantType::unaryOperatorResult(Token::Value _operator) const +{ + bigint value; + switch (_operator) + { + case Token::BitNot: + value = ~m_value; + break; + case Token::Add: + value = m_value; + break; + case Token::Sub: + value = -m_value; + break; + case Token::After: + return shared_from_this(); + default: + return TypePointer(); + } + return make_shared<IntegerConstantType>(value); +} + +TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (_other->category() == Category::Integer) + { + shared_ptr<IntegerType const> intType = integerType(); + if (!intType) + return TypePointer(); + return intType->binaryOperatorResult(_operator, _other); + } + else if (_other->category() != category()) + return TypePointer(); + + IntegerConstantType const& other = dynamic_cast<IntegerConstantType const&>(*_other); + if (Token::isCompareOp(_operator)) + { + shared_ptr<IntegerType const> thisIntegerType = integerType(); + shared_ptr<IntegerType const> otherIntegerType = other.integerType(); + if (!thisIntegerType || !otherIntegerType) + return TypePointer(); + return thisIntegerType->binaryOperatorResult(_operator, otherIntegerType); + } + else + { + bigint value; + switch (_operator) + { + case Token::BitOr: + value = m_value | other.m_value; + break; + case Token::BitXor: + value = m_value ^ other.m_value; + break; + case Token::BitAnd: + value = m_value & other.m_value; + break; + case Token::Add: + value = m_value + other.m_value; + break; + case Token::Sub: + value = m_value - other.m_value; + break; + case Token::Mul: + value = m_value * other.m_value; + break; + case Token::Div: + if (other.m_value == 0) + return TypePointer(); + value = m_value / other.m_value; + break; + case Token::Mod: + if (other.m_value == 0) + return TypePointer(); + value = m_value % other.m_value; + break; + case Token::Exp: + if (other.m_value < 0) + return TypePointer(); + else if (other.m_value > std::numeric_limits<unsigned int>::max()) + return TypePointer(); + else + value = boost::multiprecision::pow(m_value, other.m_value.convert_to<unsigned int>()); + break; + default: + return TypePointer(); + } + return make_shared<IntegerConstantType>(value); + } +} + +bool IntegerConstantType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + return m_value == dynamic_cast<IntegerConstantType const&>(_other).m_value; +} + +string IntegerConstantType::toString(bool) const +{ + return "int_const " + m_value.str(); +} + +u256 IntegerConstantType::literalValue(Literal const*) const +{ + u256 value; + // we ignore the literal and hope that the type was correctly determined + solAssert(m_value <= u256(-1), "Integer constant too large."); + solAssert(m_value >= -(bigint(1) << 255), "Integer constant too small."); + + if (m_value >= 0) + value = u256(m_value); + else + value = s2u(s256(m_value)); + + return value; +} + +TypePointer IntegerConstantType::mobileType() const +{ + auto intType = integerType(); + solAssert(!!intType, "mobileType called with invalid integer constant " + toString(false)); + return intType; +} + +shared_ptr<IntegerType const> IntegerConstantType::integerType() const +{ + bigint value = m_value; + bool negative = (value < 0); + if (negative) // convert to positive number of same bit requirements + value = ((-value) - 1) << 1; + if (value > u256(-1)) + return shared_ptr<IntegerType const>(); + else + return make_shared<IntegerType>( + max(bytesRequired(value), 1u) * 8, + negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned + ); +} + +StringLiteralType::StringLiteralType(Literal const& _literal): + m_value(_literal.value()) +{ +} + +bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo)) + return size_t(fixedBytes->numBytes()) >= m_value.size(); + else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo)) + return + arrayType->isByteArray() && + !(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()); + else + return false; +} + +bool StringLiteralType::operator==(const Type& _other) const +{ + if (_other.category() != category()) + return false; + return m_value == dynamic_cast<StringLiteralType const&>(_other).m_value; +} + +TypePointer StringLiteralType::mobileType() const +{ + return make_shared<ArrayType>(DataLocation::Memory, true); +} + +shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal) +{ + if (_literal.length() <= 32) + return make_shared<FixedBytesType>(_literal.length()); + return shared_ptr<FixedBytesType>(); +} + +FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes) +{ + solAssert(m_bytes >= 0 && m_bytes <= 32, + "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes)); +} + +bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo); + return convertTo.m_bytes >= m_bytes; +} + +bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.category() == Category::Integer || + _convertTo.category() == Category::Contract || + _convertTo.category() == category(); +} + +TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" and "~" is okay for FixedBytesType + if (_operator == Token::Delete) + return make_shared<TupleType>(); + else if (_operator == Token::BitNot) + return shared_from_this(); + + return TypePointer(); +} + +TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + auto commonType = dynamic_pointer_cast<FixedBytesType const>(Type::commonType(shared_from_this(), _other)); + if (!commonType) + return TypePointer(); + + // FixedBytes can be compared and have bitwise operators applied to them + if (Token::isCompareOp(_operator) || Token::isBitOp(_operator)) + return commonType; + + return TypePointer(); +} + +bool FixedBytesType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + FixedBytesType const& other = dynamic_cast<FixedBytesType const&>(_other); + return other.m_bytes == m_bytes; +} + +bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + // conversion to integer is fine, but not to address + // this is an example of explicit conversions being not transitive (though implicit should be) + if (_convertTo.category() == category()) + { + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (!convertTo.isAddress()) + return true; + } + return isImplicitlyConvertibleTo(_convertTo); +} + +u256 BoolType::literalValue(Literal const* _literal) const +{ + solAssert(_literal, ""); + if (_literal->token() == Token::TrueLiteral) + return u256(1); + else if (_literal->token() == Token::FalseLiteral) + return u256(0); + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); +} + +TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const +{ + if (_operator == Token::Delete) + return make_shared<TupleType>(); + return (_operator == Token::Not) ? shared_from_this() : TypePointer(); +} + +TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + if (category() != _other->category()) + return TypePointer(); + if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or) + return _other; + else + return TypePointer(); +} + +bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (*this == _convertTo) + return true; + if (_convertTo.category() == Category::Integer) + return dynamic_cast<IntegerType const&>(_convertTo).isAddress(); + if (_convertTo.category() == Category::Contract) + { + auto const& bases = contractDefinition().annotation().linearizedBaseContracts; + if (m_super && bases.size() <= 1) + return false; + return find(m_super ? ++bases.begin() : bases.begin(), bases.end(), + &dynamic_cast<ContractType const&>(_convertTo).contractDefinition()) != bases.end(); + } + return false; +} + +bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return + isImplicitlyConvertibleTo(_convertTo) || + _convertTo.category() == Category::Integer || + _convertTo.category() == Category::Contract; +} + +TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); +} + +TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const +{ + if (_operator != Token::Delete) + return TypePointer(); + // delete can be used on everything except calldata references or storage pointers + // (storage references are ok) + switch (location()) + { + case DataLocation::CallData: + return TypePointer(); + case DataLocation::Memory: + return make_shared<TupleType>(); + case DataLocation::Storage: + return m_isPointer ? TypePointer() : make_shared<TupleType>(); + default: + solAssert(false, ""); + } + return TypePointer(); +} + +TypePointer ReferenceType::copyForLocationIfReference(DataLocation _location, TypePointer const& _type) +{ + if (auto type = dynamic_cast<ReferenceType const*>(_type.get())) + return type->copyForLocation(_location, false); + return _type; +} + +TypePointer ReferenceType::copyForLocationIfReference(TypePointer const& _type) const +{ + return copyForLocationIfReference(m_location, _type); +} + +string ReferenceType::stringForReferencePart() const +{ + switch (m_location) + { + case DataLocation::Storage: + return string("storage ") + (m_isPointer ? "pointer" : "ref"); + case DataLocation::CallData: + return "calldata"; + case DataLocation::Memory: + return "memory"; + } + solAssert(false, ""); + return ""; +} + +bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); + if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString()) + return false; + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer()) + return false; + if (convertTo.location() == DataLocation::CallData && location() != convertTo.location()) + return false; + if (convertTo.location() == DataLocation::Storage && !convertTo.isPointer()) + { + // Less restrictive conversion, since we need to copy anyway. + if (!baseType()->isImplicitlyConvertibleTo(*convertTo.baseType())) + return false; + if (convertTo.isDynamicallySized()) + return true; + return !isDynamicallySized() && convertTo.length() >= length(); + } + else + { + // Conversion to storage pointer or to memory, we de not copy element-for-element here, so + // require that the base type is the same, not only convertible. + // This disallows assignment of nested dynamic arrays from storage to memory for now. + if ( + *copyForLocationIfReference(location(), baseType()) != + *copyForLocationIfReference(location(), convertTo.baseType()) + ) + return false; + if (isDynamicallySized() != convertTo.isDynamicallySized()) + return false; + // We also require that the size is the same. + if (!isDynamicallySized() && length() != convertTo.length()) + return false; + return true; + } +} + +bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + // allow conversion bytes <-> string + if (_convertTo.category() != category()) + return false; + auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo); + if (convertTo.location() != location()) + return false; + if (!isByteArray() || !convertTo.isByteArray()) + return false; + return true; +} + +bool ArrayType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + ArrayType const& other = dynamic_cast<ArrayType const&>(_other); + if ( + !ReferenceType::operator==(other) || + other.isByteArray() != isByteArray() || + other.isString() != isString() || + other.isDynamicallySized() != isDynamicallySized() + ) + return false; + return isDynamicallySized() || length() == other.length(); +} + +unsigned ArrayType::calldataEncodedSize(bool _padded) const +{ + if (isDynamicallySized()) + return 0; + bigint size = bigint(length()) * (isByteArray() ? 1 : baseType()->calldataEncodedSize(_padded)); + size = ((size + 31) / 32) * 32; + solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit unsigned."); + return unsigned(size); +} + +u256 ArrayType::storageSize() const +{ + if (isDynamicallySized()) + return 1; + + bigint size; + unsigned baseBytes = baseType()->storageBytes(); + if (baseBytes == 0) + size = 1; + else if (baseBytes < 32) + { + unsigned itemsPerSlot = 32 / baseBytes; + size = (bigint(length()) + (itemsPerSlot - 1)) / itemsPerSlot; + } + else + size = bigint(length()) * baseType()->storageSize(); + if (size >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(Error(Error::Type::TypeError) << errinfo_comment("Array too large for storage.")); + return max<u256>(1, u256(size)); +} + +unsigned ArrayType::sizeOnStack() const +{ + if (m_location == DataLocation::CallData) + // offset [length] (stack top) + return 1 + (isDynamicallySized() ? 1 : 0); + else + // storage slot or memory offset + // byte offset inside storage value is omitted + return 1; +} + +string ArrayType::toString(bool _short) const +{ + string ret; + if (isString()) + ret = "string"; + else if (isByteArray()) + ret = "bytes"; + else + { + ret = baseType()->toString(_short) + "["; + if (!isDynamicallySized()) + ret += length().str(); + ret += "]"; + } + if (!_short) + ret += " " + stringForReferencePart(); + return ret; +} + +string ArrayType::canonicalName(bool _addDataLocation) const +{ + string ret; + if (isString()) + ret = "string"; + else if (isByteArray()) + ret = "bytes"; + else + { + ret = baseType()->canonicalName(false) + "["; + if (!isDynamicallySized()) + ret += length().str(); + ret += "]"; + } + if (_addDataLocation && location() == DataLocation::Storage) + ret += " storage"; + return ret; +} + +MemberList const& ArrayType::members() const +{ + if (!m_members) + { + MemberList::MemberMap members; + if (!isString()) + { + members.push_back({"length", make_shared<IntegerType>(256)}); + if (isDynamicallySized() && location() == DataLocation::Storage) + members.push_back({"push", make_shared<FunctionType>( + TypePointers{baseType()}, + TypePointers{make_shared<IntegerType>(256)}, + strings{string()}, + strings{string()}, + isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush + )}); + } + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +TypePointer ArrayType::encodingType() const +{ + if (location() == DataLocation::Storage) + return make_shared<IntegerType>(256); + else + return this->copyForLocation(DataLocation::Memory, true); +} + +TypePointer ArrayType::decodingType() const +{ + if (location() == DataLocation::Storage) + return make_shared<IntegerType>(256); + else + return shared_from_this(); +} + +TypePointer ArrayType::interfaceType(bool _inLibrary) const +{ + if (_inLibrary && location() == DataLocation::Storage) + return shared_from_this(); + + if (m_arrayKind != ArrayKind::Ordinary) + return this->copyForLocation(DataLocation::Memory, true); + TypePointer baseExt = m_baseType->interfaceType(_inLibrary); + if (!baseExt) + return TypePointer(); + if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized()) + return TypePointer(); + + if (isDynamicallySized()) + return std::make_shared<ArrayType>(DataLocation::Memory, baseExt); + else + return std::make_shared<ArrayType>(DataLocation::Memory, baseExt, m_length); +} + +u256 ArrayType::memorySize() const +{ + solAssert(!isDynamicallySized(), ""); + solAssert(m_location == DataLocation::Memory, ""); + bigint size = bigint(m_length) * m_baseType->memoryHeadSize(); + solAssert(size <= numeric_limits<unsigned>::max(), "Array size does not fit u256."); + return u256(size); +} + +TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const +{ + auto copy = make_shared<ArrayType>(_location); + copy->m_isPointer = _isPointer; + copy->m_arrayKind = m_arrayKind; + copy->m_baseType = copy->copyForLocationIfReference(m_baseType); + copy->m_hasDynamicLength = m_hasDynamicLength; + copy->m_length = m_length; + return copy; +} + +bool ContractType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + ContractType const& other = dynamic_cast<ContractType const&>(_other); + return other.m_contract == m_contract && other.m_super == m_super; +} + +string ContractType::toString(bool) const +{ + return + string(m_contract.isLibrary() ? "library " : "contract ") + + string(m_super ? "super " : "") + + m_contract.name(); +} + +string ContractType::canonicalName(bool) const +{ + return m_contract.annotation().canonicalName; +} + +MemberList const& ContractType::members() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + // All address members and all interface functions + MemberList::MemberMap members( + IntegerType::AddressMemberList.begin(), + IntegerType::AddressMemberList.end() + ); + if (m_super) + { + // add the most derived of all functions which are visible in derived contracts + for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) + for (ASTPointer<FunctionDefinition> const& function: base->definedFunctions()) + { + if (!function->isVisibleInDerivedContracts()) + continue; + auto functionType = make_shared<FunctionType>(*function, true); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) + { + if (member.name != function->name()) + continue; + auto memberType = dynamic_cast<FunctionType const*>(member.type.get()); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualArgumentTypes(*functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (!functionWithEqualArgumentsFound) + members.push_back(MemberList::Member( + function->name(), + functionType, + function.get() + )); + } + } + else + for (auto const& it: m_contract.interfaceFunctions()) + members.push_back(MemberList::Member( + it.second->declaration().name(), + it.second->asMemberFunction(m_contract.isLibrary()), + &it.second->declaration() + )); + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +shared_ptr<FunctionType const> const& ContractType::constructorType() const +{ + if (!m_constructorType) + { + FunctionDefinition const* constructor = m_contract.constructor(); + if (constructor) + m_constructorType = make_shared<FunctionType>(*constructor); + else + m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers()); + } + return m_constructorType; +} + +vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables() const +{ + vector<VariableDeclaration const*> variables; + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) + for (ASTPointer<VariableDeclaration> const& variable: contract->stateVariables()) + if (!variable->isConstant()) + variables.push_back(variable.get()); + TypePointers types; + for (auto variable: variables) + types.push_back(variable->annotation().type); + StorageOffsets offsets; + offsets.computeOffsets(types); + + vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets; + for (size_t index = 0; index < variables.size(); ++index) + if (auto const* offset = offsets.offset(index)) + variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second)); + return variablesAndOffsets; +} + +bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + auto& convertTo = dynamic_cast<StructType const&>(_convertTo); + // memory/calldata to storage can be converted, but only to a direct storage reference + if (convertTo.location() == DataLocation::Storage && location() != DataLocation::Storage && convertTo.isPointer()) + return false; + if (convertTo.location() == DataLocation::CallData && location() != convertTo.location()) + return false; + return this->m_struct == convertTo.m_struct; +} + +bool StructType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + StructType const& other = dynamic_cast<StructType const&>(_other); + return ReferenceType::operator==(other) && other.m_struct == m_struct; +} + +unsigned StructType::calldataEncodedSize(bool _padded) const +{ + unsigned size = 0; + for (auto const& member: members()) + if (!member.type->canLiveOutsideStorage()) + return 0; + else + { + unsigned memberSize = member.type->calldataEncodedSize(_padded); + if (memberSize == 0) + return 0; + size += memberSize; + } + return size; +} + +u256 StructType::memorySize() const +{ + u256 size; + for (auto const& member: members()) + if (member.type->canLiveOutsideStorage()) + size += member.type->memoryHeadSize(); + return size; +} + +u256 StructType::storageSize() const +{ + return max<u256>(1, members().storageSize()); +} + +string StructType::toString(bool _short) const +{ + string ret = "struct " + m_struct.name(); + if (!_short) + ret += " " + stringForReferencePart(); + return ret; +} + +MemberList const& StructType::members() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + MemberList::MemberMap members; + for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) + { + TypePointer type = variable->annotation().type; + // Skip all mapping members if we are not in storage. + if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) + continue; + members.push_back(MemberList::Member( + variable->name(), + copyForLocationIfReference(type), + variable.get()) + ); + } + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +TypePointer StructType::interfaceType(bool _inLibrary) const +{ + if (_inLibrary && location() == DataLocation::Storage) + return shared_from_this(); + else + return TypePointer(); +} + +TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const +{ + auto copy = make_shared<StructType>(m_struct, _location); + copy->m_isPointer = _isPointer; + return copy; +} + +string StructType::canonicalName(bool _addDataLocation) const +{ + string ret = m_struct.annotation().canonicalName; + if (_addDataLocation && location() == DataLocation::Storage) + ret += " storage"; + return ret; +} + +FunctionTypePointer StructType::constructorType() const +{ + TypePointers paramTypes; + strings paramNames; + for (auto const& member: members()) + { + if (!member.type->canLiveOutsideStorage()) + continue; + paramNames.push_back(member.name); + paramTypes.push_back(copyForLocationIfReference(DataLocation::Memory, member.type)); + } + return make_shared<FunctionType>( + paramTypes, + TypePointers{copyForLocation(DataLocation::Memory, false)}, + paramNames, + strings(), + FunctionType::Location::Internal + ); +} + +pair<u256, unsigned> const& StructType::storageOffsetsOfMember(string const& _name) const +{ + auto const* offsets = members().memberStorageOffset(_name); + solAssert(offsets, "Storage offset of non-existing member requested."); + return *offsets; +} + +u256 StructType::memoryOffsetOfMember(string const& _name) const +{ + u256 offset; + for (auto const& member: members()) + if (member.name == _name) + return offset; + else + offset += member.type->memoryHeadSize(); + solAssert(false, "Member not found in struct."); + return 0; +} + +set<string> StructType::membersMissingInMemory() const +{ + set<string> missing; + for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) + if (!variable->annotation().type->canLiveOutsideStorage()) + missing.insert(variable->name()); + return missing; +} + +TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); +} + +bool EnumType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + EnumType const& other = dynamic_cast<EnumType const&>(_other); + return other.m_enum == m_enum; +} + +unsigned EnumType::storageBytes() const +{ + size_t elements = m_enum.members().size(); + if (elements <= 1) + return 1; + else + return dev::bytesRequired(elements - 1); +} + +string EnumType::toString(bool) const +{ + return string("enum ") + m_enum.name(); +} + +string EnumType::canonicalName(bool) const +{ + return m_enum.annotation().canonicalName; +} + +bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + return _convertTo.category() == category() || _convertTo.category() == Category::Integer; +} + +unsigned int EnumType::memberValue(ASTString const& _member) const +{ + unsigned int index = 0; + for (ASTPointer<EnumValue> const& decl: m_enum.members()) + { + if (decl->name() == _member) + return index; + ++index; + } + BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member)); +} + +bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const +{ + if (auto tupleType = dynamic_cast<TupleType const*>(&_other)) + { + TypePointers const& targets = tupleType->components(); + if (targets.empty()) + return components().empty(); + if (components().size() != targets.size() && !targets.front() && !targets.back()) + return false; // (,a,) = (1,2,3,4) - unable to position `a` in the tuple. + size_t minNumValues = targets.size(); + if (!targets.back() || !targets.front()) + --minNumValues; // wildcards can also match 0 components + if (components().size() < minNumValues) + return false; + if (components().size() > targets.size() && targets.front() && targets.back()) + return false; // larger source and no wildcard + bool fillRight = !targets.back() || targets.front(); + for (size_t i = 0; i < min(targets.size(), components().size()); ++i) + { + auto const& s = components()[fillRight ? i : components().size() - i - 1]; + auto const& t = targets[fillRight ? i : targets.size() - i - 1]; + if (!s && t) + return false; + else if (s && t && !s->isImplicitlyConvertibleTo(*t)) + return false; + } + return true; + } + else + return false; +} + +bool TupleType::operator==(Type const& _other) const +{ + if (auto tupleType = dynamic_cast<TupleType const*>(&_other)) + return components() == tupleType->components(); + else + return false; +} + +string TupleType::toString(bool _short) const +{ + if (components().empty()) + return "tuple()"; + string str = "tuple("; + for (auto const& t: components()) + str += (t ? t->toString(_short) : "") + ","; + str.pop_back(); + return str + ")"; +} + +u256 TupleType::storageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() << + errinfo_comment("Storage size of non-storable tuple type requested.") + ); +} + +unsigned TupleType::sizeOnStack() const +{ + unsigned size = 0; + for (auto const& t: components()) + size += t ? t->sizeOnStack() : 0; + return size; +} + +TypePointer TupleType::mobileType() const +{ + TypePointers mobiles; + for (auto const& c: components()) + mobiles.push_back(c ? c->mobileType() : TypePointer()); + return make_shared<TupleType>(mobiles); +} + +TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) const +{ + solAssert(!!_targetType, ""); + TypePointers const& targetComponents = dynamic_cast<TupleType const&>(*_targetType).components(); + bool fillRight = !targetComponents.empty() && (!targetComponents.back() || targetComponents.front()); + TypePointers tempComponents(targetComponents.size()); + for (size_t i = 0; i < min(targetComponents.size(), components().size()); ++i) + { + size_t si = fillRight ? i : components().size() - i - 1; + size_t ti = fillRight ? i : targetComponents.size() - i - 1; + if (components()[si] && targetComponents[ti]) + tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]); + } + return make_shared<TupleType>(tempComponents); +} + +FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): + 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.parameters().size()); + paramNames.reserve(_function.parameters().size()); + for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) + { + paramNames.push_back(var->name()); + params.push_back(var->annotation().type); + } + retParams.reserve(_function.returnParameters().size()); + retParamNames.reserve(_function.returnParameters().size()); + for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters()) + { + retParamNames.push_back(var->name()); + retParams.push_back(var->annotation().type); + } + 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 paramTypes; + vector<string> paramNames; + auto returnType = _varDecl.annotation().type; + + while (true) + { + if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) + { + paramTypes.push_back(mappingType->keyType()); + paramNames.push_back(""); + returnType = mappingType->valueType(); + } + else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) + { + if (arrayType->isByteArray()) + // Return byte arrays as as whole. + break; + returnType = arrayType->baseType(); + paramNames.push_back(""); + paramTypes.push_back(make_shared<IntegerType>(256)); + } + else + break; + } + + TypePointers retParams; + vector<string> retParamNames; + if (auto structType = dynamic_cast<StructType const*>(returnType.get())) + { + for (auto const& member: structType->members()) + if (member.type->category() != Category::Mapping) + { + if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get())) + if (!arrayType->isByteArray()) + continue; + retParams.push_back(member.type); + retParamNames.push_back(member.name); + } + } + else + { + retParams.push_back(ReferenceType::copyForLocationIfReference( + DataLocation::Memory, + returnType + )); + retParamNames.push_back(""); + } + + swap(paramTypes, m_parameterTypes); + swap(paramNames, m_parameterNames); + swap(retParams, m_returnParameterTypes); + swap(retParamNames, m_returnParameterNames); +} + +FunctionType::FunctionType(const EventDefinition& _event): + m_location(Location::Event), m_isConstant(true), m_declaration(&_event) +{ + TypePointers params; + vector<string> paramNames; + params.reserve(_event.parameters().size()); + paramNames.reserve(_event.parameters().size()); + for (ASTPointer<VariableDeclaration> const& var: _event.parameters()) + { + paramNames.push_back(var->name()); + params.push_back(var->annotation().type); + } + swap(params, m_parameterTypes); + swap(paramNames, m_parameterNames); +} + +bool FunctionType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + FunctionType const& other = dynamic_cast<FunctionType const&>(_other); + + 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; + auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; + + if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), + other.m_parameterTypes.cbegin(), typeCompare)) + return false; + if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), + other.m_returnParameterTypes.cbegin(), typeCompare)) + return false; + //@todo this is ugly, but cannot be prevented right now + if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet) + return false; + return true; +} + +string FunctionType::toString(bool _short) const +{ + string name = "function ("; + for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); + name += ") returns ("; + for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it) + name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ","); + return name + ")"; +} + +u256 FunctionType::storageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable function type requested.")); +} + +unsigned FunctionType::sizeOnStack() const +{ + Location location = m_location; + if (m_location == Location::SetGas || m_location == Location::SetValue) + { + solAssert(m_returnParameterTypes.size() == 1, ""); + location = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_location; + } + + unsigned size = 0; + if (location == Location::External || location == Location::CallCode) + size = 2; + else if (location == Location::Bare || location == Location::BareCallCode) + size = 1; + else if (location == Location::Internal) + size = 1; + else if (location == Location::ArrayPush || location == Location::ByteArrayPush) + size = 1; + if (m_gasSet) + size++; + if (m_valueSet) + size++; + return size; +} + +FunctionTypePointer FunctionType::interfaceFunctionType() const +{ + // Note that m_declaration might also be a state variable! + solAssert(m_declaration, "Declaration needed to determine interface function type."); + bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary(); + + TypePointers paramTypes; + TypePointers retParamTypes; + + for (auto type: m_parameterTypes) + { + if (auto ext = type->interfaceType(isLibraryFunction)) + paramTypes.push_back(ext); + else + return FunctionTypePointer(); + } + for (auto type: m_returnParameterTypes) + { + if (auto ext = type->interfaceType(isLibraryFunction)) + retParamTypes.push_back(ext); + else + return FunctionTypePointer(); + } + return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); +} + +MemberList const& FunctionType::members() const +{ + switch (m_location) + { + case Location::External: + case Location::Creation: + case Location::ECRecover: + case Location::SHA256: + case Location::RIPEMD160: + case Location::Bare: + case Location::BareCallCode: + if (!m_members) + { + MemberList::MemberMap members{ + { + "value", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(false, true)}, + strings(), + strings(), + Location::SetValue, + false, + nullptr, + m_gasSet, + m_valueSet + ) + } + }; + if (m_location != Location::Creation) + members.push_back( + MemberList::Member( + "gas", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(true, false)}, + strings(), + strings(), + Location::SetGas, + false, + nullptr, + m_gasSet, + m_valueSet + ) + ) + ); + m_members.reset(new MemberList(members)); + } + return *m_members; + default: + return EmptyMemberList; + } +} + +bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes) const +{ + TypePointers const& paramTypes = parameterTypes(); + if (takesArbitraryParameters()) + return true; + else if (_argumentTypes.size() != paramTypes.size()) + return false; + else + return std::equal( + _argumentTypes.cbegin(), + _argumentTypes.cend(), + paramTypes.cbegin(), + [](TypePointer const& argumentType, TypePointer const& parameterType) + { + return argumentType->isImplicitlyConvertibleTo(*parameterType); + } + ); +} + +bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const +{ + if (m_parameterTypes.size() != _other.m_parameterTypes.size()) + return false; + return equal( + m_parameterTypes.cbegin(), + m_parameterTypes.cend(), + _other.m_parameterTypes.cbegin(), + [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; } + ); +} + +bool FunctionType::isBareCall() const +{ + switch (m_location) + { + case Location::Bare: + case Location::BareCallCode: + case Location::ECRecover: + case Location::SHA256: + case Location::RIPEMD160: + return true; + default: + return false; + } +} + +string FunctionType::externalSignature() const +{ + solAssert(m_declaration != nullptr, "External signature of function needs declaration"); + + bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary(); + + string ret = m_declaration->name() + "("; + + FunctionTypePointer external = interfaceFunctionType(); + solAssert(!!external, "External function type requested."); + TypePointers externalParameterTypes = external->parameterTypes(); + for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) + { + solAssert(!!(*it), "Parameter should have external type"); + ret += (*it)->canonicalName(_inLibrary) + (it + 1 == externalParameterTypes.cend() ? "" : ","); + } + + return ret + ")"; +} + +u256 FunctionType::externalIdentifier() const +{ + return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature()))); +} + +TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) +{ + TypePointers pointers; + pointers.reserve(_types.size()); + for (string const& type: _types) + pointers.push_back(Type::fromElementaryTypeName(type)); + return pointers; +} + +TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const +{ + return make_shared<FunctionType>( + m_parameterTypes, + m_returnParameterTypes, + m_parameterNames, + m_returnParameterNames, + m_location, + m_arbitraryParameters, + m_declaration, + m_gasSet || _setGas, + m_valueSet || _setValue + ); +} + +FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const +{ + TypePointers parameterTypes; + for (auto const& t: m_parameterTypes) + { + auto refType = dynamic_cast<ReferenceType const*>(t.get()); + if (refType && refType->location() == DataLocation::CallData) + parameterTypes.push_back(refType->copyForLocation(DataLocation::Memory, false)); + else + parameterTypes.push_back(t); + } + + // Removes dynamic types. + TypePointers returnParameterTypes; + vector<string> returnParameterNames; + for (size_t i = 0; i < m_returnParameterTypes.size(); ++i) + if (m_returnParameterTypes[i]->calldataEncodedSize() > 0) + { + returnParameterTypes.push_back(m_returnParameterTypes[i]); + returnParameterNames.push_back(m_returnParameterNames[i]); + } + return make_shared<FunctionType>( + parameterTypes, + returnParameterTypes, + m_parameterNames, + returnParameterNames, + _inLibrary ? Location::CallCode : m_location, + m_arbitraryParameters, + m_declaration, + m_gasSet, + m_valueSet + ); +} + +vector<string> const FunctionType::parameterTypeNames(bool _addDataLocation) const +{ + vector<string> names; + for (TypePointer const& t: m_parameterTypes) + names.push_back(t->canonicalName(_addDataLocation)); + + return names; +} + +vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocation) const +{ + vector<string> names; + for (TypePointer const& t: m_returnParameterTypes) + names.push_back(t->canonicalName(_addDataLocation)); + + return names; +} + +ASTPointer<ASTString> FunctionType::documentation() const +{ + auto function = dynamic_cast<Documented const*>(m_declaration); + if (function) + return function->documentation(); + + return ASTPointer<ASTString>(); +} + +bool MappingType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + MappingType const& other = dynamic_cast<MappingType const&>(_other); + return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType; +} + +string MappingType::toString(bool _short) const +{ + return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")"; +} + +string MappingType::canonicalName(bool) const +{ + return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")"; +} + +bool TypeType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + TypeType const& other = dynamic_cast<TypeType const&>(_other); + return *actualType() == *other.actualType(); +} + +u256 TypeType::storageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable type type requested.")); +} + +unsigned TypeType::sizeOnStack() const +{ + if (auto contractType = dynamic_cast<ContractType const*>(m_actualType.get())) + if (contractType->contractDefinition().isLibrary()) + return 1; + return 0; +} + +MemberList const& TypeType::members() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + MemberList::MemberMap members; + if (m_actualType->category() == Category::Contract) + { + ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition(); + if (contract.isLibrary()) + for (auto const& it: contract.interfaceFunctions()) + members.push_back(MemberList::Member( + it.second->declaration().name(), + it.second->asMemberFunction(true), // use callcode + &it.second->declaration() + )); + else if (m_currentContract != nullptr) + { + auto const& currentBases = m_currentContract->annotation().linearizedBaseContracts; + if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) + // We are accessing the type of a base contract, so add all public and protected + // members. Note that this does not add inherited functions on purpose. + for (Declaration const* decl: contract.inheritableMembers()) + members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); + } + } + else if (m_actualType->category() == Category::Enum) + { + EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition(); + auto enumType = make_shared<EnumType>(enumDef); + for (ASTPointer<EnumValue> const& enumValue: enumDef.members()) + members.push_back(MemberList::Member(enumValue->name(), enumType)); + } + m_members.reset(new MemberList(members)); + } + return *m_members; +} + +ModifierType::ModifierType(const ModifierDefinition& _modifier) +{ + TypePointers params; + params.reserve(_modifier.parameters().size()); + for (ASTPointer<VariableDeclaration> const& var: _modifier.parameters()) + params.push_back(var->annotation().type); + swap(params, m_parameterTypes); +} + +u256 ModifierType::storageSize() const +{ + BOOST_THROW_EXCEPTION( + InternalCompilerError() + << errinfo_comment("Storage size of non-storable type type requested.")); +} + +bool ModifierType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + ModifierType const& other = dynamic_cast<ModifierType const&>(_other); + + if (m_parameterTypes.size() != other.m_parameterTypes.size()) + return false; + auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; + + if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), + other.m_parameterTypes.cbegin(), typeCompare)) + return false; + return true; +} + +string ModifierType::toString(bool _short) const +{ + string name = "modifier ("; + for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) + name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); + return name + ")"; +} + +MagicType::MagicType(MagicType::Kind _kind): + m_kind(_kind) +{ + switch (m_kind) + { + case Kind::Block: + m_members = MemberList({ + {"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"timestamp", make_shared<IntegerType>(256)}, + {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, + {"difficulty", make_shared<IntegerType>(256)}, + {"number", make_shared<IntegerType>(256)}, + {"gaslimit", make_shared<IntegerType>(256)} + }); + break; + case Kind::Message: + m_members = MemberList({ + {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"gas", make_shared<IntegerType>(256)}, + {"value", make_shared<IntegerType>(256)}, + {"data", make_shared<ArrayType>(DataLocation::CallData)}, + {"sig", make_shared<FixedBytesType>(4)} + }); + break; + case Kind::Transaction: + m_members = MemberList({ + {"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, + {"gasprice", make_shared<IntegerType>(256)} + }); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} + +bool MagicType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + MagicType const& other = dynamic_cast<MagicType const&>(_other); + return other.m_kind == m_kind; +} + +string MagicType::toString(bool) const +{ + switch (m_kind) + { + case Kind::Block: + return "block"; + case Kind::Message: + return "msg"; + case Kind::Transaction: + return "tx"; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); + } +} diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h new file mode 100644 index 00000000..2f75975f --- /dev/null +++ b/libsolidity/ast/Types.h @@ -0,0 +1,996 @@ +/* + 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 + * Solidity data types + */ + +#pragma once + +#include <memory> +#include <string> +#include <map> +#include <boost/noncopyable.hpp> +#include <libdevcore/Common.h> +#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/ast/ASTForward.h> +#include <libsolidity/parsing/Token.h> +#include <libdevcore/UndefMacros.h> + +namespace dev +{ +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>; + + +enum class DataLocation { Storage, CallData, Memory }; + +/** + * Helper class to compute storage offsets of members of structs and contracts. + */ +class StorageOffsets +{ +public: + /// Resets the StorageOffsets objects and determines the position in storage for each + /// of the elements of @a _types. + void computeOffsets(TypePointers const& _types); + /// @returns the offset of the given member, might be null if the member is not part of storage. + std::pair<u256, unsigned> const* offset(size_t _index) const; + /// @returns the total number of slots occupied by all members. + u256 const& storageSize() const { return m_storageSize; } + +private: + u256 m_storageSize; + std::map<size_t, std::pair<u256, unsigned>> m_offsets; +}; + +/** + * List of members of a type. + */ +class MemberList +{ +public: + struct Member + { + Member(std::string const& _name, TypePointer const& _type, Declaration const* _declaration = nullptr): + name(_name), + type(_type), + declaration(_declaration) + { + } + + std::string name; + TypePointer type; + Declaration const* declaration = nullptr; + }; + + using MemberMap = std::vector<Member>; + + MemberList() {} + explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} + MemberList& operator=(MemberList&& _other); + TypePointer memberType(std::string const& _name) const + { + TypePointer type; + for (auto const& it: m_memberTypes) + if (it.name == _name) + { + solAssert(!type, "Requested member type by non-unique name."); + type = it.type; + } + return type; + } + MemberMap membersByName(std::string const& _name) const + { + MemberMap members; + for (auto const& it: m_memberTypes) + if (it.name == _name) + members.push_back(it); + return members; + } + /// @returns the offset of the given member in storage slots and bytes inside a slot or + /// a nullptr if the member is not part of storage. + std::pair<u256, unsigned> const* memberStorageOffset(std::string const& _name) const; + /// @returns the number of storage slots occupied by the members. + u256 const& storageSize() const; + + MemberMap::const_iterator begin() const { return m_memberTypes.begin(); } + MemberMap::const_iterator end() const { return m_memberTypes.end(); } + +private: + MemberMap m_memberTypes; + mutable std::unique_ptr<StorageOffsets> m_storageOffsets; +}; + +/** + * Abstract base class that forms the root of the type hierarchy. + */ +class Type: private boost::noncopyable, public std::enable_shared_from_this<Type> +{ +public: + enum class Category + { + Integer, IntegerConstant, StringLiteral, Bool, Real, Array, + FixedBytes, Contract, Struct, Function, Enum, Tuple, + Mapping, TypeType, Modifier, Magic + }; + + /// @{ + /// @name Factory functions + /// Factory functions that convert an AST @ref TypeName to a Type. + static TypePointer fromElementaryTypeName(Token::Value _typeToken); + static TypePointer fromElementaryTypeName(std::string const& _name); + static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType); + static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length); + /// @} + + /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does + /// not fit any type. + static TypePointer forLiteral(Literal const& _literal); + /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise + static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); + + /// Calculates the + + virtual Category category() const = 0; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const + { + return isImplicitlyConvertibleTo(_convertTo); + } + /// @returns the resulting type of applying the given unary operator or an empty pointer if + /// this is not possible. + /// The default implementation does not allow any unary operator. + virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); } + /// @returns the resulting type of applying the given binary operator or an empty pointer if + /// this is not possible. + /// The default implementation allows comparison operators if a common type exists + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const + { + return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); + } + + virtual bool operator==(Type const& _other) const { return category() == _other.category(); } + virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); } + + /// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding + /// is not a simple big-endian encoding or the type cannot be stored in calldata. + /// If @a _padded then it is assumed that each element is padded to a multiple of 32 bytes. + virtual unsigned calldataEncodedSize(bool _padded) const { (void)_padded; return 0; } + /// @returns the size of this data type in bytes when stored in memory. For memory-reference + /// types, this is the size of the memory pointer. + virtual unsigned memoryHeadSize() const { return calldataEncodedSize(); } + /// Convenience version of @see calldataEncodedSize(bool) + unsigned calldataEncodedSize() const { return calldataEncodedSize(true); } + /// @returns true if the type is dynamically encoded in calldata + virtual bool isDynamicallySized() const { return false; } + /// @returns the number of storage slots required to hold this value in storage. + /// For dynamically "allocated" types, it returns the size of the statically allocated head, + virtual u256 storageSize() const { return 1; } + /// Multiple small types can be packed into a single storage slot. If such a packing is possible + /// this function @returns the size in bytes smaller than 32. Data is moved to the next slot if + /// it does not fit. + /// In order to avoid computation at runtime of whether such moving is necessary, structs and + /// array data (not each element) always start a new slot. + virtual unsigned storageBytes() const { return 32; } + /// Returns true if the type can be stored in storage. + virtual bool canBeStored() const { return true; } + /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. + virtual bool canLiveOutsideStorage() const { return true; } + /// Returns true if the type can be stored as a value (as opposed to a reference) on the stack, + /// i.e. it behaves differently in lvalue context and in value context. + virtual bool isValueType() const { return false; } + virtual unsigned sizeOnStack() const { return 1; } + /// @returns the mobile (in contrast to static) type corresponding to the given type. + /// This returns the corresponding integer type for IntegerConstantTypes and the pointer type + /// for storage reference types. + virtual TypePointer mobileType() const { return shared_from_this(); } + /// @returns true if this is a non-value type and the data of this type is stored at the + /// given location. + virtual bool dataStoredIn(DataLocation) const { return false; } + /// @returns the type of a temporary during assignment to a variable of the given type. + /// Specifically, returns the requested itself if it can be dynamically allocated (or is a value type) + /// and the mobile type otherwise. + virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const + { + return _targetType->dataStoredIn(DataLocation::Storage) ? mobileType() : _targetType; + } + + /// Returns the list of all members of this type. Default implementation: no members. + virtual MemberList const& members() const { return EmptyMemberList; } + /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. + TypePointer memberType(std::string const& _name) const { return members().memberType(_name); } + + virtual std::string toString(bool _short) const = 0; + std::string toString() const { return toString(false); } + /// @returns the canonical name of this type for use in function signatures. + /// @param _addDataLocation if true, includes data location for reference types if it is "storage". + virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); } + virtual u256 literalValue(Literal const*) const + { + BOOST_THROW_EXCEPTION( + InternalCompilerError() << + errinfo_comment("Literal value requested for type without literals.") + ); + } + + /// @returns a (simpler) type that is encoded in the same way for external function calls. + /// This for example returns address for contract types. + /// If there is no such type, returns an empty shared pointer. + virtual TypePointer encodingType() const { return TypePointer(); } + /// @returns a (simpler) type that is used when decoding this type in calldata. + virtual TypePointer decodingType() const { return encodingType(); } + /// @returns a type that will be used outside of Solidity for e.g. function signatures. + /// This for example returns address for contract types. + /// If there is no such type, returns an empty shared pointer. + /// @param _inLibrary if set, returns types as used in a library, e.g. struct and contract types + /// are returned without modification. + virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); } + +protected: + /// Convenience object used when returning an empty member list. + static const MemberList EmptyMemberList; +}; + +/** + * Any kind of integer type (signed, unsigned, address). + */ +class IntegerType: public Type +{ +public: + enum class Modifier + { + Unsigned, Signed, Address + }; + virtual Category category() const override { return Category::Integer; } + + explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; } + virtual unsigned storageBytes() const override { return m_bits / 8; } + virtual bool isValueType() const override { return true; } + + virtual MemberList const& members() const override { return isAddress() ? AddressMemberList : EmptyMemberList; } + + virtual std::string toString(bool _short) const override; + + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + int numBits() const { return m_bits; } + bool isAddress() const { return m_modifier == Modifier::Address; } + bool isSigned() const { return m_modifier == Modifier::Signed; } + + static const MemberList AddressMemberList; + +private: + int m_bits; + Modifier m_modifier; +}; + +/** + * Integer constants either literals or computed. Example expressions: 2, 2+10, ~10. + * There is one distinct type per value. + */ +class IntegerConstantType: public Type +{ +public: + virtual Category category() const override { return Category::IntegerConstant; } + + /// @returns true if the literal is a valid integer. + static bool isValidLiteral(Literal const& _literal); + + explicit IntegerConstantType(Literal const& _literal); + explicit IntegerConstantType(bigint _value): m_value(_value) {} + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + + virtual std::string toString(bool _short) const override; + virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer mobileType() const override; + + /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. + std::shared_ptr<IntegerType const> integerType() const; + +private: + bigint m_value; +}; + +/** + * Literal string, can be converted to bytes, bytesX or string. + */ +class StringLiteralType: public Type +{ +public: + virtual Category category() const override { return Category::StringLiteral; } + + explicit StringLiteralType(Literal const& _literal); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + { + return TypePointer(); + } + + virtual bool operator==(Type const& _other) const override; + + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override { return 0; } + + virtual std::string toString(bool) const override { return "literal_string \"" + m_value + "\""; } + virtual TypePointer mobileType() const override; + + std::string const& value() const { return m_value; } + +private: + std::string m_value; +}; + +/** + * Bytes type with fixed length of up to 32 bytes. + */ +class FixedBytesType: public Type +{ +public: + virtual Category category() const override { return Category::FixedBytes; } + + /// @returns the smallest bytes type for the given literal or an empty pointer + /// if no type fits. + static std::shared_ptr<FixedBytesType> smallestTypeForLiteral(std::string const& _literal); + + explicit FixedBytesType(int _bytes); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool operator==(Type const& _other) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } + virtual unsigned storageBytes() const override { return m_bytes; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + int numBytes() const { return m_bytes; } + +private: + int m_bytes; +}; + +/** + * The boolean type. + */ +class BoolType: public Type +{ +public: + BoolType() {} + virtual Category category() const override { return Category::Bool; } + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } + virtual unsigned storageBytes() const override { return 1; } + virtual bool isValueType() const override { return true; } + + virtual std::string toString(bool) const override { return "bool"; } + virtual u256 literalValue(Literal const* _literal) const override; + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } +}; + +/** + * Base class used by types which are not value types and can be stored either in storage, memory + * or calldata. This is currently used by arrays and structs. + */ +class ReferenceType: public Type +{ +public: + explicit ReferenceType(DataLocation _location): m_location(_location) {} + DataLocation location() const { return m_location; } + + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + { + return TypePointer(); + } + virtual unsigned memoryHeadSize() const override { return 32; } + + /// @returns a copy of this type with location (recursively) changed to @a _location, + /// whereas isPointer is only shallowly changed - the deep copy is always a bound reference. + virtual TypePointer copyForLocation(DataLocation _location, bool _isPointer) const = 0; + + virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } + virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; } + + /// Storage references can be pointers or bound references. In general, local variables are of + /// pointer type, state variables are bound references. Assignments to pointers or deleting + /// them will not modify storage (that will only change the pointer). Assignment from + /// non-storage objects to a variable of storage pointer type is not possible. + bool isPointer() const { return m_isPointer; } + + bool operator==(ReferenceType const& _other) const + { + return location() == _other.location() && isPointer() == _other.isPointer(); + } + + /// @returns a copy of @a _type having the same location as this (and is not a pointer type) + /// if _type is a reference type and an unmodified copy of _type otherwise. + /// This function is mostly useful to modify inner types appropriately. + static TypePointer copyForLocationIfReference(DataLocation _location, TypePointer const& _type); + +protected: + TypePointer copyForLocationIfReference(TypePointer const& _type) const; + /// @returns a human-readable description of the reference part of the type. + std::string stringForReferencePart() const; + + DataLocation m_location = DataLocation::Storage; + bool m_isPointer = true; +}; + +/** + * The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>]) + * and dynamically-sized array (<type>[]). + * In storage, all arrays are packed tightly (as long as more than one elementary type fits in + * one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and + * thus start on their own slot. + */ +class ArrayType: public ReferenceType +{ +public: + virtual Category category() const override { return Category::Array; } + + /// Constructor for a byte array ("bytes") and string. + explicit ArrayType(DataLocation _location, bool _isString = false): + ReferenceType(_location), + m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes), + m_baseType(std::make_shared<FixedBytesType>(1)) + { + } + /// Constructor for a dynamically sized array type ("type[]") + ArrayType(DataLocation _location, TypePointer const& _baseType): + ReferenceType(_location), + m_baseType(copyForLocationIfReference(_baseType)) + { + } + /// Constructor for a fixed-size array type ("type[20]") + ArrayType(DataLocation _location, TypePointer const& _baseType, u256 const& _length): + ReferenceType(_location), + m_baseType(copyForLocationIfReference(_baseType)), + m_hasDynamicLength(false), + m_length(_length) + {} + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool operator==(const Type& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded) const override; + virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } + virtual unsigned sizeOnStack() const override; + virtual std::string toString(bool _short) const override; + virtual std::string canonicalName(bool _addDataLocation) const override; + virtual MemberList const& members() const override; + virtual TypePointer encodingType() const override; + virtual TypePointer decodingType() const override; + virtual TypePointer interfaceType(bool _inLibrary) const override; + + /// @returns true if this is a byte array or a string + bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; } + /// @returns true if this is a string + bool isString() const { return m_arrayKind == ArrayKind::String; } + TypePointer const& baseType() const { solAssert(!!m_baseType, ""); return m_baseType;} + u256 const& length() const { return m_length; } + u256 memorySize() const; + + TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; + +private: + /// String is interpreted as a subtype of Bytes. + enum class ArrayKind { Ordinary, Bytes, String }; + + ///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays. + ArrayKind m_arrayKind = ArrayKind::Ordinary; + TypePointer m_baseType; + bool m_hasDynamicLength = true; + u256 m_length; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; +}; + +/** + * The type of a contract instance or library, there is one distinct type for each contract definition. + */ +class ContractType: public Type +{ +public: + virtual Category category() const override { return Category::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. + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded ) const override + { + return encodingType()->calldataEncodedSize(_padded); + } + virtual unsigned storageBytes() const override { return 20; } + virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool isValueType() const override { return true; } + virtual std::string toString(bool _short) const override; + virtual std::string canonicalName(bool _addDataLocation) const override; + + virtual MemberList const& members() const override; + virtual TypePointer encodingType() const override + { + return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); + } + virtual TypePointer interfaceType(bool _inLibrary) const override + { + return _inLibrary ? shared_from_this() : encodingType(); + } + + bool isSuper() const { return m_super; } + ContractDefinition const& contractDefinition() 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. + FunctionTypePointer const& constructorType() const; + + /// @returns the identifier of the function with the given name or Invalid256 if such a name does + /// not exist. + u256 functionIdentifier(std::string const& _functionName) const; + + /// @returns a list of all state variables (including inherited) of the contract and their + /// offsets in storage. + std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const; + +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 = false; + /// Type of the constructor, @see constructorType. Lazily initialized. + mutable FunctionTypePointer m_constructorType; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; +}; + +/** + * The type of a struct instance, there is one distinct type per struct definition. + */ +class StructType: public ReferenceType +{ +public: + virtual Category category() const override { return Category::Struct; } + explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): + ReferenceType(_location), m_struct(_struct) {} + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded) const override; + u256 memorySize() const; + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return true; } + virtual std::string toString(bool _short) const override; + + virtual MemberList const& members() const override; + virtual TypePointer encodingType() const override + { + return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer(); + } + virtual TypePointer interfaceType(bool _inLibrary) const override; + + TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override; + + virtual std::string canonicalName(bool _addDataLocation) const override; + + /// @returns a function that peforms the type conversion between a list of struct members + /// and a memory struct of this type. + FunctionTypePointer constructorType() const; + + std::pair<u256, unsigned> const& storageOffsetsOfMember(std::string const& _name) const; + u256 memoryOffsetOfMember(std::string const& _name) const; + + StructDefinition const& structDefinition() const { return m_struct; } + + /// @returns the set of all members that are removed in the memory version (typically mappings). + std::set<std::string> membersMissingInMemory() const; + +private: + StructDefinition const& m_struct; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; +}; + +/** + * The type of an enum instance, there is one distinct type per enum definition. + */ +class EnumType: public Type +{ +public: + virtual Category category() const override { return Category::Enum; } + explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded) const override + { + return encodingType()->calldataEncodedSize(_padded); + } + virtual unsigned storageBytes() const override; + virtual bool canLiveOutsideStorage() const override { return true; } + virtual std::string toString(bool _short) const override; + virtual std::string canonicalName(bool _addDataLocation) const override; + virtual bool isValueType() const override { return true; } + + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer encodingType() const override + { + return std::make_shared<IntegerType>(8 * int(storageBytes())); + } + virtual TypePointer interfaceType(bool _inLibrary) const override + { + return _inLibrary ? shared_from_this() : encodingType(); + } + + EnumDefinition const& enumDefinition() const { return m_enum; } + /// @returns the value that the string has in the Enum + unsigned int memberValue(ASTString const& _member) const; + +private: + EnumDefinition const& m_enum; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; +}; + +/** + * Type that can hold a finite sequence of values of different types. + * In some cases, the components are empty pointers (when used as placeholders). + */ +class TupleType: public Type +{ +public: + virtual Category category() const override { return Category::Tuple; } + explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {} + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual bool operator==(Type const& _other) const override; + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual std::string toString(bool) const override; + virtual bool canBeStored() const override { return false; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override; + virtual TypePointer mobileType() const override; + /// Converts components to their temporary types and performs some wildcard matching. + virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override; + + std::vector<TypePointer> const& components() const { return m_components; } + +private: + std::vector<TypePointer> const m_components; +}; + +/** + * The type of a function, identified by its (return) parameter types. + * @todo the return parameters should also have names, i.e. return parameters should be a struct + * type. + */ +class FunctionType: public Type +{ +public: + /// How this function is invoked on the EVM. + /// @todo This documentation is outdated, and Location should rather be named "Type" + enum class Location + { + Internal, ///< stack-call using plain JUMP + External, ///< external call using CALL + CallCode, ///< extercnal call using CALLCODE, i.e. not exchanging the storage + Bare, ///< CALL without function hash + BareCallCode, ///< CALLCODE without function hash + Creation, ///< external call using CREATE + Send, ///< CALL, but without data and gas + SHA3, ///< SHA3 + Suicide, ///< SUICIDE + ECRecover, ///< CALL to special contract for ecrecover + SHA256, ///< CALL to special contract for sha256 + RIPEMD160, ///< CALL to special contract for ripemd160 + Log0, + Log1, + Log2, + Log3, + Log4, + Event, ///< syntactic sugar for LOG* + SetGas, ///< modify the default gas value for the function call + SetValue, ///< modify the default value transfer for the function call + BlockHash, ///< BLOCKHASH + ArrayPush, ///< .push() to a dynamically sized array in storage + ByteArrayPush ///< .push() to a dynamically sized byte array in storage + }; + + virtual Category category() const override { return Category::Function; } + + /// Creates the type of a function. + explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); + /// Creates the accessor function type of a state variable. + explicit FunctionType(VariableDeclaration const& _varDecl); + /// Creates the function type of an event. + explicit FunctionType(EventDefinition const& _event); + FunctionType( + strings const& _parameterTypes, + strings const& _returnParameterTypes, + Location _location = Location::Internal, + bool _arbitraryParameters = false + ): FunctionType( + parseElementaryTypeVector(_parameterTypes), + parseElementaryTypeVector(_returnParameterTypes), + strings(), + strings(), + _location, + _arbitraryParameters + ) + { + } + FunctionType( + TypePointers const& _parameterTypes, + TypePointers const& _returnParameterTypes, + strings _parameterNames = strings(), + strings _returnParameterNames = strings(), + Location _location = Location::Internal, + bool _arbitraryParameters = false, + Declaration const* _declaration = nullptr, + bool _gasSet = false, + bool _valueSet = false + ): + m_parameterTypes(_parameterTypes), + m_returnParameterTypes(_returnParameterTypes), + m_parameterNames(_parameterNames), + m_returnParameterNames(_returnParameterNames), + m_location(_location), + m_arbitraryParameters(_arbitraryParameters), + m_gasSet(_gasSet), + m_valueSet(_valueSet), + m_declaration(_declaration) + {} + + TypePointers const& parameterTypes() const { return m_parameterTypes; } + std::vector<std::string> const& parameterNames() const { return m_parameterNames; } + std::vector<std::string> const parameterTypeNames(bool _addDataLocation) const; + TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } + std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; } + std::vector<std::string> const returnParameterTypeNames(bool _addDataLocation) const; + + virtual bool operator==(Type const& _other) const override; + virtual std::string toString(bool _short) const override; + virtual bool canBeStored() const override { return false; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override; + virtual MemberList const& members() const override; + + /// @returns TypePointer of a new FunctionType object. All input/return parameters are an + /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of + /// current function. + /// Returns an empty shared pointer if one of the input/return parameters does not have an + /// external type. + FunctionTypePointer interfaceFunctionType() const; + + /// @returns true if this function can take the given argument types (possibly + /// after implicit conversion). + bool canTakeArguments(TypePointers const& _arguments) const; + /// @returns true if the types of parameters are equal (does't check return parameter types) + bool hasEqualArgumentTypes(FunctionType const& _other) const; + + /// @returns true if the ABI is used for this call (only meaningful for external calls) + bool isBareCall() const; + Location const& location() const { return m_location; } + /// @returns the external signature of this function type given the function name + std::string externalSignature() const; + /// @returns the external identifier of this function (the hash of the signature). + u256 externalIdentifier() const; + Declaration const& declaration() 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> documentation() const; + + /// true iff arguments are to be padded to multiples of 32 bytes for external calls + bool padArguments() const { return !(m_location == Location::SHA3 || m_location == Location::SHA256 || m_location == Location::RIPEMD160); } + bool takesArbitraryParameters() const { return m_arbitraryParameters; } + bool gasSet() const { return m_gasSet; } + bool valueSet() const { return m_valueSet; } + + /// @returns a copy of this type, where gas or value are set manually. This will never set one + /// of the parameters to fals. + TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; + + /// @returns a copy of this function type where all return parameters of dynamic size are + /// removed and the location of reference types is changed from CallData to Memory. + /// This is needed if external functions are called on other contracts, as they cannot return + /// dynamic values. + /// @param _inLibrary if true, uses CallCode as location. + FunctionTypePointer asMemberFunction(bool _inLibrary) const; + +private: + static TypePointers parseElementaryTypeVector(strings const& _types); + + TypePointers m_parameterTypes; + TypePointers m_returnParameterTypes; + std::vector<std::string> m_parameterNames; + std::vector<std::string> m_returnParameterNames; + Location const m_location; + /// true if the function takes an arbitrary number of arguments of arbitrary types + bool const m_arbitraryParameters = false; + 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 = false; + mutable std::unique_ptr<MemberList> m_members; + Declaration const* m_declaration = nullptr; +}; + +/** + * The type of a mapping, there is one distinct type per key/value type pair. + * Mappings always occupy their own storage slot, but do not actually use it. + */ +class MappingType: public Type +{ +public: + virtual Category category() const override { return Category::Mapping; } + MappingType(TypePointer const& _keyType, TypePointer const& _valueType): + m_keyType(_keyType), m_valueType(_valueType) {} + + virtual bool operator==(Type const& _other) const override; + virtual std::string toString(bool _short) const override; + virtual std::string canonicalName(bool _addDataLocation) const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual TypePointer encodingType() const override + { + return std::make_shared<IntegerType>(256); + } + virtual TypePointer interfaceType(bool _inLibrary) const override + { + return _inLibrary ? shared_from_this() : TypePointer(); + } + + TypePointer const& keyType() const { return m_keyType; } + TypePointer const& valueType() const { return m_valueType; } + +private: + TypePointer m_keyType; + TypePointer m_valueType; +}; + +/** + * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example + * of a TypeType. + * For super contracts or libraries, this has members directly. + */ +class TypeType: public Type +{ +public: + virtual Category category() const override { return Category::TypeType; } + explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): + m_actualType(_actualType), m_currentContract(_currentContract) {} + TypePointer const& actualType() const { return m_actualType; } + + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual bool operator==(Type const& _other) const override; + virtual bool canBeStored() const override { return false; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override; + virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } + virtual MemberList const& members() const override; + +private: + TypePointer m_actualType; + /// Context in which this type is used (influences visibility etc.), can be nullptr. + ContractDefinition const* m_currentContract; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr<MemberList> m_members; +}; + + +/** + * The type of a function modifier. Not used for anything for now. + */ +class ModifierType: public Type +{ +public: + virtual Category category() const override { return Category::Modifier; } + explicit ModifierType(ModifierDefinition const& _modifier); + + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual bool canBeStored() const override { return false; } + virtual u256 storageSize() const override; + virtual bool canLiveOutsideStorage() const override { return false; } + virtual unsigned sizeOnStack() const override { return 0; } + virtual bool operator==(Type const& _other) const override; + virtual std::string toString(bool _short) const override; + +private: + TypePointers m_parameterTypes; +}; + + +/** + * Special type for magic variables (block, msg, tx), similar to a struct but without any reference + * (it always references a global singleton by name). + */ +class MagicType: public Type +{ +public: + enum class Kind { Block, Message, Transaction }; + virtual Category category() const override { return Category::Magic; } + + explicit MagicType(Kind _kind); + + virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + { + return TypePointer(); + } + + virtual bool operator==(Type const& _other) const override; + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned sizeOnStack() const override { return 0; } + virtual MemberList const& members() const override { return m_members; } + + virtual std::string toString(bool _short) const override; + +private: + Kind m_kind; + + MemberList m_members; +}; + +} +} |