Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
8936419186
  1. 269
      libsolidity/AST.cpp
  2. 133
      libsolidity/AST.h
  3. 126
      libsolidity/ASTPrinter.cpp
  4. 130
      libsolidity/ASTPrinter.h
  5. 72
      libsolidity/ASTVisitor.h
  6. 493
      libsolidity/AST_accept.h
  7. 67
      libsolidity/Compiler.cpp
  8. 20
      libsolidity/Compiler.h
  9. 26
      libsolidity/CompilerContext.cpp
  10. 13
      libsolidity/CompilerContext.h
  11. 22
      libsolidity/CompilerStack.cpp
  12. 28
      libsolidity/CompilerStack.h
  13. 71
      libsolidity/CompilerUtils.cpp
  14. 63
      libsolidity/CompilerUtils.h
  15. 4
      libsolidity/DeclarationContainer.cpp
  16. 15
      libsolidity/DeclarationContainer.h
  17. 100
      libsolidity/ExpressionCompiler.cpp
  18. 31
      libsolidity/ExpressionCompiler.h
  19. 8
      libsolidity/GlobalContext.cpp
  20. 8
      libsolidity/GlobalContext.h
  21. 84
      libsolidity/InterfaceHandler.cpp
  22. 21
      libsolidity/InterfaceHandler.h
  23. 22
      libsolidity/NameAndTypeResolver.cpp
  24. 14
      libsolidity/NameAndTypeResolver.h
  25. 5
      libsolidity/Parser.cpp
  26. 2
      libsolidity/SourceReferenceFormatter.cpp
  27. 2
      libsolidity/SourceReferenceFormatter.h
  28. 36
      libsolidity/Types.cpp
  29. 15
      libsolidity/Types.h
  30. 2
      solc/CMakeLists.txt
  31. 383
      solc/CommandLineInterface.cpp
  32. 71
      solc/CommandLineInterface.h
  33. 135
      solc/main.cpp
  34. 4
      test/solidityCompiler.cpp
  35. 35
      test/solidityEndToEndTest.cpp
  36. 88
      test/solidityNatspecJSON.cpp

269
libsolidity/AST.cpp

@ -25,6 +25,7 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h>
using namespace std; using namespace std;
@ -33,250 +34,6 @@ namespace dev
namespace solidity namespace solidity
{ {
void SourceUnit::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_nodes, _visitor);
_visitor.endVisit(*this);
}
void ImportDirective::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ContractDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_members, _visitor);
_visitor.endVisit(*this);
}
void StructDefinition::checkValidityOfMembers()
{
checkMemberTypes();
checkRecursion();
}
void ParameterList::accept(ASTVisitor& _visitor)
{
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);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void UserDefinedTypeName::accept(ASTVisitor& _visitor)
{
_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 Statement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
listAccept(m_statements, _visitor);
_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 BreakableStatement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void WhileStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_condition->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Continue::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Break::accept(ASTVisitor& _visitor)
{
_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 ExpressionStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_expression)
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->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 UnaryOperation::accept(ASTVisitor& _visitor)
{
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 FunctionCall::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_expression->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_expression->accept(_visitor);
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Identifier::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Literal::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
TypeError ASTNode::createTypeError(string const& _description) const TypeError ASTNode::createTypeError(string const& _description) const
{ {
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
@ -297,14 +54,14 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co
return exportedFunctions; return exportedFunctions;
} }
void StructDefinition::checkMemberTypes() void StructDefinition::checkMemberTypes() const
{ {
for (ASTPointer<VariableDeclaration> const& member: getMembers()) for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored()) if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
} }
void StructDefinition::checkRecursion() void StructDefinition::checkRecursion() const
{ {
set<StructDefinition const*> definitionsSeen; set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this}; vector<StructDefinition const*> queue = {this};
@ -319,7 +76,7 @@ void StructDefinition::checkRecursion()
for (ASTPointer<VariableDeclaration> const& member: def->getMembers()) for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT) if (member->getType()->getCategory() == Type::Category::STRUCT)
{ {
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName()); UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration())); queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
} }
} }
@ -532,7 +289,7 @@ void Identifier::checkTypeRequirements()
if (asserts(m_referencedDeclaration)) if (asserts(m_referencedDeclaration))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved."));
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration); VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
if (variable) if (variable)
{ {
if (!variable->getType()) if (!variable->getType())
@ -542,29 +299,29 @@ void Identifier::checkTypeRequirements()
return; return;
} }
//@todo can we unify these with TypeName::toType()? //@todo can we unify these with TypeName::toType()?
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration); StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
if (structDef) if (structDef)
{ {
// note that we do not have a struct type here // note that we do not have a struct type here
m_type = make_shared<TypeType>(make_shared<StructType>(*structDef)); m_type = make_shared<TypeType const>(make_shared<StructType const>(*structDef));
return; return;
} }
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
if (functionDef) if (functionDef)
{ {
// a function reference is not a TypeType, because calling a TypeType converts to the type. // a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion. // conversion.
m_type = make_shared<FunctionType>(*functionDef); m_type = make_shared<FunctionType const>(*functionDef);
return; return;
} }
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef) if (contractDef)
{ {
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef)); m_type = make_shared<TypeType const>(make_shared<ContractType>(*contractDef));
return; return;
} }
MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration); MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
if (magicVariable) if (magicVariable)
{ {
m_type = magicVariable->getType(); m_type = magicVariable->getType();
@ -575,7 +332,7 @@ void Identifier::checkTypeRequirements()
void ElementaryTypeNameExpression::checkTypeRequirements() void ElementaryTypeNameExpression::checkTypeRequirements()
{ {
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); m_type = make_shared<TypeType const>(Type::fromElementaryTypeName(m_typeToken));
} }
void Literal::checkTypeRequirements() void Literal::checkTypeRequirements()

133
libsolidity/AST.h

@ -39,6 +39,7 @@ namespace solidity
{ {
class ASTVisitor; class ASTVisitor;
class ASTConstVisitor;
/** /**
@ -54,12 +55,19 @@ public:
virtual ~ASTNode() {} virtual ~ASTNode() {}
virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0;
template <class T> template <class T>
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor)
{ {
for (ASTPointer<T>& element: _list) for (ASTPointer<T>& element: _list)
element->accept(_visitor); 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. /// Returns the source code location of this node.
Location const& getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
@ -89,6 +97,7 @@ public:
ASTNode(_location), m_nodes(_nodes) {} ASTNode(_location), m_nodes(_nodes) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<ASTNode>> getNodes() const { return m_nodes; } std::vector<ASTPointer<ASTNode>> getNodes() const { return m_nodes; }
@ -108,6 +117,7 @@ public:
ASTNode(_location), m_identifier(_identifier) {} ASTNode(_location), m_identifier(_identifier) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& getIdentifier() const { return *m_identifier; } ASTString const& getIdentifier() const { return *m_identifier; }
@ -122,18 +132,18 @@ class Declaration: public ASTNode
{ {
public: public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {} ASTNode(_location), m_name(_name), m_scope(nullptr) {}
/// @returns the declared name. /// @returns the declared name.
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Available only after name and type resolution step.
Declaration* getScope() const { return m_scope; } Declaration const* getScope() const { return m_scope; }
void setScope(Declaration* const& _scope) { m_scope = _scope; } void setScope(Declaration const* _scope) { m_scope = _scope; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Declaration* m_scope; Declaration const* m_scope;
}; };
/** /**
@ -146,21 +156,28 @@ class ContractDefinition: public Declaration
public: public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name), Declaration(_location, _name),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions) m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; } std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// Returns the functions that make up the calling interface in the intended order. /// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const; std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
@ -168,6 +185,7 @@ private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
}; };
class StructDefinition: public Declaration class StructDefinition: public Declaration
@ -178,16 +196,17 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _members): std::vector<ASTPointer<VariableDeclaration>> const& _members):
Declaration(_location, _name), m_members(_members) {} Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; } std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
/// Checks that the members do not include any recursive structs and have valid types /// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions). /// (e.g. no functions).
void checkValidityOfMembers(); void checkValidityOfMembers() const;
private: private:
void checkMemberTypes(); void checkMemberTypes() const;
void checkRecursion(); void checkRecursion() const;
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
}; };
@ -204,6 +223,7 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _parameters): std::vector<ASTPointer<VariableDeclaration>> const& _parameters):
ASTNode(_location), m_parameters(_parameters) {} ASTNode(_location), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; } std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters; }
@ -230,14 +250,15 @@ public:
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; } bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList& getParameterList() { return *m_parameters; } ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; } Block const& getBody() const { return *m_body; }
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
@ -270,15 +291,16 @@ public:
ASTPointer<ASTString> const& _name): ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {} Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
TypeName* getTypeName() const { return m_typeName.get(); } TypeName const* getTypeName() const { return m_typeName.get(); }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type. /// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@ -297,6 +319,8 @@ public:
Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {} Declaration(Location(), std::make_shared<ASTString>(_name)), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError() virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); } << errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
@ -315,11 +339,12 @@ class TypeName: public ASTNode
public: public:
explicit TypeName(Location const& _location): ASTNode(_location) {} explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver. /// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found. /// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type> toType() const = 0; virtual std::shared_ptr<Type const> toType() const = 0;
}; };
/** /**
@ -335,7 +360,8 @@ public:
if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromElementaryTypeName(m_type); } virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; } Token::Value getTypeName() const { return m_type; }
@ -350,18 +376,19 @@ class UserDefinedTypeName: public TypeName
{ {
public: public:
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name): UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name):
TypeName(_location), m_name(_name) {} TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromUserDefinedTypeName(*this); } virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
Declaration* m_referencedDeclaration; Declaration const* m_referencedDeclaration;
}; };
/** /**
@ -374,7 +401,8 @@ public:
ASTPointer<TypeName> const& _valueType): ASTPointer<TypeName> const& _valueType):
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() const override { return Type::fromMapping(*this); } virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); }
ElementaryTypeName const& getKeyType() const { return *m_keyType; } ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; } TypeName const& getValueType() const { return *m_valueType; }
@ -397,7 +425,6 @@ class Statement: public ASTNode
{ {
public: public:
explicit Statement(Location const& _location): ASTNode(_location) {} explicit Statement(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
/// Check all type requirements, throws exception if some requirement is not met. /// Check all type requirements, throws exception if some requirement is not met.
/// This includes checking that operators are applicable to their arguments but also that /// This includes checking that operators are applicable to their arguments but also that
@ -414,6 +441,7 @@ public:
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements): Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements):
Statement(_location), m_statements(_statements) {} Statement(_location), m_statements(_statements) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
@ -433,12 +461,13 @@ public:
Statement(_location), Statement(_location),
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; } Expression const& getCondition() const { return *m_condition; }
Statement& getTrueStatement() const { return *m_trueBody; } Statement const& getTrueStatement() const { return *m_trueBody; }
/// @returns the "else" part of the if statement or nullptr if there is no "else" part. /// @returns the "else" part of the if statement or nullptr if there is no "else" part.
Statement* getFalseStatement() const { return m_falseBody.get(); } Statement const* getFalseStatement() const { return m_falseBody.get(); }
private: private:
ASTPointer<Expression> m_condition; ASTPointer<Expression> m_condition;
@ -447,13 +476,12 @@ private:
}; };
/** /**
* Statement in which a break statement is legal. * Statement in which a break statement is legal (abstract class).
*/ */
class BreakableStatement: public Statement class BreakableStatement: public Statement
{ {
public: public:
BreakableStatement(Location const& _location): Statement(_location) {} BreakableStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
}; };
class WhileStatement: public BreakableStatement class WhileStatement: public BreakableStatement
@ -463,10 +491,11 @@ public:
ASTPointer<Statement> const& _body): ASTPointer<Statement> const& _body):
BreakableStatement(_location), m_condition(_condition), m_body(_body) {} BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getCondition() const { return *m_condition; } Expression const& getCondition() const { return *m_condition; }
Statement& getBody() const { return *m_body; } Statement const& getBody() const { return *m_body; }
private: private:
ASTPointer<Expression> m_condition; ASTPointer<Expression> m_condition;
@ -478,6 +507,7 @@ class Continue: public Statement
public: public:
Continue(Location const& _location): Statement(_location) {} Continue(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {} virtual void checkTypeRequirements() override {}
}; };
@ -486,6 +516,7 @@ class Break: public Statement
public: public:
Break(Location const& _location): Statement(_location) {} Break(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override {} virtual void checkTypeRequirements() override {}
}; };
@ -493,8 +524,9 @@ class Return: public Statement
{ {
public: public:
Return(Location const& _location, ASTPointer<Expression> _expression): Return(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {} Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
@ -504,7 +536,7 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
return *m_returnParameters; return *m_returnParameters;
} }
Expression* getExpression() const { return m_expression.get(); } Expression const* getExpression() const { return m_expression.get(); }
private: private:
ASTPointer<Expression> m_expression; ///< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
@ -525,10 +557,11 @@ public:
ASTPointer<Expression> _value): ASTPointer<Expression> _value):
Statement(_location), m_variable(_variable), m_value(_value) {} Statement(_location), m_variable(_variable), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; } VariableDeclaration const& getDeclaration() const { return *m_variable; }
Expression* getExpression() const { return m_value.get(); } Expression const* getExpression() const { return m_value.get(); }
private: private:
ASTPointer<VariableDeclaration> m_variable; ASTPointer<VariableDeclaration> m_variable;
@ -544,9 +577,10 @@ public:
ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression): ExpressionStatement(Location const& _location, ASTPointer<Expression> _expression):
Statement(_location), m_expression(_expression) {} Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
private: private:
ASTPointer<Expression> m_expression; ASTPointer<Expression> m_expression;
@ -608,11 +642,12 @@ public:
if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getLeftHandSide() const { return *m_leftHandSide; } Expression const& getLeftHandSide() const { return *m_leftHandSide; }
Token::Value getAssignmentOperator() const { return m_assigmentOperator; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
Expression& getRightHandSide() const { return *m_rightHandSide; } Expression const& getRightHandSide() const { return *m_rightHandSide; }
private: private:
ASTPointer<Expression> m_leftHandSide; ASTPointer<Expression> m_leftHandSide;
@ -635,11 +670,12 @@ public:
if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; } bool isPrefixOperation() const { return m_isPrefix; }
Expression& getSubExpression() const { return *m_subExpression; } Expression const& getSubExpression() const { return *m_subExpression; }
private: private:
Token::Value m_operator; Token::Value m_operator;
@ -661,10 +697,11 @@ public:
if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getLeftExpression() const { return *m_left; } Expression const& getLeftExpression() const { return *m_left; }
Expression& getRightExpression() const { return *m_right; } Expression const& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
Type const& getCommonType() const { return *m_commonType; } Type const& getCommonType() const { return *m_commonType; }
@ -688,10 +725,11 @@ public:
std::vector<ASTPointer<Expression>> const& _arguments): std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} Expression(_location), m_expression(_expression), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getExpression() const { return *m_expression; } Expression const& getExpression() const { return *m_expression; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; } std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// Returns true if this is not an actual function call, but an explicit type conversion /// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call. /// or constructor call.
@ -712,7 +750,8 @@ public:
ASTPointer<ASTString> const& _memberName): ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {} Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
Expression& getExpression() const { return *m_expression; } virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& getExpression() const { return *m_expression; }
ASTString const& getMemberName() const { return *m_memberName; } ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
@ -731,10 +770,11 @@ public:
ASTPointer<Expression> const& _index): ASTPointer<Expression> const& _index):
Expression(_location), m_base(_base), m_index(_index) {} Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getBaseExpression() const { return *m_base; } Expression const& getBaseExpression() const { return *m_base; }
Expression& getIndexExpression() const { return *m_index; } Expression const& getIndexExpression() const { return *m_index; }
private: private:
ASTPointer<Expression> m_base; ASTPointer<Expression> m_base;
@ -758,20 +798,21 @@ class Identifier: public PrimaryExpression
{ {
public: public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name) {} PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
/// Declaration the name refers to. /// Declaration the name refers to.
Declaration* m_referencedDeclaration; Declaration const* m_referencedDeclaration;
}; };
/** /**
@ -788,6 +829,7 @@ public:
if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError()); if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError());
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getTypeToken() const { return m_typeToken; } Token::Value getTypeToken() const { return m_typeToken; }
@ -805,6 +847,7 @@ public:
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value): Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value):
PrimaryExpression(_location), m_token(_token), m_value(_value) {} PrimaryExpression(_location), m_token(_token), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; } Token::Value getToken() const { return m_token; }

126
libsolidity/ASTPrinter.cpp

@ -30,7 +30,7 @@ namespace dev
namespace solidity namespace solidity
{ {
ASTPrinter::ASTPrinter(ASTNode& _ast, string const& _source): ASTPrinter::ASTPrinter(ASTNode const& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(&_ast) m_indentation(0), m_source(_source), m_ast(&_ast)
{ {
} }
@ -43,35 +43,35 @@ void ASTPrinter::print(ostream& _stream)
} }
bool ASTPrinter::visit(ImportDirective& _node) bool ASTPrinter::visit(ImportDirective const& _node)
{ {
writeLine("ImportDirective \"" + _node.getIdentifier() + "\""); writeLine("ImportDirective \"" + _node.getIdentifier() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ContractDefinition& _node) bool ASTPrinter::visit(ContractDefinition const& _node)
{ {
writeLine("ContractDefinition \"" + _node.getName() + "\""); writeLine("ContractDefinition \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(StructDefinition& _node) bool ASTPrinter::visit(StructDefinition const& _node)
{ {
writeLine("StructDefinition \"" + _node.getName() + "\""); writeLine("StructDefinition \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ParameterList& _node) bool ASTPrinter::visit(ParameterList const& _node)
{ {
writeLine("ParameterList"); writeLine("ParameterList");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(FunctionDefinition& _node) bool ASTPrinter::visit(FunctionDefinition const& _node)
{ {
writeLine("FunctionDefinition \"" + _node.getName() + "\"" + writeLine("FunctionDefinition \"" + _node.getName() + "\"" +
(_node.isPublic() ? " - public" : "") + (_node.isPublic() ? " - public" : "") +
@ -80,112 +80,112 @@ bool ASTPrinter::visit(FunctionDefinition& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(VariableDeclaration& _node) bool ASTPrinter::visit(VariableDeclaration const& _node)
{ {
writeLine("VariableDeclaration \"" + _node.getName() + "\""); writeLine("VariableDeclaration \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(TypeName& _node) bool ASTPrinter::visit(TypeName const& _node)
{ {
writeLine("TypeName"); writeLine("TypeName");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ElementaryTypeName& _node) bool ASTPrinter::visit(ElementaryTypeName const& _node)
{ {
writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName())); writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(UserDefinedTypeName& _node) bool ASTPrinter::visit(UserDefinedTypeName const& _node)
{ {
writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); writeLine("UserDefinedTypeName \"" + _node.getName() + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Mapping& _node) bool ASTPrinter::visit(Mapping const& _node)
{ {
writeLine("Mapping"); writeLine("Mapping");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Statement& _node) bool ASTPrinter::visit(Statement const& _node)
{ {
writeLine("Statement"); writeLine("Statement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Block& _node) bool ASTPrinter::visit(Block const& _node)
{ {
writeLine("Block"); writeLine("Block");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(IfStatement& _node) bool ASTPrinter::visit(IfStatement const& _node)
{ {
writeLine("IfStatement"); writeLine("IfStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(BreakableStatement& _node) bool ASTPrinter::visit(BreakableStatement const& _node)
{ {
writeLine("BreakableStatement"); writeLine("BreakableStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(WhileStatement& _node) bool ASTPrinter::visit(WhileStatement const& _node)
{ {
writeLine("WhileStatement"); writeLine("WhileStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Continue& _node) bool ASTPrinter::visit(Continue const& _node)
{ {
writeLine("Continue"); writeLine("Continue");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Break& _node) bool ASTPrinter::visit(Break const& _node)
{ {
writeLine("Break"); writeLine("Break");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Return& _node) bool ASTPrinter::visit(Return const& _node)
{ {
writeLine("Return"); writeLine("Return");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(VariableDefinition& _node) bool ASTPrinter::visit(VariableDefinition const& _node)
{ {
writeLine("VariableDefinition"); writeLine("VariableDefinition");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ExpressionStatement& _node) bool ASTPrinter::visit(ExpressionStatement const& _node)
{ {
writeLine("ExpressionStatement"); writeLine("ExpressionStatement");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Expression& _node) bool ASTPrinter::visit(Expression const& _node)
{ {
writeLine("Expression"); writeLine("Expression");
printType(_node); printType(_node);
@ -193,7 +193,7 @@ bool ASTPrinter::visit(Expression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Assignment& _node) bool ASTPrinter::visit(Assignment const& _node)
{ {
writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node); printType(_node);
@ -201,7 +201,7 @@ bool ASTPrinter::visit(Assignment& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(UnaryOperation& _node) bool ASTPrinter::visit(UnaryOperation const& _node)
{ {
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator())); ") " + Token::toString(_node.getOperator()));
@ -210,7 +210,7 @@ bool ASTPrinter::visit(UnaryOperation& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(BinaryOperation& _node) bool ASTPrinter::visit(BinaryOperation const& _node)
{ {
writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node); printType(_node);
@ -218,7 +218,7 @@ bool ASTPrinter::visit(BinaryOperation& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(FunctionCall& _node) bool ASTPrinter::visit(FunctionCall const& _node)
{ {
writeLine("FunctionCall"); writeLine("FunctionCall");
printType(_node); printType(_node);
@ -226,7 +226,7 @@ bool ASTPrinter::visit(FunctionCall& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(MemberAccess& _node) bool ASTPrinter::visit(MemberAccess const& _node)
{ {
writeLine("MemberAccess to member " + _node.getMemberName()); writeLine("MemberAccess to member " + _node.getMemberName());
printType(_node); printType(_node);
@ -234,7 +234,7 @@ bool ASTPrinter::visit(MemberAccess& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(IndexAccess& _node) bool ASTPrinter::visit(IndexAccess const& _node)
{ {
writeLine("IndexAccess"); writeLine("IndexAccess");
printType(_node); printType(_node);
@ -242,7 +242,7 @@ bool ASTPrinter::visit(IndexAccess& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(PrimaryExpression& _node) bool ASTPrinter::visit(PrimaryExpression const& _node)
{ {
writeLine("PrimaryExpression"); writeLine("PrimaryExpression");
printType(_node); printType(_node);
@ -250,7 +250,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(Identifier const& _node)
{ {
writeLine(string("Identifier ") + _node.getName()); writeLine(string("Identifier ") + _node.getName());
printType(_node); printType(_node);
@ -258,7 +258,7 @@ bool ASTPrinter::visit(Identifier& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
{ {
writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node); printType(_node);
@ -266,7 +266,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Literal& _node) bool ASTPrinter::visit(Literal const& _node)
{ {
char const* tokenString = Token::toString(_node.getToken()); char const* tokenString = Token::toString(_node.getToken());
if (!tokenString) if (!tokenString)
@ -277,157 +277,157 @@ bool ASTPrinter::visit(Literal& _node)
return goDeeper(); return goDeeper();
} }
void ASTPrinter::endVisit(ImportDirective&) void ASTPrinter::endVisit(ImportDirective const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ContractDefinition&) void ASTPrinter::endVisit(ContractDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(StructDefinition&) void ASTPrinter::endVisit(StructDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ParameterList&) void ASTPrinter::endVisit(ParameterList const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(FunctionDefinition&) void ASTPrinter::endVisit(FunctionDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(VariableDeclaration&) void ASTPrinter::endVisit(VariableDeclaration const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(TypeName&) void ASTPrinter::endVisit(TypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ElementaryTypeName&) void ASTPrinter::endVisit(ElementaryTypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(UserDefinedTypeName&) void ASTPrinter::endVisit(UserDefinedTypeName const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Mapping&) void ASTPrinter::endVisit(Mapping const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Statement&) void ASTPrinter::endVisit(Statement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Block&) void ASTPrinter::endVisit(Block const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(IfStatement&) void ASTPrinter::endVisit(IfStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(BreakableStatement&) void ASTPrinter::endVisit(BreakableStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(WhileStatement&) void ASTPrinter::endVisit(WhileStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Continue&) void ASTPrinter::endVisit(Continue const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Break&) void ASTPrinter::endVisit(Break const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Return&) void ASTPrinter::endVisit(Return const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(VariableDefinition&) void ASTPrinter::endVisit(VariableDefinition const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ExpressionStatement&) void ASTPrinter::endVisit(ExpressionStatement const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Expression&) void ASTPrinter::endVisit(Expression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Assignment&) void ASTPrinter::endVisit(Assignment const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(UnaryOperation&) void ASTPrinter::endVisit(UnaryOperation const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(BinaryOperation&) void ASTPrinter::endVisit(BinaryOperation const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(FunctionCall&) void ASTPrinter::endVisit(FunctionCall const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(MemberAccess&) void ASTPrinter::endVisit(MemberAccess const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(IndexAccess&) void ASTPrinter::endVisit(IndexAccess const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(PrimaryExpression&) void ASTPrinter::endVisit(PrimaryExpression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Identifier&) void ASTPrinter::endVisit(Identifier const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ElementaryTypeNameExpression&) void ASTPrinter::endVisit(ElementaryTypeNameExpression const&)
{ {
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(Literal&) void ASTPrinter::endVisit(Literal const&)
{ {
m_indentation--; m_indentation--;
} }

130
libsolidity/ASTPrinter.h

@ -33,78 +33,78 @@ namespace solidity
/** /**
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes. * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
*/ */
class ASTPrinter: public ASTVisitor class ASTPrinter: public ASTConstVisitor
{ {
public: public:
/// Create a printer for the given abstract syntax tree. If the source is specified, /// 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. /// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTNode& _ast, std::string const& _source = std::string()); ASTPrinter(ASTNode const& _ast, std::string const& _source = std::string());
/// Output the string representation of the AST to _stream. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);
bool visit(ImportDirective& _node) override; bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition& _node) override; bool visit(ContractDefinition const& _node) override;
bool visit(StructDefinition& _node) override; bool visit(StructDefinition const& _node) override;
bool visit(ParameterList& _node) override; bool visit(ParameterList const& _node) override;
bool visit(FunctionDefinition& _node) override; bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration& _node) override; bool visit(VariableDeclaration const& _node) override;
bool visit(TypeName& _node) override; bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping& _node) override; bool visit(Mapping const& _node) override;
bool visit(Statement& _node) override; bool visit(Statement const& _node) override;
bool visit(Block& _node) override; bool visit(Block const& _node) override;
bool visit(IfStatement& _node) override; bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement& _node) override; bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement& _node) override; bool visit(WhileStatement const& _node) override;
bool visit(Continue& _node) override; bool visit(Continue const& _node) override;
bool visit(Break& _node) override; bool visit(Break const& _node) override;
bool visit(Return& _node) override; bool visit(Return const& _node) override;
bool visit(VariableDefinition& _node) override; bool visit(VariableDefinition const& _node) override;
bool visit(ExpressionStatement& _node) override; bool visit(ExpressionStatement const& _node) override;
bool visit(Expression& _node) override; bool visit(Expression const& _node) override;
bool visit(Assignment& _node) override; bool visit(Assignment const& _node) override;
bool visit(UnaryOperation& _node) override; bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation& _node) override; bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall& _node) override; bool visit(FunctionCall const& _node) override;
bool visit(MemberAccess& _node) override; bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess& _node) override; bool visit(IndexAccess const& _node) override;
bool visit(PrimaryExpression& _node) override; bool visit(PrimaryExpression const& _node) override;
bool visit(Identifier& _node) override; bool visit(Identifier const& _node) override;
bool visit(ElementaryTypeNameExpression& _node) override; bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal& _node) override; bool visit(Literal const& _node) override;
void endVisit(ImportDirective&) override; void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition&) override; void endVisit(ContractDefinition const&) override;
void endVisit(StructDefinition&) override; void endVisit(StructDefinition const&) override;
void endVisit(ParameterList&) override; void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition&) override; void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration&) override; void endVisit(VariableDeclaration const&) override;
void endVisit(TypeName&) override; void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping&) override; void endVisit(Mapping const&) override;
void endVisit(Statement&) override; void endVisit(Statement const&) override;
void endVisit(Block&) override; void endVisit(Block const&) override;
void endVisit(IfStatement&) override; void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement&) override; void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement&) override; void endVisit(WhileStatement const&) override;
void endVisit(Continue&) override; void endVisit(Continue const&) override;
void endVisit(Break&) override; void endVisit(Break const&) override;
void endVisit(Return&) override; void endVisit(Return const&) override;
void endVisit(VariableDefinition&) override; void endVisit(VariableDefinition const&) override;
void endVisit(ExpressionStatement&) override; void endVisit(ExpressionStatement const&) override;
void endVisit(Expression&) override; void endVisit(Expression const&) override;
void endVisit(Assignment&) override; void endVisit(Assignment const&) override;
void endVisit(UnaryOperation&) override; void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation&) override; void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall&) override; void endVisit(FunctionCall const&) override;
void endVisit(MemberAccess&) override; void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess&) override; void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression&) override; void endVisit(PrimaryExpression const&) override;
void endVisit(Identifier&) override; void endVisit(Identifier const&) override;
void endVisit(ElementaryTypeNameExpression&) override; void endVisit(ElementaryTypeNameExpression const&) override;
void endVisit(Literal&) override; void endVisit(Literal const&) override;
private: private:
void printSourcePart(ASTNode const& _node); void printSourcePart(ASTNode const& _node);
@ -115,7 +115,7 @@ private:
int m_indentation; int m_indentation;
std::string m_source; std::string m_source;
ASTNode* m_ast; ASTNode const* m_ast;
std::ostream* m_ostream; std::ostream* m_ostream;
}; };

72
libsolidity/ASTVisitor.h

@ -110,5 +110,77 @@ public:
virtual void endVisit(Literal&) { } virtual void endVisit(Literal&) { }
}; };
class ASTConstVisitor
{
public:
virtual bool visit(ASTNode const&) { return true; }
virtual bool visit(SourceUnit const&) { return true; }
virtual bool visit(ImportDirective const&) { return true; }
virtual bool visit(ContractDefinition const&) { return true; }
virtual bool visit(StructDefinition const&) { return true; }
virtual bool visit(ParameterList const&) { return true; }
virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; }
virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; }
virtual bool visit(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; }
virtual bool visit(Continue const&) { return true; }
virtual bool visit(Break const&) { return true; }
virtual bool visit(Return const&) { return true; }
virtual bool visit(VariableDefinition const&) { return true; }
virtual bool visit(ExpressionStatement const&) { return true; }
virtual bool visit(Expression const&) { return true; }
virtual bool visit(Assignment const&) { return true; }
virtual bool visit(UnaryOperation const&) { return true; }
virtual bool visit(BinaryOperation const&) { return true; }
virtual bool visit(FunctionCall const&) { return true; }
virtual bool visit(MemberAccess const&) { return true; }
virtual bool visit(IndexAccess const&) { return true; }
virtual bool visit(PrimaryExpression const&) { return true; }
virtual bool visit(Identifier const&) { return true; }
virtual bool visit(ElementaryTypeNameExpression const&) { return true; }
virtual bool visit(Literal const&) { return true; }
virtual void endVisit(ASTNode const&) { }
virtual void endVisit(SourceUnit const&) { }
virtual void endVisit(ImportDirective const&) { }
virtual void endVisit(ContractDefinition const&) { }
virtual void endVisit(StructDefinition const&) { }
virtual void endVisit(ParameterList const&) { }
virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { }
virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { }
virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { }
virtual void endVisit(Continue const&) { }
virtual void endVisit(Break const&) { }
virtual void endVisit(Return const&) { }
virtual void endVisit(VariableDefinition const&) { }
virtual void endVisit(ExpressionStatement const&) { }
virtual void endVisit(Expression const&) { }
virtual void endVisit(Assignment const&) { }
virtual void endVisit(UnaryOperation const&) { }
virtual void endVisit(BinaryOperation const&) { }
virtual void endVisit(FunctionCall const&) { }
virtual void endVisit(MemberAccess const&) { }
virtual void endVisit(IndexAccess const&) { }
virtual void endVisit(PrimaryExpression const&) { }
virtual void endVisit(Identifier const&) { }
virtual void endVisit(ElementaryTypeNameExpression const&) { }
virtual void endVisit(Literal const&) { }
};
} }
} }

493
libsolidity/AST_accept.h

@ -0,0 +1,493 @@
/*
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.h>
#include <libsolidity/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_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
}
void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_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 StructDefinition::checkValidityOfMembers() const
{
checkMemberTypes();
checkRecursion();
}
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);
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);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->accept(_visitor);
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
if (m_typeName)
m_typeName->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 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 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 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 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 VariableDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->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 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 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);
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
void IndexAccess::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
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);
}
}
}

67
libsolidity/Compiler.cpp

@ -26,13 +26,14 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
namespace dev { namespace dev {
namespace solidity { namespace solidity {
void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals) void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
@ -135,7 +136,7 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{ {
unsigned const numBytes = var->getType()->getCalldataEncodedSize(); unsigned const numBytes = var->getType()->getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation()) << errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
@ -154,18 +155,20 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
//@todo this can be also done more efficiently //@todo this can be also done more efficiently
unsigned dataOffset = 0; unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters(); vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i) for (unsigned i = 0; i < parameters.size(); ++i)
{ {
Type const& paramType = *parameters[i]->getType(); Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize(); unsigned numBytes = paramType.getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0 || numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError() BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported.")); << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
m_context << eth::dupInstruction(parameters.size() - i); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
if (numBytes != 32) if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes; dataOffset += numBytes;
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
@ -179,7 +182,7 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable); m_context.addStateVariable(*variable);
} }
bool Compiler::visit(FunctionDefinition& _function) bool Compiler::visit(FunctionDefinition const& _function)
{ {
//@todo to simplify this, the calling convention could by changed such that //@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn] // caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
@ -195,15 +198,12 @@ bool Compiler::visit(FunctionDefinition& _function)
// stack upon entry: [return address] [arg0] [arg1] ... [argn] // stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned const numArguments = _function.getParameters().size(); for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
unsigned const numReturnValues = _function.getReturnParameters().size();
unsigned const numLocalVariables = _function.getLocalVariables().size();
for (ASTPointer<VariableDeclaration> const& variable: _function.getParameters() + _function.getReturnParameters())
m_context.addVariable(*variable); m_context.addVariable(*variable);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables()) for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addVariable(*localVariable); m_context.addAndInitializeVariable(*localVariable);
m_context.initializeLocalVariables(numReturnValues + numLocalVariables);
_function.getBody().accept(*this); _function.getBody().accept(*this);
@ -215,12 +215,16 @@ bool Compiler::visit(FunctionDefinition& _function)
// Note that the fact that the return arguments are of increasing index is vital for this // Note that the fact that the return arguments are of increasing index is vital for this
// algorithm to work. // algorithm to work.
unsigned const argumentsSize = CompilerUtils::getSizeOnStack(_function.getParameters());
unsigned const returnValuesSize = CompilerUtils::getSizeOnStack(_function.getReturnParameters());
unsigned const localVariablesSize = CompilerUtils::getSizeOnStack(_function.getLocalVariables());
vector<int> stackLayout; vector<int> stackLayout;
stackLayout.push_back(numReturnValues); // target of return address stackLayout.push_back(returnValuesSize); // target of return address
stackLayout += vector<int>(numArguments, -1); // discard all arguments stackLayout += vector<int>(argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < numReturnValues; ++i) for (unsigned i = 0; i < returnValuesSize; ++i)
stackLayout.push_back(i); stackLayout.push_back(i);
stackLayout += vector<int>(numLocalVariables, -1); stackLayout += vector<int>(localVariablesSize, -1);
while (stackLayout.back() != int(stackLayout.size() - 1)) while (stackLayout.back() != int(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
@ -240,7 +244,7 @@ bool Compiler::visit(FunctionDefinition& _function)
return false; return false;
} }
bool Compiler::visit(IfStatement& _ifStatement) bool Compiler::visit(IfStatement const& _ifStatement)
{ {
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition()); ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump(); eth::AssemblyItem trueTag = m_context.appendConditionalJump();
@ -253,7 +257,7 @@ bool Compiler::visit(IfStatement& _ifStatement)
return false; return false;
} }
bool Compiler::visit(WhileStatement& _whileStatement) bool Compiler::visit(WhileStatement const& _whileStatement)
{ {
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
@ -275,58 +279,53 @@ bool Compiler::visit(WhileStatement& _whileStatement)
return false; return false;
} }
bool Compiler::visit(Continue&) bool Compiler::visit(Continue const&)
{ {
if (!m_continueTags.empty()) if (!m_continueTags.empty())
m_context.appendJumpTo(m_continueTags.back()); m_context.appendJumpTo(m_continueTags.back());
return false; return false;
} }
bool Compiler::visit(Break&) bool Compiler::visit(Break const&)
{ {
if (!m_breakTags.empty()) if (!m_breakTags.empty())
m_context.appendJumpTo(m_breakTags.back()); m_context.appendJumpTo(m_breakTags.back());
return false; return false;
} }
bool Compiler::visit(Return& _return) bool Compiler::visit(Return const& _return)
{ {
//@todo modifications are needed to make this work with functions returning multiple values //@todo modifications are needed to make this work with functions returning multiple values
if (Expression* expression = _return.getExpression()) if (Expression const* expression = _return.getExpression())
{ {
ExpressionCompiler::compileExpression(m_context, *expression); ExpressionCompiler::compileExpression(m_context, *expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable)); CompilerUtils(m_context).moveToStackVariable(firstVariable);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
m_context.appendJumpTo(m_returnTag); m_context.appendJumpTo(m_returnTag);
return false; return false;
} }
bool Compiler::visit(VariableDefinition& _variableDefinition) bool Compiler::visit(VariableDefinition const& _variableDefinition)
{ {
if (Expression* expression = _variableDefinition.getExpression()) if (Expression const* expression = _variableDefinition.getExpression())
{ {
ExpressionCompiler::compileExpression(m_context, *expression); ExpressionCompiler::compileExpression(m_context, *expression);
ExpressionCompiler::appendTypeConversion(m_context, ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(), *expression->getType(),
*_variableDefinition.getDeclaration().getType()); *_variableDefinition.getDeclaration().getType());
unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration()); CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset);
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
} }
return false; return false;
} }
bool Compiler::visit(ExpressionStatement& _expressionStatement) bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{ {
Expression& expression = _expressionStatement.getExpression(); Expression const& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression); ExpressionCompiler::compileExpression(m_context, expression);
// Type::Category category = expression.getType()->getCategory(); CompilerUtils(m_context).popStackElement(*expression.getType());
for (unsigned i = 0; i < expression.getType()->getSizeOnStack(); ++i)
m_context << eth::Instruction::POP;
return false; return false;
} }

20
libsolidity/Compiler.h

@ -27,12 +27,12 @@
namespace dev { namespace dev {
namespace solidity { namespace solidity {
class Compiler: private ASTVisitor class Compiler: private ASTConstVisitor
{ {
public: public:
Compiler(): m_returnTag(m_context.newTag()) {} Compiler(): m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals); void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); } bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
@ -48,14 +48,14 @@ private:
void registerStateVariables(ContractDefinition const& _contract); void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(FunctionDefinition& _function) override; virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement& _ifStatement) override; virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement& _whileStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(Continue& _continue) override; virtual bool visit(Continue const& _continue) override;
virtual bool visit(Break& _break) override; virtual bool visit(Break const& _break) override;
virtual bool visit(Return& _return) override; virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition& _variableDefinition) override; virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement& _expressionStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override;
CompilerContext m_context; CompilerContext m_context;

26
libsolidity/CompilerContext.cpp

@ -41,20 +41,30 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize(); m_stateVariablesSize += _declaration.getType()->getStorageSize();
} }
void CompilerContext::initializeLocalVariables(unsigned _numVariables) void CompilerContext::addVariable(VariableDeclaration const& _declaration)
{ {
if (_numVariables > 0) m_localVariables[&_declaration] = m_localVariablesSize;
m_localVariablesSize += _declaration.getType()->getSizeOnStack();
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
{ {
addVariable(_declaration);
unsigned const size = _declaration.getType()->getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
*this << u256(0); *this << u256(0);
for (unsigned i = 1; i < _numVariables; ++i) m_asm.adjustDeposit(-size);
*this << eth::Instruction::DUP1;
m_asm.adjustDeposit(-_numVariables);
} }
void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
} }
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{ {
return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end(); return m_localVariables.count(_declaration) > 0;
} }
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
@ -67,10 +77,10 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{ {
auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration); auto res = m_localVariables.find(&_declaration);
if (asserts(res != m_localVariables.end())) if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack."));
return unsigned(end(m_localVariables) - res - 1); return m_localVariablesSize - res->second - 1;
} }
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const

13
libsolidity/CompilerContext.h

@ -25,6 +25,7 @@
#include <ostream> #include <ostream>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Types.h> #include <libsolidity/Types.h>
namespace dev { namespace dev {
@ -43,9 +44,9 @@ public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables); void addVariable(VariableDeclaration const& _declaration);
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); } void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } void addFunction(FunctionDefinition const& _function);
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
@ -98,8 +99,10 @@ private:
u256 m_stateVariablesSize; u256 m_stateVariablesSize;
/// Storage offsets of state variables /// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables; std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack. /// Offsets of local variables on the stack (relative to stack base).
std::vector<Declaration const*> m_localVariables; std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons. /// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
}; };

22
libsolidity/CompilerStack.cpp

@ -82,7 +82,7 @@ void CompilerStack::parse(string const& _sourceCode)
parse(); parse();
} }
vector<string> CompilerStack::getContractNames() vector<string> CompilerStack::getContractNames() const
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
@ -116,29 +116,29 @@ bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
return getBytecode(); return getBytecode();
} }
bytes const& CompilerStack::getBytecode(string const& _contractName) bytes const& CompilerStack::getBytecode(string const& _contractName) const
{ {
return getContract(_contractName).bytecode; return getContract(_contractName).bytecode;
} }
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{ {
getContract(_contractName).compiler->streamAssembly(_outStream); getContract(_contractName).compiler->streamAssembly(_outStream);
} }
string const& CompilerStack::getInterface(std::string const& _contractName) string const& CompilerStack::getInterface(string const& _contractName) const
{ {
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE); return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
} }
std::string const& CompilerStack::getJsonDocumentation(std::string const& _contractName, DocumentationType _type) string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
Contract& contract = getContract(_contractName); Contract const& contract = getContract(_contractName);
std::unique_ptr<string>* doc; std::unique_ptr<string const>* doc;
switch (_type) switch (_type)
{ {
case DocumentationType::NATSPEC_USER: case DocumentationType::NATSPEC_USER:
@ -158,12 +158,12 @@ std::string const& CompilerStack::getJsonDocumentation(std::string const& _contr
return *(*doc); return *(*doc);
} }
Scanner const& CompilerStack::getScanner(string const& _sourceName) Scanner const& CompilerStack::getScanner(string const& _sourceName) const
{ {
return *getSource(_sourceName).scanner; return *getSource(_sourceName).scanner;
} }
SourceUnit& CompilerStack::getAST(string const& _sourceName) SourceUnit const& CompilerStack::getAST(string const& _sourceName) const
{ {
return *getSource(_sourceName).ast; return *getSource(_sourceName).ast;
} }
@ -217,7 +217,7 @@ void CompilerStack::resolveImports()
swap(m_sourceOrder, sourceOrder); swap(m_sourceOrder, sourceOrder);
} }
CompilerStack::Contract& CompilerStack::getContract(string const& _contractName) CompilerStack::Contract const& CompilerStack::getContract(string const& _contractName) const
{ {
if (m_contracts.empty()) if (m_contracts.empty())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
@ -229,7 +229,7 @@ CompilerStack::Contract& CompilerStack::getContract(string const& _contractName)
return it->second; return it->second;
} }
CompilerStack::Source& CompilerStack::getSource(string const& _sourceName) CompilerStack::Source const& CompilerStack::getSource(string const& _sourceName) const
{ {
auto it = m_sources.find(_sourceName); auto it = m_sources.find(_sourceName);
if (it == m_sources.end()) if (it == m_sources.end())

28
libsolidity/CompilerStack.h

@ -64,7 +64,7 @@ public:
/// Sets the given source code as the only source unit and parses it. /// Sets the given source code as the only source unit and parses it.
void parse(std::string const& _sourceCode); void parse(std::string const& _sourceCode);
/// Returns a list of the contract names in the sources. /// Returns a list of the contract names in the sources.
std::vector<std::string> getContractNames(); std::vector<std::string> getContractNames() const;
/// Compiles the source units that were previously added and parsed. /// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false); void compile(bool _optimize = false);
@ -72,23 +72,23 @@ public:
/// @returns the compiled bytecode /// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false); bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
bytes const& getBytecode(std::string const& _contractName = ""); bytes const& getBytecode(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream. /// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation. /// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = ""); void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
/// Returns a string representing the contract interface in JSON. /// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(std::string const& _contractName = ""); std::string const& getInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract's documentation in JSON. /// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile. /// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get. /// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c DocumentationType /// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type); std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Returns the previously used scanner, useful for counting lines during error reporting. /// Returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = ""); Scanner const& getScanner(std::string const& _sourceName = "") const;
SourceUnit& getAST(std::string const& _sourceName = ""); SourceUnit const& getAST(std::string const& _sourceName = "") const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information. /// scanning the source code - this is useful for printing exception information.
@ -112,9 +112,9 @@ private:
std::shared_ptr<Compiler> compiler; std::shared_ptr<Compiler> compiler;
bytes bytecode; bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler; std::shared_ptr<InterfaceHandler> interfaceHandler;
std::unique_ptr<std::string> interface; mutable std::unique_ptr<std::string const> interface;
std::unique_ptr<std::string> userDocumentation; mutable std::unique_ptr<std::string const> userDocumentation;
std::unique_ptr<std::string> devDocumentation; mutable std::unique_ptr<std::string const> devDocumentation;
Contract(); Contract();
}; };
@ -122,14 +122,14 @@ private:
void reset(bool _keepSources = false); void reset(bool _keepSources = false);
void resolveImports(); void resolveImports();
Contract& getContract(std::string const& _contractName = ""); Contract const& getContract(std::string const& _contractName = "") const;
Source& getSource(std::string const& _sourceName = ""); Source const& getSource(std::string const& _sourceName = "") const;
bool m_parseSuccessful; bool m_parseSuccessful;
std::map<std::string, Source> m_sources; std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext; std::shared_ptr<GlobalContext> m_globalContext;
std::vector<Source const*> m_sourceOrder; std::vector<Source const*> m_sourceOrder;
std::map<std::string, Contract> m_contracts; std::map<std::string const, Contract> m_contracts;
}; };
} }

71
libsolidity/CompilerUtils.cpp

@ -0,0 +1,71 @@
/*
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
* Routines used by both the compiler and the expression compiler.
*/
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/AST.h>
#include <libevmcore/Instruction.h>
using namespace std;
namespace dev
{
namespace solidity
{
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
unsigned const size = _variable.getType()->getSizeOnStack();
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < size; ++i)
m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP;
}
void CompilerUtils::copyToStackTop(unsigned _stackDepth, Type const& _type)
{
if (_stackDepth > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::dupInstruction(_stackDepth);
}
void CompilerUtils::popStackElement(Type const& _type)
{
unsigned const size = _type.getSizeOnStack();
for (unsigned i = 0; i < size; ++i)
m_context << eth::Instruction::POP;
}
unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
{
unsigned size = 0;
for (shared_ptr<Type const> const& type: _variableTypes)
size += type->getSizeOnStack();
return size;
}
}
}

63
libsolidity/CompilerUtils.h

@ -0,0 +1,63 @@
/*
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
* Routines used by both the compiler and the expression compiler.
*/
#pragma once
#include <libsolidity/CompilerContext.h>
#include <libsolidity/ASTForward.h>
namespace dev {
namespace solidity {
class Type; // forward
class CompilerUtils
{
public:
CompilerUtils(CompilerContext& _context): m_context(_context) {}
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
void copyToStackTop(unsigned _stackDepth, Type const& _type);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
template <class T>
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
private:
CompilerContext& m_context;
};
template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{
unsigned size = 0;
for (T const& variable: _variables)
size += variable->getType()->getSizeOnStack();
return size;
}
}
}

4
libsolidity/DeclarationContainer.cpp

@ -28,7 +28,7 @@ namespace dev
namespace solidity namespace solidity
{ {
bool DeclarationContainer::registerDeclaration(Declaration& _declaration, bool _update) bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _update)
{ {
if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end()) if (!_update && m_declarations.find(_declaration.getName()) != m_declarations.end())
return false; return false;
@ -36,7 +36,7 @@ bool DeclarationContainer::registerDeclaration(Declaration& _declaration, bool _
return true; return true;
} }
Declaration* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const Declaration const* DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{ {
auto result = m_declarations.find(_name); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())

15
libsolidity/DeclarationContainer.h

@ -39,18 +39,19 @@ namespace solidity
class DeclarationContainer class DeclarationContainer
{ {
public: public:
explicit DeclarationContainer(Declaration* _enclosingDeclaration = nullptr, DeclarationContainer* _enclosingContainer = nullptr): explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff /// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared. /// it was not yet declared.
bool registerDeclaration(Declaration& _declaration, bool _update = false); bool registerDeclaration(Declaration const& _declaration, bool _update = false);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration* getEnclosingDeclaration() const { return m_enclosingDeclaration; } Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
private: private:
Declaration* m_enclosingDeclaration; Declaration const* m_enclosingDeclaration;
DeclarationContainer* m_enclosingContainer; DeclarationContainer const* m_enclosingContainer;
std::map<ASTString, Declaration*> m_declarations; std::map<ASTString, Declaration const*> m_declarations;
}; };
} }

100
libsolidity/ExpressionCompiler.cpp

@ -26,13 +26,14 @@
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
#include <libsolidity/CompilerUtils.h>
using namespace std; using namespace std;
namespace dev { namespace dev {
namespace solidity { namespace solidity {
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression) void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression)
{ {
ExpressionCompiler compiler(_context); ExpressionCompiler compiler(_context);
_expression.accept(compiler); _expression.accept(compiler);
@ -45,7 +46,7 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
compiler.appendTypeConversion(_typeOnStack, _targetType); compiler.appendTypeConversion(_typeOnStack, _targetType);
} }
bool ExpressionCompiler::visit(Assignment& _assignment) bool ExpressionCompiler::visit(Assignment const& _assignment)
{ {
_assignment.getRightHandSide().accept(*this); _assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
@ -67,7 +68,7 @@ bool ExpressionCompiler::visit(Assignment& _assignment)
return false; return false;
} }
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation) void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
{ {
//@todo type checking and creating code for an operator should be in the same place: //@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so // the operator should know how to convert itself and to which types it applies, so
@ -128,10 +129,10 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
} }
} }
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{ {
Expression& leftExpression = _binaryOperation.getLeftExpression(); Expression const& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression(); Expression const& rightExpression = _binaryOperation.getRightExpression();
Type const& commonType = _binaryOperation.getCommonType(); Type const& commonType = _binaryOperation.getCommonType();
Token::Value const op = _binaryOperation.getOperator(); Token::Value const op = _binaryOperation.getOperator();
@ -158,7 +159,7 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
return false; return false;
} }
bool ExpressionCompiler::visit(FunctionCall& _functionCall) bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{ {
using Location = FunctionType::Location; using Location = FunctionType::Location;
if (_functionCall.isTypeConversion()) if (_functionCall.isTypeConversion())
@ -166,7 +167,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
//@todo struct construction //@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1)) if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front(); Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this); firstArgument.accept(*this);
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
_functionCall.getType()->getCategory() == Type::Category::INTEGER) _functionCall.getType()->getCategory() == Type::Category::INTEGER)
@ -174,14 +175,12 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
// explicit type conversion contract -> address, nothing to do. // explicit type conversion contract -> address, nothing to do.
} }
else else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
} }
}
else else
{ {
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()); FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments(); std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameterTypes().size())) if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
@ -203,13 +202,14 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
m_context.appendJump(); m_context.appendJump();
m_context << returnLabel; m_context << returnLabel;
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
// callee adds return parameters, but removes arguments and return label // callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(function.getReturnParameterTypes().size() - arguments.size() - 1); m_context.adjustStackOffset(returnParametersSize - CompilerUtils::getSizeOnStack(arguments) - 1);
// @todo for now, the return value of a function is its first return value, so remove // @todo for now, the return value of a function is its first return value, so remove
// all others // all others
for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i) for (unsigned i = 1; i < function.getReturnParameterTypes().size(); ++i)
m_context << eth::Instruction::POP; CompilerUtils(m_context).popStackElement(*function.getReturnParameterTypes()[i]);
break; break;
} }
case Location::EXTERNAL: case Location::EXTERNAL:
@ -299,7 +299,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
return false; return false;
} }
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
ASTString const& member = _memberAccess.getMemberName(); ASTString const& member = _memberAccess.getMemberName();
switch (_memberAccess.getExpression().getType()->getCategory()) switch (_memberAccess.getExpression().getType()->getCategory())
@ -356,7 +356,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{ {
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType()); StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue = LValue(m_context, LValue::STORAGE, *_memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; break;
} }
@ -365,7 +365,7 @@ void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
} }
} }
bool ExpressionCompiler::visit(IndexAccess& _indexAccess) bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this); _indexAccess.getIndexExpression().accept(*this);
@ -376,36 +376,36 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE; m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
m_context << u256(64) << u256(0) << eth::Instruction::SHA3; m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE); m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
return false; return false;
} }
void ExpressionCompiler::endVisit(Identifier& _identifier) void ExpressionCompiler::endVisit(Identifier const& _identifier)
{ {
Declaration* declaration = _identifier.getReferencedDeclaration(); Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration)) if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{ {
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
return; return;
} }
if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration)) if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{ {
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return; return;
} }
if (/*VariableDeclaration* varDef = */dynamic_cast<VariableDeclaration*>(declaration)) if (dynamic_cast<VariableDeclaration const*>(declaration))
{ {
m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration()); m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return; return;
} }
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
} }
void ExpressionCompiler::endVisit(Literal& _literal) void ExpressionCompiler::endVisit(Literal const& _literal)
{ {
switch (_literal.getType()->getCategory()) switch (_literal.getType()->getCategory())
{ {
@ -418,7 +418,7 @@ void ExpressionCompiler::endVisit(Literal& _literal)
} }
} }
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{ {
Token::Value const op = _binaryOperation.getOperator(); Token::Value const op = _binaryOperation.getOperator();
if (asserts(op == Token::OR || op == Token::AND)) if (asserts(op == Token::OR || op == Token::AND))
@ -565,6 +565,13 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
} }
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
m_stackSize(_dataType.getSizeOnStack())
{
}
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
{ {
switch (m_type) switch (m_type)
@ -575,6 +582,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(stackPos + 1); *m_context << eth::dupInstruction(stackPos + 1);
break; break;
} }
@ -583,7 +591,17 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::DUP1;
if (m_stackSize == 1)
*m_context << eth::Instruction::SLOAD; *m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_stackSize; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
break; break;
case MEMORY: case MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -604,12 +622,13 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{ {
case STACK: case STACK:
{ {
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
if (stackPos > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackPos > 0) else if (stackDiff > 0)
*m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression);
break; break;
@ -617,9 +636,27 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
case LValue::STORAGE: case LValue::STORAGE:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_move) // stack layout: value value ... value ref
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1; if (!_move) // copy values
{
if (m_stackSize + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
}
if (m_stackSize > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i)
{
if (i + 1 >= m_stackSize)
*m_context << eth::Instruction::SSTORE; *m_context << eth::Instruction::SSTORE;
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
break; break;
case LValue::MEMORY: case LValue::MEMORY:
if (!_expression.getType()->isValueType()) if (!_expression.getType()->isValueType())
@ -645,6 +682,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{ {
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration)) if (m_context->isLocalVariable(&_declaration))
{ {
m_type = STACK; m_type = STACK;

31
libsolidity/ExpressionCompiler.h

@ -41,11 +41,11 @@ class IntegerType;
* of EVM instructions. It needs a compiler context that is the same for the whole compilation * of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit. * unit.
*/ */
class ExpressionCompiler: private ASTVisitor class ExpressionCompiler: private ASTConstVisitor
{ {
public: public:
/// Compile the given @a _expression into the @a _context. /// Compile the given @a _expression into the @a _context.
static void compileExpression(CompilerContext& _context, Expression& _expression); static void compileExpression(CompilerContext& _context, Expression const& _expression);
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType); static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
@ -54,18 +54,18 @@ private:
ExpressionCompiler(CompilerContext& _compilerContext): ExpressionCompiler(CompilerContext& _compilerContext):
m_context(_compilerContext), m_currentLValue(m_context) {} m_context(_compilerContext), m_currentLValue(m_context) {}
virtual bool visit(Assignment& _assignment) override; virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override; virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override; virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall& _functionCall) override; virtual bool visit(FunctionCall const& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override; virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess& _indexAccess) override; virtual bool visit(IndexAccess const& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override; virtual void endVisit(Identifier const& _identifier) override;
virtual void endVisit(Literal& _literal) override; virtual void endVisit(Literal const& _literal) override;
///@{ ///@{
///@name Append code for various operator types ///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation); void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation);
void appendCompareOperatorCode(Token::Value _operator, Type const& _type); void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
@ -93,8 +93,7 @@ private:
enum LValueType { NONE, STACK, MEMORY, STORAGE }; enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0): LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0);
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
@ -129,14 +128,12 @@ private:
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset; unsigned m_baseStackOffset;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize;
}; };
CompilerContext& m_context; CompilerContext& m_context;
LValue m_currentLValue; LValue m_currentLValue;
/// If a "virtual" function (i.e. a bulit-in function without jump tag) is encountered, the
/// actual function is stored here. @todo prevent assignment or store it with assignment
enum class SpecialFunction { NONE, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160 };
SpecialFunction m_currentSpecialFunction;
}; };

8
libsolidity/GlobalContext.cpp

@ -68,16 +68,16 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
m_currentContract = &_contract; m_currentContract = &_contract;
} }
vector<Declaration*> GlobalContext::getDeclarations() const vector<Declaration const*> GlobalContext::getDeclarations() const
{ {
vector<Declaration*> declarations; vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1); declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<Declaration> const& variable: m_magicVariables) for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get()); declarations.push_back(variable.get());
return declarations; return declarations;
} }
MagicVariableDeclaration* GlobalContext::getCurrentThis() const MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
{ {
if (!m_thisPointer[m_currentContract]) if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>( m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(

8
libsolidity/GlobalContext.h

@ -47,17 +47,17 @@ class GlobalContext: private boost::noncopyable
public: public:
GlobalContext(); GlobalContext();
void setCurrentContract(ContractDefinition const& _contract); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration* getCurrentThis() const; MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables. /// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const; std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this". /// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration*> getDeclarations() const; std::vector<Declaration const*> getDeclarations() const;
private: private:
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables; std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
ContractDefinition const* m_currentContract; ContractDefinition const* m_currentContract;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer; std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
}; };
} }

84
libsolidity/InterfaceHandler.cpp

@ -75,7 +75,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (strPtr) if (strPtr)
{ {
resetUser(); resetUser();
parseDocString(*strPtr); parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_notice.empty()) if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear {// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice); user["notice"] = Json::Value(m_notice);
@ -95,6 +95,20 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
auto contractDoc = _contractDef.getDocumentation();
if (contractDoc)
{
m_contractAuthor.clear();
m_title.clear();
parseDocString(*contractDoc, CommentOwner::CONTRACT);
if (!m_contractAuthor.empty())
doc["author"] = m_contractAuthor;
if (!m_title.empty())
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
{ {
Json::Value method; Json::Value method;
@ -102,16 +116,21 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
if (strPtr) if (strPtr)
{ {
resetDev(); resetDev();
parseDocString(*strPtr); parseDocString(*strPtr, CommentOwner::FUNCTION);
if (!m_dev.empty()) if (!m_dev.empty())
method["details"] = Json::Value(m_dev); method["details"] = Json::Value(m_dev);
if (!m_author.empty())
method["author"] = m_author;
Json::Value params(Json::objectValue); Json::Value params(Json::objectValue);
for (auto const& pair: m_params) for (auto const& pair: m_params)
params[pair.first] = pair.second; params[pair.first] = pair.second;
if (!m_params.empty()) if (!m_params.empty())
method["params"] = params; method["params"] = params;
if (!m_return.empty()) if (!m_return.empty())
method["return"] = m_return; method["return"] = m_return;
@ -133,6 +152,7 @@ void InterfaceHandler::resetUser()
void InterfaceHandler::resetDev() void InterfaceHandler::resetDev()
{ {
m_dev.clear(); m_dev.clear();
m_author.clear();
m_return.clear(); m_return.clear();
m_params.clear(); m_params.clear();
} }
@ -193,10 +213,12 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string const& _tag) std::string const& _tag,
CommentOwner _owner)
{ {
// LTODO: need to check for @(start of a tag) between here and the end of line // LTODO: need to check for @(start of a tag) between here and the end of line
// for all cases // for all cases. Also somehow automate list of acceptable tags for each
// language construct since current way does not scale well.
if (m_lastTag == DocTagType::NONE || _tag != "") if (m_lastTag == DocTagType::NONE || _tag != "")
{ {
if (_tag == "dev") if (_tag == "dev")
@ -205,20 +227,37 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
else if (_tag == "return") else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
else if (_tag == "author")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
else
// LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
}
else if (_tag == "title")
{
if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
}
else if (_tag == "param") else if (_tag == "param")
return parseDocTagParam(_pos, _end); return parseDocTagParam(_pos, _end);
else else
{
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered"));
} }
}
else else
return appendDocTag(_pos, _end); return appendDocTag(_pos, _end, _owner);
} }
std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end) std::string::const_iterator _end,
CommentOwner _owner)
{ {
switch (m_lastTag) switch (m_lastTag)
{ {
@ -231,6 +270,29 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
case DocTagType::RETURN: case DocTagType::RETURN:
m_return += " "; m_return += " ";
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT)
{
m_contractAuthor += " ";
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
}
else if (_owner == CommentOwner::FUNCTION)
{
m_author += " ";
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT)
{
m_title += " ";
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
}
else
// LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
case DocTagType::PARAM: case DocTagType::PARAM:
return appendDocTagParam(_pos, _end); return appendDocTagParam(_pos, _end);
default: default:
@ -247,7 +309,7 @@ static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_i
return (spacePos < nlPos) ? spacePos : nlPos; return (spacePos < nlPos) ? spacePos : nlPos;
} }
void InterfaceHandler::parseDocString(std::string const& _string) void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner)
{ {
auto currPos = _string.begin(); auto currPos = _string.begin();
auto end = _string.end(); auto end = _string.end();
@ -265,10 +327,10 @@ void InterfaceHandler::parseDocString(std::string const& _string)
BOOST_THROW_EXCEPTION(DocstringParsingError() << BOOST_THROW_EXCEPTION(DocstringParsingError() <<
errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found"));
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
} }
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end); currPos = appendDocTag(currPos + 1, end, _owner);
else if (currPos != end) // skip the line if a newline was found else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1; currPos = nlPos + 1;
} }

21
libsolidity/InterfaceHandler.h

@ -45,7 +45,15 @@ enum class DocTagType: uint8_t
DEV, DEV,
NOTICE, NOTICE,
PARAM, PARAM,
RETURN RETURN,
AUTHOR,
TITLE
};
enum class CommentOwner
{
CONTRACT,
FUNCTION
}; };
class InterfaceHandler class InterfaceHandler
@ -89,12 +97,14 @@ private:
std::string::const_iterator _end); std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end); std::string::const_iterator _end);
void parseDocString(std::string const& _string); void parseDocString(std::string const& _string, CommentOwner _owner);
std::string::const_iterator appendDocTag(std::string::const_iterator _pos, std::string::const_iterator appendDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end); std::string::const_iterator _end,
CommentOwner _owner);
std::string::const_iterator parseDocTag(std::string::const_iterator _pos, std::string::const_iterator parseDocTag(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string const& _tag); std::string const& _tag,
CommentOwner _owner);
Json::StyledWriter m_writer; Json::StyledWriter m_writer;
@ -103,6 +113,9 @@ private:
std::string m_notice; std::string m_notice;
std::string m_dev; std::string m_dev;
std::string m_return; std::string m_return;
std::string m_contractAuthor;
std::string m_author;
std::string m_title;
std::vector<std::pair<std::string, std::string>> m_params; std::vector<std::pair<std::string, std::string>> m_params;
}; };

22
libsolidity/NameAndTypeResolver.cpp

@ -32,9 +32,9 @@ namespace solidity
{ {
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globals) NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{ {
for (Declaration* declaration: _globals) for (Declaration const* declaration: _globals)
m_scopes[nullptr].registerDeclaration(*declaration); m_scopes[nullptr].registerDeclaration(*declaration);
} }
@ -70,13 +70,14 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[nullptr]; m_currentScope = &m_scopes[nullptr];
} }
void NameAndTypeResolver::updateDeclaration(Declaration& _declaration) void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{ {
m_scopes[nullptr].registerDeclaration(_declaration, true); m_scopes[nullptr].registerDeclaration(_declaration, true);
_declaration.setScope(nullptr); if (asserts(_declaration.getScope() == nullptr))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Updated declaration outside global scope."));
} }
Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
{ {
auto iterator = m_scopes.find(_scope); auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes)) if (iterator == end(m_scopes))
@ -84,7 +85,7 @@ Declaration* NameAndTypeResolver::resolveName(ASTString const& _name, Declaratio
return iterator->second.resolveName(_name, false); return iterator->second.resolveName(_name, false);
} }
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive)
{ {
return m_currentScope->resolveName(_name, _recursive); return m_currentScope->resolveName(_name, _recursive);
} }
@ -146,7 +147,7 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true; return true;
} }
void DeclarationRegistrationHelper::enterNewSubScope(Declaration& _declaration) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{ {
map<ASTNode const*, DeclarationContainer>::iterator iter; map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded; bool newlyAdded;
@ -204,15 +205,14 @@ bool ReferencesResolver::visit(Return& _return)
return true; return true;
} }
bool ReferencesResolver::visit(Mapping& _mapping) bool ReferencesResolver::visit(Mapping&)
{ {
(void)_mapping;
return true; return true;
} }
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
{ {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); Declaration const* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName());
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));
@ -222,7 +222,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
bool ReferencesResolver::visit(Identifier& _identifier) bool ReferencesResolver::visit(Identifier& _identifier)
{ {
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); Declaration const* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));

14
libsolidity/NameAndTypeResolver.h

@ -41,27 +41,25 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable class NameAndTypeResolver: private boost::noncopyable
{ {
public: public:
explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals); explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
/// Registers all declarations found in the source unit. /// Registers all declarations found in the source unit.
void registerDeclarations(SourceUnit& _sourceUnit); void registerDeclarations(SourceUnit& _sourceUnit);
/// Resolves all names and types referenced from the given contract. /// Resolves all names and types referenced from the given contract.
void resolveNamesAndTypes(ContractDefinition& _contract); void resolveNamesAndTypes(ContractDefinition& _contract);
/// Updates the given global declaration (used for "this"). Not to be used with declarations /// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope. /// that create their own scope.
void updateDeclaration(Declaration& _declaration); void updateDeclaration(Declaration const& _declaration);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the contract). /// the global scope is used (i.e. the one containing only the contract).
/// @returns a pointer to the declaration on success or nullptr on failure. /// @returns a pointer to the declaration on success or nullptr on failure.
Declaration* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const; Declaration const* resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial /// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase. /// resolving phase.
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); Declaration const* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private: private:
/// Throws if @a _struct contains a recursive loop. Note that recursion via mappings is fine.
void checkForRecursion(StructDefinition const& _struct);
void reset(); void reset();
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
@ -91,12 +89,12 @@ private:
void endVisit(VariableDefinition& _variableDefinition); void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration); bool visit(VariableDeclaration& _declaration);
void enterNewSubScope(Declaration& _declaration); void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope(); void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope); void registerDeclaration(Declaration& _declaration, bool _opensScope);
std::map<ASTNode const*, DeclarationContainer>& m_scopes; std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration* m_currentScope; Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction; FunctionDefinition* m_currentFunction;
}; };

5
libsolidity/Parser.cpp

@ -112,6 +112,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
ASTPointer<ContractDefinition> Parser::parseContractDefinition() ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT); expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken(); ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
@ -146,7 +149,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
} }
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)

2
libsolidity/SourceReferenceFormatter.cpp

@ -61,7 +61,7 @@ void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
void SourceReferenceFormatter::printExceptionInformation(ostream& _stream, void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
Exception const& _exception, Exception const& _exception,
string const& _name, string const& _name,
CompilerStack& _compiler) CompilerStack const& _compiler)
{ {
Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
Scanner const* scanner; Scanner const* scanner;

2
libsolidity/SourceReferenceFormatter.h

@ -41,7 +41,7 @@ struct SourceReferenceFormatter
public: public:
static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner);
static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, static void printExceptionInformation(std::ostream& _stream, Exception const& _exception,
std::string const& _name, CompilerStack& _compiler); std::string const& _name, CompilerStack const& _compiler);
}; };
} }

36
libsolidity/Types.cpp

@ -32,7 +32,7 @@ namespace dev
namespace solidity namespace solidity
{ {
shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
{ {
if (asserts(Token::isElementaryTypeName(_typeToken))) if (asserts(Token::isElementaryTypeName(_typeToken)))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
@ -44,33 +44,33 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
if (bytes == 0) if (bytes == 0)
bytes = 32; bytes = 32;
int modifier = offset / 33; int modifier = offset / 33;
return make_shared<IntegerType>(bytes * 8, return make_shared<IntegerType const>(bytes * 8,
modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH); IntegerType::Modifier::HASH);
} }
else if (_typeToken == Token::ADDRESS) else if (_typeToken == Token::ADDRESS)
return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL) else if (_typeToken == Token::BOOL)
return make_shared<BoolType>(); return make_shared<BoolType const>();
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
} }
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{ {
Declaration const* declaration = _typeName.getReferencedDeclaration(); Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
return make_shared<StructType>(*structDef); return make_shared<StructType const>(*structDef);
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration)) else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
return make_shared<FunctionType>(*function); return make_shared<FunctionType const>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract); return make_shared<ContractType const>(*contract);
return shared_ptr<Type>(); return shared_ptr<Type const>();
} }
shared_ptr<Type> Type::fromMapping(Mapping const& _typeName) shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName)
{ {
shared_ptr<Type const> keyType = _typeName.getKeyType().toType(); shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
if (!keyType) if (!keyType)
@ -78,28 +78,28 @@ shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
shared_ptr<Type const> valueType = _typeName.getValueType().toType(); shared_ptr<Type const> valueType = _typeName.getValueType().toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType const>(keyType, valueType);
} }
shared_ptr<Type> Type::forLiteral(Literal const& _literal) shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
{ {
case Token::TRUE_LITERAL: case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL: case Token::FALSE_LITERAL:
return make_shared<BoolType>(); return make_shared<BoolType const>();
case Token::NUMBER: case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue()); return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL: case Token::STRING_LITERAL:
return shared_ptr<Type>(); // @todo add string literals return shared_ptr<Type const>(); // @todo add string literals
default: default:
return shared_ptr<Type>(); return shared_ptr<Type const>();
} }
} }
const MemberList Type::EmptyMemberList = MemberList(); const MemberList Type::EmptyMemberList = MemberList();
shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _literal) shared_ptr<IntegerType const> IntegerType::smallestTypeForLiteral(string const& _literal)
{ {
bigint value(_literal); bigint value(_literal);
bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-'); bool isSigned = value < 0 || (!_literal.empty() && _literal.front() == '-');
@ -108,8 +108,8 @@ shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _liter
value = ((-value) - 1) << 1; value = ((-value) - 1) << 1;
unsigned bytes = max(bytesRequired(value), 1u); unsigned bytes = max(bytesRequired(value), 1u);
if (bytes > 32) if (bytes > 32)
return shared_ptr<IntegerType>(); return shared_ptr<IntegerType const>();
return make_shared<IntegerType>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED); return make_shared<IntegerType const>(bytes * 8, isSigned ? Modifier::SIGNED : Modifier::UNSIGNED);
} }
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):

15
libsolidity/Types.h

@ -80,15 +80,15 @@ public:
///@{ ///@{
///@name Factory functions ///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type. /// Factory functions that convert an AST @ref TypeName to a Type.
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken); static std::shared_ptr<Type const> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static std::shared_ptr<Type const> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); static std::shared_ptr<Type const> fromMapping(Mapping const& _typeName);
static std::shared_ptr<Type> fromFunction(FunctionDefinition const& _function); static std::shared_ptr<Type const> fromFunction(FunctionDefinition const& _function);
/// @} /// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
/// not fit any type. /// not fit any type.
static std::shared_ptr<Type> forLiteral(Literal const& _literal); static std::shared_ptr<Type const> forLiteral(Literal const& _literal);
virtual Category getCategory() const = 0; virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
@ -148,7 +148,7 @@ public:
/// @returns the smallest integer type for the given literal or an empty pointer /// @returns the smallest integer type for the given literal or an empty pointer
/// if no type fits. /// if no type fits.
static std::shared_ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal); static std::shared_ptr<IntegerType const> smallestTypeForLiteral(std::string const& _literal);
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED);
@ -184,6 +184,7 @@ private:
class BoolType: public Type class BoolType: public Type
{ {
public: public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; } virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override virtual bool acceptsBinaryOperator(Token::Value _operator) const override
@ -286,7 +287,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
Location getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
private: private:
TypePointers m_parameterTypes; TypePointers m_parameterTypes;

2
solc/CMakeLists.txt

@ -8,6 +8,8 @@ set(EXECUTABLE solc)
add_executable(${EXECUTABLE} ${SRC_LIST}) add_executable(${EXECUTABLE} ${SRC_LIST})
target_link_libraries(${EXECUTABLE} boost_filesystem)
target_link_libraries(${EXECUTABLE} boost_program_options)
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
install( TARGETS ${EXECUTABLE} DESTINATION bin ) install( TARGETS ${EXECUTABLE} DESTINATION bin )

383
solc/CommandLineInterface.cpp

@ -0,0 +1,383 @@
/*
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 2014
* Solidity command line interface.
*/
#include "CommandLineInterface.h"
#include <string>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include "BuildInfo.h"
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
namespace po = boost::program_options;
namespace dev
{
namespace solidity
{
static void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
static inline bool argToStdout(po::variables_map const& _args, const char* _name)
{
return _args.count(_name) && _args[_name].as<OutputType>() != OutputType::FILE;
}
static bool needStdout(po::variables_map const& _args)
{
return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") ||
argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary");
}
static inline bool outputToFile(OutputType type)
{
return type == OutputType::FILE || type == OutputType::BOTH;
}
static inline bool outputToStdout(OutputType type)
{
return type == OutputType::STDOUT || type == OutputType::BOTH;
}
static std::istream& operator>>(std::istream& _in, OutputType& io_output)
{
std::string token;
_in >> token;
if (token == "stdout")
io_output = OutputType::STDOUT;
else if (token == "file")
io_output = OutputType::FILE;
else if (token == "both")
io_output = OutputType::BOTH;
else
throw boost::program_options::invalid_option_value(token);
return _in;
}
void CommandLineInterface::handleBinary(string const& _contract)
{
auto choice = m_args["binary"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Binary: " << endl;
cout << toHex(m_compiler.getBytecode(_contract)) << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".binary");
outFile << toHex(m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleOpcode(string const& _contract)
{
// TODO: Figure out why the wrong operator << (from boost) is used here
auto choice = m_args["opcode"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Opcodes: " << endl;
dev::operator<<(cout, m_compiler.getBytecode(_contract));
cout << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".opcode");
dev::operator<<(outFile, m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleBytecode(string const& _contract)
{
if (m_args.count("opcodes"))
handleOpcode(_contract);
if (m_args.count("binary"))
handleBinary(_contract);
}
void CommandLineInterface::handleJson(DocumentationType _type,
string const& _contract)
{
std::string argName;
std::string suffix;
std::string title;
switch(_type)
{
case DocumentationType::ABI_INTERFACE:
argName = "abi";
suffix = ".abi";
title = "Contract ABI";
break;
case DocumentationType::NATSPEC_USER:
argName = "natspec-user";
suffix = ".docuser";
title = "User Documentation";
break;
case DocumentationType::NATSPEC_DEV:
argName = "natspec-dev";
suffix = ".docdev";
title = "Developer Documentation";
break;
default:
// should never happen
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation _type"));
}
if (m_args.count(argName))
{
auto choice = m_args[argName].as<OutputType>();
if (outputToStdout(choice))
{
cout << title << endl;
cout << m_compiler.getJsonDocumentation(_contract, _type);
}
if (outputToFile(choice))
{
ofstream outFile(_contract + suffix);
outFile << m_compiler.getJsonDocumentation(_contract, _type);
outFile.close();
}
}
}
bool CommandLineInterface::parseArguments(int argc, char** argv)
{
#define OUTPUT_TYPE_STR "Legal values:\n" \
"\tstdout: Print it to standard output\n" \
"\tfile: Print it to a file with same name\n" \
"\tboth: Print both to a file and the stdout\n"
// Declare the supported options.
po::options_description desc("Allowed options");
desc.add_options()
("help", "Show help message and exit")
("version", "Show version and exit")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size")
("input-file", po::value<vector<string>>(), "input file")
("ast", po::value<OutputType>(),
"Request to output the AST of the contract. " OUTPUT_TYPE_STR)
("asm", po::value<OutputType>(),
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR)
("opcodes", po::value<OutputType>(),
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR)
("binary", po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
("abi", po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
("natspec-user", po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
("natspec-dev", po::value<OutputType>(),
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR);
#undef OUTPUT_TYPE_STR
// All positional options should be interpreted as input files
po::positional_options_description p;
p.add("input-file", -1);
// parse the compiler arguments
try
{
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args);
}
catch (po::error const& exception)
{
cout << exception.what() << endl;
return false;
}
po::notify(m_args);
if (m_args.count("help"))
{
cout << desc;
return false;
}
if (m_args.count("version"))
{
version();
return false;
}
return true;
}
bool CommandLineInterface::processInput()
{
if (!m_args.count("input-file"))
{
string s;
while (!cin.eof())
{
getline(cin, s);
m_sourceCodes["<stdin>"].append(s);
}
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
{
auto path = boost::filesystem::path(infile);
if (!boost::filesystem::exists(path))
{
cout << "Skipping non existant input file \"" << infile << "\"" << endl;
continue;
}
if (!boost::filesystem::is_regular_file(path))
{
cout << "\"" << infile << "\" is not a valid file. Skipping" << endl;
continue;
}
m_sourceCodes[infile] = asString(dev::contents(infile));
}
try
{
for (auto const& sourceCode: m_sourceCodes)
m_compiler.addSource(sourceCode.first, sourceCode.second);
// TODO: Perhaps we should not compile unless requested
m_compiler.compile(m_args["optimize"].as<bool>());
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler);
return false;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler);
return false;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler);
return false;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler);
return false;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler);
return false;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return false;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return false;
}
return true;
}
void CommandLineInterface::actOnInput()
{
// do we need AST output?
if (m_args.count("ast"))
{
auto choice = m_args["ast"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Syntax trees:" << endl << endl;
for (auto const& sourceCode: m_sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(cout);
}
}
if (outputToFile(choice))
{
for (auto const& sourceCode: m_sourceCodes)
{
boost::filesystem::path p(sourceCode.first);
ofstream outFile(p.stem().string() + ".ast");
ASTPrinter printer(m_compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(outFile);
outFile.close();
}
}
}
vector<string> contracts = m_compiler.getContractNames();
for (string const& contract: contracts)
{
if (needStdout(m_args))
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count("asm"))
{
auto choice = m_args["asm"].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;
m_compiler.streamAssembly(cout, contract);
}
if (outputToFile(choice))
{
ofstream outFile(contract + ".evm");
m_compiler.streamAssembly(outFile, contract);
outFile.close();
}
}
handleBytecode(contract);
handleJson(DocumentationType::ABI_INTERFACE, contract);
handleJson(DocumentationType::NATSPEC_DEV, contract);
handleJson(DocumentationType::NATSPEC_USER, contract);
} // end of contracts iteration
}
}
}

71
solc/CommandLineInterface.h

@ -0,0 +1,71 @@
/*
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 2014
* Solidity command line interface.
*/
#pragma once
#include <boost/program_options.hpp>
#include <libsolidity/CompilerStack.h>
namespace dev
{
namespace solidity
{
//forward declaration
enum class DocumentationType: uint8_t;
enum class OutputType: uint8_t
{
STDOUT,
FILE,
BOTH
};
class CommandLineInterface
{
public:
CommandLineInterface() {}
/// Parse command line arguments and return false if we should not continue
bool parseArguments(int argc, char** argv);
/// Parse the files and create source code objects
bool processInput();
/// Perform actions on the input depending on provided compiler arguments
void actOnInput();
private:
void handleBinary(std::string const& _contract);
void handleOpcode(std::string const& _contract);
void handleBytecode(std::string const& _contract);
void handleJson(DocumentationType _type,
std::string const& _contract);
/// Compiler arguments variable map
boost::program_options::variables_map m_args;
/// map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes;
/// Solidity compiler stack
dev::solidity::CompilerStack m_compiler;
};
}
}

135
solc/main.cpp

@ -20,137 +20,16 @@
* Solidity commandline compiler. * Solidity commandline compiler.
*/ */
#include <string> #include "CommandLineInterface.h"
#include <iostream>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
using namespace std;
using namespace dev;
using namespace solidity;
void help()
{
cout << "Usage solc [OPTIONS] <file>" << endl
<< "Options:" << endl
<< " -o,--optimize Optimize the bytecode for size." << endl
<< " -h,--help Show this help message and exit." << endl
<< " -V,--version Show the version and exit." << endl;
exit(0);
}
void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
vector<string> infiles; dev::solidity::CommandLineInterface cli;
bool optimize = false; if (!cli.parseArguments(argc, argv))
for (int i = 1; i < argc; ++i) return 1;
{ if (!cli.processInput())
string arg = argv[i]; return 1;
if (arg == "-o" || arg == "--optimize") cli.actOnInput();
optimize = true;
else if (arg == "-h" || arg == "--help")
help();
else if (arg == "-V" || arg == "--version")
version();
else
infiles.push_back(argv[i]);
}
map<string, string> sourceCodes;
if (infiles.empty())
{
string s;
while (!cin.eof())
{
getline(cin, s);
sourceCodes["<stdin>"].append(s);
}
}
else
for (string const& infile: infiles)
sourceCodes[infile] = asString(dev::contents(infile));
CompilerStack compiler;
try
{
for (auto const& sourceCode: sourceCodes)
compiler.addSource(sourceCode.first, sourceCode.second);
compiler.compile(optimize);
}
catch (ParserError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", compiler);
return -1;
}
catch (DeclarationError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", compiler);
return -1;
}
catch (TypeError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", compiler);
return -1;
}
catch (CompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", compiler);
return -1;
}
catch (InternalCompilerError const& exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", compiler);
return -1;
}
catch (Exception const& exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
return -1;
}
catch (...)
{
cerr << "Unknown exception during compilation." << endl;
return -1;
}
cout << "Syntax trees:" << endl << endl;
for (auto const& sourceCode: sourceCodes)
{
cout << endl << "======= " << sourceCode.first << " =======" << endl;
ASTPrinter printer(compiler.getAST(sourceCode.first), sourceCode.second);
printer.print(cout);
}
vector<string> contracts = compiler.getContractNames();
cout << endl << "Contracts:" << endl;
for (string const& contract: contracts)
{
cout << endl << "======= " << contract << " =======" << endl
<< "EVM assembly:" << endl;
compiler.streamAssembly(cout, contract);
cout << "Opcodes:" << endl
<< eth::disassemble(compiler.getBytecode(contract)) << endl
<< "Binary: " << toHex(compiler.getBytecode(contract)) << endl
<< "Interface specification: " << compiler.getJsonDocumentation(contract, DocumentationType::ABI_INTERFACE) << endl
<< "Natspec user documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_USER) << endl
<< "Natspec developer documentation: " << compiler.getJsonDocumentation(contract, DocumentationType::NATSPEC_DEV) << endl;
}
return 0; return 0;
} }

4
test/solidityCompiler.cpp

@ -125,8 +125,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
byte(Instruction::JUMP), // end of f byte(Instruction::JUMP), // end of f
byte(Instruction::JUMPDEST), // beginning of g byte(Instruction::JUMPDEST), // beginning of g
byte(Instruction::PUSH1), 0x0, byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1), // initialized e and h byte(Instruction::PUSH1), 0x0, // initialized e and h
byte(Instruction::PUSH1), byte(0x29 + shift), // ret address byte(Instruction::PUSH1), byte(0x2a + shift), // ret address
byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),
byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND), byte(Instruction::PUSH1), 0x3, byte(Instruction::PUSH1), 0xff, byte(Instruction::AND),

35
test/solidityEndToEndTest.cpp

@ -1025,6 +1025,41 @@ BOOST_AUTO_TEST_CASE(calls_to_this)
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10)); BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10));
} }
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
{
// note that a reference to another contract's function occupies two stack slots,
// so this tests correct stack slot allocation
char const* sourceCode = R"(
contract Helper {
function multiply(uint a, uint b) returns (uint c) {
return a * b;
}
}
contract Main {
Helper h;
function callHelper(uint a, uint b) returns (uint c) {
var fu = h.multiply;
var y = 9;
var ret = fu(a, b);
return ret + y;
}
function getHelper() returns (address haddress) {
return address(h);
}
function setHelper(address haddress) {
h = Helper(haddress);
}
})";
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

88
test/solidityNatspecJSON.cpp

@ -22,6 +22,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
#include <jsonrpc/json/json.h> #include <jsonrpc/json/json.h>
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
@ -393,6 +394,93 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_contract_no_doc)
{
char const* sourceCode = "contract test {\n"
" /// @dev Mul function\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_contract_doc)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_author_at_function)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @author John Doe\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\",\n"
" \"author\": \"John Doe\",\n"
" }\n"
" }\n"
"}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
" \"author\": \"Lefteris\","
" \"title\": \"Just a test contract\","
" \"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Mul function\"\n"
" }\n"
" }\n"
"}";
BOOST_CHECK_THROW(checkNatspec(sourceCode, natspec, false), DocstringParsingError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save