aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp59
-rw-r--r--AST.h153
-rw-r--r--ASTPrinter.cpp30
-rw-r--r--ASTPrinter.h3
-rw-r--r--ASTVisitor.h12
-rw-r--r--BaseTypes.h6
-rw-r--r--Compiler.cpp401
-rw-r--r--Compiler.h146
-rw-r--r--NameAndTypeResolver.cpp21
-rw-r--r--NameAndTypeResolver.h23
-rw-r--r--Parser.h13
-rw-r--r--Scanner.cpp45
-rw-r--r--Scanner.h36
-rw-r--r--Scope.cpp4
-rw-r--r--Scope.h10
-rw-r--r--SourceReferenceFormatter.cpp36
-rw-r--r--Token.h20
-rw-r--r--Types.cpp92
-rw-r--r--Types.h68
19 files changed, 991 insertions, 187 deletions
diff --git a/AST.cpp b/AST.cpp
index 91b4a42b..0b635339 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -26,6 +26,8 @@
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
+using namespace std;
+
namespace dev
{
namespace solidity
@@ -248,7 +250,7 @@ void Literal::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
-TypeError ASTNode::createTypeError(std::string const& _description)
+TypeError ASTNode::createTypeError(string const& _description)
{
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
@@ -263,7 +265,7 @@ void Statement::expectType(Expression& _expression, const Type& _expectedType)
void Block::checkTypeRequirements()
{
- for (std::shared_ptr<Statement> const& statement: m_statements)
+ for (shared_ptr<Statement> const& statement: m_statements)
statement->checkTypeRequirements();
}
@@ -291,7 +293,7 @@ void Break::checkTypeRequirements()
void Return::checkTypeRequirements()
{
- BOOST_ASSERT(m_returnParameters);
+ assert(m_returnParameters);
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
@@ -328,7 +330,7 @@ void Assignment::checkTypeRequirements()
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
{
- // complex assignment
+ // compound assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
}
@@ -339,7 +341,7 @@ void UnaryOperation::checkTypeRequirements()
// INC, DEC, NOT, BIT_NOT, DELETE
m_subExpression->checkTypeRequirements();
m_type = m_subExpression->getType();
- if (m_type->acceptsUnaryOperator(m_operator))
+ if (!m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
}
@@ -354,10 +356,10 @@ void BinaryOperation::checkTypeRequirements()
else
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation."));
if (Token::isCompareOp(m_operator))
- m_type = std::make_shared<BoolType>();
+ m_type = make_shared<BoolType>();
else
{
- BOOST_ASSERT(Token::isBinaryOp(m_operator));
+ assert(Token::isBinaryOp(m_operator));
m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
@@ -369,12 +371,12 @@ void FunctionCall::checkTypeRequirements()
m_expression->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
- Type const& expressionType = *m_expression->getType();
- Type::Category const category = expressionType.getCategory();
- if (category == Type::Category::TYPE)
+
+ Type const* expressionType = m_expression->getType().get();
+ if (isTypeConversion())
{
- TypeType const* type = dynamic_cast<TypeType const*>(&expressionType);
- BOOST_ASSERT(type);
+ TypeType const* type = dynamic_cast<TypeType const*>(expressionType);
+ assert(type);
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
@@ -384,15 +386,15 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type->getActualType();
}
- else if (category == Type::Category::FUNCTION)
+ else
{
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
- FunctionType const* function = dynamic_cast<FunctionType const*>(&expressionType);
- BOOST_ASSERT(function);
+ FunctionType const* function = dynamic_cast<FunctionType const*>(expressionType);
+ assert(function);
FunctionDefinition const& fun = function->getFunction();
- std::vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
+ vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
@@ -401,29 +403,32 @@ void FunctionCall::checkTypeRequirements()
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (fun.getReturnParameterList()->getParameters().empty())
- m_type = std::make_shared<VoidType>();
+ m_type = make_shared<VoidType>();
else
m_type = fun.getReturnParameterList()->getParameters().front()->getType();
}
- else
- BOOST_THROW_EXCEPTION(createTypeError("Type does not support invocation."));
+}
+
+bool FunctionCall::isTypeConversion() const
+{
+ return m_expression->getType()->getCategory() == Type::Category::TYPE;
}
void MemberAccess::checkTypeRequirements()
{
- BOOST_ASSERT(false); // not yet implemented
+ assert(false); // not yet implemented
// m_type = ;
}
void IndexAccess::checkTypeRequirements()
{
- BOOST_ASSERT(false); // not yet implemented
+ assert(false); // not yet implemented
// m_type = ;
}
void Identifier::checkTypeRequirements()
{
- BOOST_ASSERT(m_referencedDeclaration);
+ assert(m_referencedDeclaration);
//@todo these dynamic casts here are not really nice...
// is i useful to have an AST visitor here?
// or can this already be done in NameAndTypeResolver?
@@ -446,7 +451,7 @@ void Identifier::checkTypeRequirements()
if (structDef)
{
// note that we do not have a struct type here
- m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef));
+ m_type = make_shared<TypeType>(make_shared<StructType>(*structDef));
return;
}
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration);
@@ -455,21 +460,21 @@ void Identifier::checkTypeRequirements()
// 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
// conversion.
- m_type = std::make_shared<FunctionType>(*functionDef);
+ m_type = make_shared<FunctionType>(*functionDef);
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
if (contractDef)
{
- m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef));
+ m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return;
}
- BOOST_ASSERT(false); // declaration reference of unknown/forbidden type
+ assert(false); // declaration reference of unknown/forbidden type
}
void ElementaryTypeNameExpression::checkTypeRequirements()
{
- m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
+ m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
}
void Literal::checkTypeRequirements()
diff --git a/AST.h b/AST.h
index df146ab1..e6657280 100644
--- a/AST.h
+++ b/AST.h
@@ -40,6 +40,12 @@ namespace solidity
class ASTVisitor;
+
+/**
+ * The root (abstract) class of the AST inheritance tree.
+ * It is possible to traverse all direct and indirect children of an AST node by calling
+ * accept, providing an ASTVisitor.
+ */
class ASTNode: private boost::noncopyable
{
public:
@@ -55,28 +61,45 @@ public:
element->accept(_visitor);
}
+ /// Returns the source code location of this node.
Location const& getLocation() const { return m_location; }
/// Creates a @ref TypeError exception and decorates it with the location of the node and
/// the given description
TypeError createTypeError(std::string const& _description);
+ ///@{
+ ///@name equality operators
+ /// Equality relies on the fact that nodes cannot be copied.
+ bool operator==(ASTNode const& _other) const { return this == &_other; }
+ bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
+ ///@}
+
private:
Location m_location;
};
+/**
+ * Abstract AST class for a declaration (contract, function, struct, variable).
+ */
class Declaration: public ASTNode
{
public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {}
+ /// Returns the declared name.
const ASTString& getName() const { return *m_name; }
private:
ASTPointer<ASTString> m_name;
};
+/**
+ * Definition of a contract. This is the only AST nodes where child nodes are not visited in
+ * document order. It first visits all struct declarations, then all variable declarations and
+ * finally all function declarations.
+ */
class ContractDefinition: public Declaration
{
public:
@@ -116,9 +139,11 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
-/// Used as function parameter list and return list
-/// None of the parameters is allowed to contain mappings (not even recursively
-/// inside structs)
+/**
+ * Parameter list, used as function parameter list and return list.
+ * None of the parameters is allowed to contain mappings (not even recursively
+ * inside structs), but (@todo) this is not yet enforced.
+ */
class ParameterList: public ASTNode
{
public:
@@ -161,6 +186,10 @@ private:
ASTPointer<Block> m_body;
};
+/**
+ * Declaration of a variable. This can be used in various places, e.g. in function parameter
+ * lists, struct definitions and even function bodys.
+ */
class VariableDeclaration: public Declaration
{
public:
@@ -172,30 +201,38 @@ public:
bool isTypeGivenExplicitly() const { return bool(m_typeName); }
TypeName* getTypeName() const { return m_typeName.get(); }
- //! 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.
+ /// 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.
std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
- std::shared_ptr<Type const> m_type;
+ std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
-/// types
+/// Types
/// @{
+/**
+ * Abstract base class of a type name, can be any built-in or user-defined type.
+ */
class TypeName: public ASTNode
{
public:
explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
+ /// 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.
virtual std::shared_ptr<Type> toType() = 0;
};
-/// any pre-defined type that is not a mapping
+/**
+ * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings,
+ * contracts, functions, etc.
+ */
class ElementaryTypeName: public TypeName
{
public:
@@ -204,12 +241,16 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); }
- Token::Value getType() const { return m_type; }
+ Token::Value getTypeName() const { return m_type; }
private:
Token::Value m_type;
};
+/**
+ * Name referring to a user-defined type (i.e. a struct).
+ * @todo some changes are necessary if this is also used to refer to contract types later
+ */
class UserDefinedTypeName: public TypeName
{
public:
@@ -228,6 +269,9 @@ private:
StructDefinition* m_referencedStruct;
};
+/**
+ * A mapping type. Its source form is "mapping('keyType' => 'valueType')"
+ */
class Mapping: public TypeName
{
public:
@@ -247,23 +291,30 @@ private:
/// Statements
/// @{
+
+/**
+ * Abstract base class for statements.
+ */
class Statement: public ASTNode
{
public:
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.
- //! For expressions, this also returns the inferred type of the expression. For other
- //! statements, returns the empty pointer.
+ /// 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
+ /// the number of function call arguments matches the number of formal parameters and so forth.
virtual void checkTypeRequirements() = 0;
protected:
- //! Check that the inferred type for _expression is _expectedType or at least implicitly
- //! convertible to _expectedType. If not, throw exception.
+ /// Helper function, check that the inferred type for @a _expression is @a _expectedType or at
+ /// least implicitly convertible to @a _expectedType. If not, throw exception.
void expectType(Expression& _expression, Type const& _expectedType);
};
+/**
+ * Brace-enclosed block containing zero or more statements.
+ */
class Block: public Statement
{
public:
@@ -277,6 +328,10 @@ private:
std::vector<ASTPointer<Statement>> m_statements;
};
+/**
+ * If-statement with an optional "else" part. Note that "else if" is modeled by having a new
+ * if-statement as the false (else) body.
+ */
class IfStatement: public Statement
{
public:
@@ -293,6 +348,10 @@ private:
ASTPointer<Statement> m_falseBody; //< "else" part, optional
};
+/**
+ * Statement in which a break statement is legal.
+ * @todo actually check this requirement.
+ */
class BreakableStatement: public Statement
{
public:
@@ -343,9 +402,15 @@ public:
private:
ASTPointer<Expression> m_expression; //< value to return, optional
- ParameterList* m_returnParameters; //< extracted from the function declaration
+ /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
+ ParameterList* m_returnParameters;
};
+/**
+ * Definition of a variable as a statement inside a function. It requires a type name (which can
+ * also be "var") but the actual assignment can be missing.
+ * Examples: var a = 2; uint256 a;
+ */
class VariableDefinition: public Statement
{
public:
@@ -357,17 +422,22 @@ public:
private:
ASTPointer<VariableDeclaration> m_variable;
- ASTPointer<Expression> m_value; ///< can be missing
+ ASTPointer<Expression> m_value; ///< the assigned value, can be missing
};
+/**
+ * An expression, i.e. something that has a value (which can also be of type "void" in case
+ * of function calls).
+ */
class Expression: public Statement
{
public:
Expression(Location const& _location): Statement(_location) {}
+
std::shared_ptr<Type const> const& getType() const { return m_type; }
protected:
- //! Inferred type of the expression, only filled after a call to checkTypeRequirements().
+ /// Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
};
@@ -376,6 +446,10 @@ protected:
/// Expressions
/// @{
+/**
+ * Assignment, can also be a compound assignment.
+ * Examples: (a = 7 + 8) or (a *= 2)
+ */
class Assignment: public Expression
{
public:
@@ -386,7 +460,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
+ Expression& getLeftHandSide() const { return *m_leftHandSide; }
Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
+ Expression& getRightHandSide() const { return *m_rightHandSide; }
private:
ASTPointer<Expression> m_leftHandSide;
@@ -394,6 +470,10 @@ private:
ASTPointer<Expression> m_rightHandSide;
};
+/**
+ * Operation involving a unary operator, pre- or postfix.
+ * Examples: ++i, delete x or !true
+ */
class UnaryOperation: public Expression
{
public:
@@ -413,6 +493,10 @@ private:
bool m_isPrefix;
};
+/**
+ * Operation involving a binary operator.
+ * Examples: 1 + 2, true && false or 1 <= 4
+ */
class BinaryOperation: public Expression
{
public:
@@ -422,6 +506,8 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
+ Expression& getLeftExpression() const { return *m_left; }
+ Expression& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; }
private:
@@ -432,7 +518,9 @@ private:
std::shared_ptr<Type const> m_commonType;
};
-/// Can be ordinary function call, type cast or struct construction.
+/**
+ * Can be ordinary function call, type cast or struct construction.
+ */
class FunctionCall: public Expression
{
public:
@@ -442,11 +530,18 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
+ /// Returns true if this is not an actual function call, but an explicit type conversion
+ /// or constructor call.
+ bool isTypeConversion() const;
+
private:
ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments;
};
+/**
+ * Access to a member of an object. Example: x.name
+ */
class MemberAccess: public Expression
{
public:
@@ -462,6 +557,9 @@ private:
ASTPointer<ASTString> m_memberName;
};
+/**
+ * Index access to an array. Example: a[2]
+ */
class IndexAccess: public Expression
{
public:
@@ -476,12 +574,19 @@ private:
ASTPointer<Expression> m_index;
};
+/**
+ * Primary expression, i.e. an expression that cannot be divided any further. Examples are literals
+ * or variable references.
+ */
class PrimaryExpression: public Expression
{
public:
PrimaryExpression(Location const& _location): Expression(_location) {}
};
+/**
+ * An identifier, i.e. a reference to a declaration by name like a variable or function.
+ */
class Identifier: public PrimaryExpression
{
public:
@@ -491,16 +596,22 @@ public:
virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; }
+
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
- //! Declaration the name refers to.
+ /// Declaration the name refers to.
Declaration* m_referencedDeclaration;
};
+/**
+ * An elementary type name expression is used in expressions like "a = uint32(2)" to change the
+ * type of an expression explicitly. Here, "uint32" is the elementary type name expression and
+ * "uint32(2)" is a @ref FunctionCall.
+ */
class ElementaryTypeNameExpression: public PrimaryExpression
{
public:
@@ -515,6 +626,9 @@ private:
Token::Value m_typeToken;
};
+/**
+ * A literal string or number. @see Type::literalToBigEndian is used to actually parse its value.
+ */
class Literal: public PrimaryExpression
{
public:
@@ -524,6 +638,7 @@ public:
virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; }
+ /// @returns the non-parsed value of the literal
ASTString const& getValue() const { return *m_value; }
private:
diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp
index 44245ed4..9b545ac9 100644
--- a/ASTPrinter.cpp
+++ b/ASTPrinter.cpp
@@ -23,17 +23,19 @@
#include <libsolidity/ASTPrinter.h>
#include <libsolidity/AST.h>
+using namespace std;
+
namespace dev
{
namespace solidity
{
-ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source):
+ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(_ast)
{
}
-void ASTPrinter::print(std::ostream& _stream)
+void ASTPrinter::print(ostream& _stream)
{
m_ostream = &_stream;
m_ast->accept(*this);
@@ -87,7 +89,7 @@ bool ASTPrinter::visit(TypeName& _node)
bool ASTPrinter::visit(ElementaryTypeName& _node)
{
- writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType()));
+ writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
printSourcePart(_node);
return goDeeper();
}
@@ -179,7 +181,7 @@ bool ASTPrinter::visit(Expression& _node)
bool ASTPrinter::visit(Assignment& _node)
{
- writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
+ writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -187,7 +189,7 @@ bool ASTPrinter::visit(Assignment& _node)
bool ASTPrinter::visit(UnaryOperation& _node)
{
- writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
+ writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
@@ -196,7 +198,7 @@ bool ASTPrinter::visit(UnaryOperation& _node)
bool ASTPrinter::visit(BinaryOperation& _node)
{
- writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
+ writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -236,7 +238,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
bool ASTPrinter::visit(Identifier& _node)
{
- writeLine(std::string("Identifier ") + _node.getName());
+ writeLine(string("Identifier ") + _node.getName());
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -244,7 +246,7 @@ bool ASTPrinter::visit(Identifier& _node)
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{
- writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
+ writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -255,7 +257,7 @@ bool ASTPrinter::visit(Literal& _node)
char const* tokenString = Token::toString(_node.getToken());
if (!tokenString)
tokenString = "[no token]";
- writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue());
+ writeLine(string("Literal, token: ") + tokenString + " value: " + _node.getValue());
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -417,7 +419,7 @@ void ASTPrinter::printSourcePart(ASTNode const& _node)
{
Location const& location(_node.getLocation());
*m_ostream << getIndentation() << " Source: |"
- << m_source.substr(location.start, location.end - location.start) << "|" << std::endl;
+ << m_source.substr(location.start, location.end - location.start) << "|" << endl;
}
}
@@ -429,14 +431,14 @@ void ASTPrinter::printType(Expression const& _expression)
*m_ostream << getIndentation() << " Type unknown.\n";
}
-std::string ASTPrinter::getIndentation() const
+string ASTPrinter::getIndentation() const
{
- return std::string(m_indentation * 2, ' ');
+ return string(m_indentation * 2, ' ');
}
-void ASTPrinter::writeLine(std::string const& _line)
+void ASTPrinter::writeLine(string const& _line)
{
- *m_ostream << getIndentation() << _line << std::endl;
+ *m_ostream << getIndentation() << _line << endl;
}
}
diff --git a/ASTPrinter.h b/ASTPrinter.h
index 14592e2b..97256c05 100644
--- a/ASTPrinter.h
+++ b/ASTPrinter.h
@@ -30,6 +30,9 @@ namespace dev
namespace solidity
{
+/**
+ * Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
+ */
class ASTPrinter: public ASTVisitor
{
public:
diff --git a/ASTVisitor.h b/ASTVisitor.h
index 72f28768..e4818ee2 100644
--- a/ASTVisitor.h
+++ b/ASTVisitor.h
@@ -30,13 +30,17 @@ namespace dev
namespace solidity
{
+/**
+ * Visitor interface for the abstract syntax tree. This class is tightly bound to the
+ * implementation of @ref ASTNode::accept and its overrides. After a call to
+ * @ref ASTNode::accept, the function visit for the appropriate parameter is called and then
+ * (if it returns true) this continues recursively for all child nodes in document order
+ * (there is an exception for contracts). After all child nodes have been visited, endVisit is
+ * called for the node.
+ */
class ASTVisitor
{
public:
- /// These functions are called after a call to ASTNode::accept,
- /// first visit, then (if visit returns true) recursively for all
- /// child nodes in document order (exception for contracts) and then
- /// endVisit.
virtual bool visit(ASTNode&) { return true; }
virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; }
diff --git a/BaseTypes.h b/BaseTypes.h
index cfc14c7e..d1ffd7bb 100644
--- a/BaseTypes.h
+++ b/BaseTypes.h
@@ -29,8 +29,10 @@ namespace dev
namespace solidity
{
-/// Representation of an interval of source positions.
-/// The interval includes start and excludes end.
+/**
+ * Representation of an interval of source positions.
+ * The interval includes start and excludes end.
+ */
struct Location
{
Location(int _start, int _end): start(_start), end(_end) { }
diff --git a/Compiler.cpp b/Compiler.cpp
new file mode 100644
index 00000000..43cbc462
--- /dev/null
+++ b/Compiler.cpp
@@ -0,0 +1,401 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity AST to EVM bytecode compiler.
+ */
+
+#include <cassert>
+#include <utility>
+#include <libsolidity/AST.h>
+#include <libsolidity/Compiler.h>
+
+namespace dev {
+namespace solidity {
+
+void CompilerContext::setLabelPosition(uint32_t _label, uint32_t _position)
+{
+ assert(m_labelPositions.find(_label) == m_labelPositions.end());
+ m_labelPositions[_label] = _position;
+}
+
+uint32_t CompilerContext::getLabelPosition(uint32_t _label) const
+{
+ auto iter = m_labelPositions.find(_label);
+ assert(iter != m_labelPositions.end());
+ return iter->second;
+}
+
+void ExpressionCompiler::compile(Expression& _expression)
+{
+ m_assemblyItems.clear();
+ _expression.accept(*this);
+}
+
+bytes ExpressionCompiler::getAssembledBytecode() const
+{
+ bytes assembled;
+ assembled.reserve(m_assemblyItems.size());
+
+ // resolve label references
+ for (uint32_t pos = 0; pos < m_assemblyItems.size(); ++pos)
+ {
+ AssemblyItem const& item = m_assemblyItems[pos];
+ if (item.getType() == AssemblyItem::Type::LABEL)
+ m_context.setLabelPosition(item.getLabel(), pos + 1);
+ }
+
+ for (AssemblyItem const& item: m_assemblyItems)
+ if (item.getType() == AssemblyItem::Type::LABELREF)
+ assembled.push_back(m_context.getLabelPosition(item.getLabel()));
+ else
+ assembled.push_back(item.getData());
+
+ return assembled;
+}
+
+AssemblyItems ExpressionCompiler::compileExpression(CompilerContext& _context,
+ Expression& _expression)
+{
+ ExpressionCompiler compiler(_context);
+ compiler.compile(_expression);
+ return compiler.getAssemblyItems();
+}
+
+void ExpressionCompiler::endVisit(Assignment& _assignment)
+{
+ Expression& rightHandSide = _assignment.getRightHandSide();
+ Token::Value op = _assignment.getAssignmentOperator();
+ if (op != Token::ASSIGN)
+ {
+ // compound assignment
+ // @todo retrieve lvalue value
+ rightHandSide.accept(*this);
+ Type const& resultType = *_assignment.getType();
+ cleanHigherOrderBitsIfNeeded(*rightHandSide.getType(), resultType);
+ appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
+ }
+ else
+ rightHandSide.accept(*this);
+ // @todo store value
+}
+
+void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
+{
+ //@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
+ // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
+ // represents the operator
+ switch (_unaryOperation.getOperator())
+ {
+ case Token::NOT: // !
+ append(eth::Instruction::NOT);
+ break;
+ case Token::BIT_NOT: // ~
+ append(eth::Instruction::BNOT);
+ break;
+ case Token::DELETE: // delete
+ // a -> a xor a (= 0).
+ // @todo this should also be an assignment
+ // @todo semantics change for complex types
+ append(eth::Instruction::DUP1);
+ append(eth::Instruction::XOR);
+ break;
+ case Token::INC: // ++ (pre- or postfix)
+ // @todo this should also be an assignment
+ if (_unaryOperation.isPrefixOperation())
+ {
+ append(eth::Instruction::PUSH1);
+ append(1);
+ append(eth::Instruction::ADD);
+ }
+ break;
+ case Token::DEC: // -- (pre- or postfix)
+ // @todo this should also be an assignment
+ if (_unaryOperation.isPrefixOperation())
+ {
+ append(eth::Instruction::PUSH1);
+ append(1);
+ append(eth::Instruction::SWAP1); //@todo avoid this
+ append(eth::Instruction::SUB);
+ }
+ break;
+ case Token::ADD: // +
+ // unary add, so basically no-op
+ break;
+ case Token::SUB: // -
+ // unary -x translates into "0-x"
+ append(eth::Instruction::PUSH1);
+ append(0);
+ append(eth::Instruction::SUB);
+ break;
+ default:
+ assert(false); // invalid operation
+ }
+}
+
+bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
+{
+ Expression& leftExpression = _binaryOperation.getLeftExpression();
+ Expression& rightExpression = _binaryOperation.getRightExpression();
+ Type const& resultType = *_binaryOperation.getType();
+ Token::Value const op = _binaryOperation.getOperator();
+
+ if (op == Token::AND || op == Token::OR)
+ {
+ // special case: short-circuiting
+ appendAndOrOperatorCode(_binaryOperation);
+ }
+ else if (Token::isCompareOp(op))
+ {
+ leftExpression.accept(*this);
+ rightExpression.accept(*this);
+
+ // the types to compare have to be the same, but the resulting type is always bool
+ assert(*leftExpression.getType() == *rightExpression.getType());
+ appendCompareOperatorCode(op, *leftExpression.getType());
+ }
+ else
+ {
+ leftExpression.accept(*this);
+ cleanHigherOrderBitsIfNeeded(*leftExpression.getType(), resultType);
+ rightExpression.accept(*this);
+ cleanHigherOrderBitsIfNeeded(*rightExpression.getType(), resultType);
+ appendOrdinaryBinaryOperatorCode(op, resultType);
+ }
+
+ // do not visit the child nodes, we already did that explicitly
+ return false;
+}
+
+void ExpressionCompiler::endVisit(FunctionCall& _functionCall)
+{
+ if (_functionCall.isTypeConversion())
+ {
+ //@todo binary representation for all supported types (bool and int) is the same, so no-op
+ // here for now.
+ }
+ else
+ {
+ //@todo
+ }
+}
+
+void ExpressionCompiler::endVisit(MemberAccess&)
+{
+
+}
+
+void ExpressionCompiler::endVisit(IndexAccess&)
+{
+
+}
+
+void ExpressionCompiler::endVisit(Identifier&)
+{
+
+}
+
+void ExpressionCompiler::endVisit(Literal& _literal)
+{
+ switch (_literal.getType()->getCategory())
+ {
+ case Type::Category::INTEGER:
+ case Type::Category::BOOL:
+ {
+ bytes value = _literal.getType()->literalToBigEndian(_literal);
+ assert(value.size() <= 32);
+ assert(!value.empty());
+ append(static_cast<byte>(eth::Instruction::PUSH1) + static_cast<byte>(value.size() - 1));
+ append(value);
+ break;
+ }
+ default:
+ assert(false); // @todo
+ }
+}
+
+void ExpressionCompiler::cleanHigherOrderBitsIfNeeded(const Type& _typeOnStack, const Type& _targetType)
+{
+ // If the type of one of the operands is extended, we need to remove all
+ // higher-order bits that we might have ignored in previous operations.
+ // @todo: store in the AST whether the operand might have "dirty" higher
+ // order bits
+
+ if (_typeOnStack == _targetType)
+ return;
+ if (_typeOnStack.getCategory() == Type::Category::INTEGER &&
+ _targetType.getCategory() == Type::Category::INTEGER)
+ {
+ //@todo
+ }
+ else
+ {
+ // If we get here, there is either an implementation missing to clean higher oder bits
+ // for non-integer types that are explicitly convertible or we got here in error.
+ assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType));
+ assert(false); // these types should not be convertible.
+ }
+}
+
+void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
+{
+ Token::Value const op = _binaryOperation.getOperator();
+ assert(op == Token::OR || op == Token::AND);
+
+ _binaryOperation.getLeftExpression().accept(*this);
+ append(eth::Instruction::DUP1);
+ if (op == Token::AND)
+ append(eth::Instruction::NOT);
+ uint32_t endLabel = appendConditionalJump();
+ _binaryOperation.getRightExpression().accept(*this);
+ appendLabel(endLabel);
+}
+
+void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
+{
+ if (_operator == Token::EQ || _operator == Token::NE)
+ {
+ append(eth::Instruction::EQ);
+ if (_operator == Token::NE)
+ append(eth::Instruction::NOT);
+ }
+ else
+ {
+ IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
+ assert(type);
+ bool const isSigned = type->isSigned();
+
+ // note that EVM opcodes compare like "stack[0] < stack[1]",
+ // but our left value is at stack[1], so everyhing is reversed.
+ switch (_operator)
+ {
+ case Token::GTE:
+ append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
+ append(eth::Instruction::NOT);
+ break;
+ case Token::LTE:
+ append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
+ append(eth::Instruction::NOT);
+ break;
+ case Token::GT:
+ append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
+ break;
+ case Token::LT:
+ append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
+ break;
+ default:
+ assert(false);
+ }
+ }
+}
+
+void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
+{
+ if (Token::isArithmeticOp(_operator))
+ appendArithmeticOperatorCode(_operator, _type);
+ else if (Token::isBitOp(_operator))
+ appendBitOperatorCode(_operator);
+ else if (Token::isShiftOp(_operator))
+ appendShiftOperatorCode(_operator);
+ else
+ assert(false); // unknown binary operator
+}
+
+void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
+{
+ IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
+ assert(type);
+ bool const isSigned = type->isSigned();
+
+ switch (_operator)
+ {
+ case Token::ADD:
+ append(eth::Instruction::ADD);
+ break;
+ case Token::SUB:
+ append(eth::Instruction::SWAP1);
+ append(eth::Instruction::SUB);
+ break;
+ case Token::MUL:
+ append(eth::Instruction::MUL);
+ break;
+ case Token::DIV:
+ append(isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
+ break;
+ case Token::MOD:
+ append(isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
+{
+ switch (_operator)
+ {
+ case Token::BIT_OR:
+ append(eth::Instruction::OR);
+ break;
+ case Token::BIT_AND:
+ append(eth::Instruction::AND);
+ break;
+ case Token::BIT_XOR:
+ append(eth::Instruction::XOR);
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
+{
+ switch (_operator)
+ {
+ case Token::SHL:
+ assert(false); //@todo
+ break;
+ case Token::SAR:
+ assert(false); //@todo
+ break;
+ default:
+ assert(false);
+ }
+}
+
+uint32_t ExpressionCompiler::appendConditionalJump()
+{
+ uint32_t label = m_context.dispenseNewLabel();
+ append(eth::Instruction::PUSH1);
+ appendLabelref(label);
+ append(eth::Instruction::JUMPI);
+ return label;
+}
+
+void ExpressionCompiler::append(bytes const& _data)
+{
+ m_assemblyItems.reserve(m_assemblyItems.size() + _data.size());
+ for (byte b: _data)
+ append(b);
+}
+
+
+
+}
+}
diff --git a/Compiler.h b/Compiler.h
new file mode 100644
index 00000000..1401e12e
--- /dev/null
+++ b/Compiler.h
@@ -0,0 +1,146 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Solidity AST to EVM bytecode compiler.
+ */
+
+#include <libevmface/Instruction.h>
+#include <libsolidity/ASTVisitor.h>
+#include <libsolidity/Types.h>
+#include <libsolidity/Token.h>
+
+namespace dev {
+namespace solidity {
+
+/**
+ * A single item of compiled code that can be assembled to a single byte value in the final
+ * bytecode. Its main purpose is to inject jump labels and label references into the opcode stream,
+ * which can be resolved in the final step.
+ */
+class AssemblyItem
+{
+public:
+ enum class Type
+ {
+ CODE, //< m_data is opcode, m_label is empty.
+ DATA, //< m_data is actual data, m_label is empty
+ LABEL, //< m_data is JUMPDEST opcode, m_label is id of label
+ LABELREF //< m_data is empty, m_label is id of label
+ };
+
+ explicit AssemblyItem(eth::Instruction _instruction) : m_type(Type::CODE), m_data(byte(_instruction)) {}
+ explicit AssemblyItem(byte _data): m_type(Type::DATA), m_data(_data) {}
+
+ /// Factory functions
+ static AssemblyItem labelRef(uint32_t _label) { return AssemblyItem(Type::LABELREF, 0, _label); }
+ static AssemblyItem label(uint32_t _label) { return AssemblyItem(Type::LABEL, byte(eth::Instruction::JUMPDEST), _label); }
+
+ Type getType() const { return m_type; }
+ byte getData() const { return m_data; }
+ uint32_t getLabel() const { return m_label; }
+
+private:
+ AssemblyItem(Type _type, byte _data, uint32_t _label): m_type(_type), m_data(_data), m_label(_label) {}
+
+ Type m_type;
+ byte m_data; //< data to be written to the bytecode stream (or filled by a label if this is a LABELREF)
+ uint32_t m_label; //< the id of a label either referenced or defined by this item
+};
+
+using AssemblyItems = std::vector<AssemblyItem>;
+
+
+/**
+ * Context to be shared by all units that compile the same contract. Its current usage only
+ * concerns dispensing unique jump label IDs and storing their actual positions in the bytecode
+ * stream.
+ */
+class CompilerContext
+{
+public:
+ CompilerContext(): m_nextLabel(0) {}
+ uint32_t dispenseNewLabel() { return m_nextLabel++; }
+ void setLabelPosition(uint32_t _label, uint32_t _position);
+ uint32_t getLabelPosition(uint32_t _label) const;
+
+private:
+ uint32_t m_nextLabel;
+
+ std::map<uint32_t, uint32_t> m_labelPositions;
+};
+
+/**
+ * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
+ * of EVM instructions. It needs a compiler context that is the same for the whole compilation
+ * unit.
+ */
+class ExpressionCompiler: public ASTVisitor
+{
+public:
+ ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
+
+ /// Compile the given expression and (re-)populate the assembly item list.
+ void compile(Expression& _expression);
+ AssemblyItems const& getAssemblyItems() const { return m_assemblyItems; }
+ bytes getAssembledBytecode() const;
+
+ /// Compile the given expression and return the assembly items right away.
+ static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression);
+
+private:
+ virtual void endVisit(Assignment& _assignment) override;
+ virtual void endVisit(UnaryOperation& _unaryOperation) override;
+ virtual bool visit(BinaryOperation& _binaryOperation) override;
+ virtual void endVisit(FunctionCall& _functionCall) override;
+ virtual void endVisit(MemberAccess& _memberAccess) override;
+ virtual void endVisit(IndexAccess& _indexAccess) override;
+ virtual void endVisit(Identifier& _identifier) override;
+ virtual void endVisit(Literal& _literal) override;
+
+ /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
+ void cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType);
+
+ ///@{
+ ///@name Append code for various operator types
+ void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
+ void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
+ void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
+
+ void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
+ void appendBitOperatorCode(Token::Value _operator);
+ void appendShiftOperatorCode(Token::Value _operator);
+ /// @}
+
+ /// Appends a JUMPI instruction to a new label and returns the label
+ uint32_t appendConditionalJump();
+
+ /// Append elements to the current instruction list.
+ void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); }
+ void append(byte _value) { m_assemblyItems.push_back(AssemblyItem(_value)); }
+ void append(bytes const& _data);
+ void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); }
+ void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); }
+
+ AssemblyItems m_assemblyItems;
+ CompilerContext& m_context;
+};
+
+
+}
+}
diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp
index e9d90dc8..9626ca84 100644
--- a/NameAndTypeResolver.cpp
+++ b/NameAndTypeResolver.cpp
@@ -20,11 +20,12 @@
* Parser part that determines the declarations corresponding to names and the types of expressions.
*/
+#include <cassert>
#include <libsolidity/NameAndTypeResolver.h>
-
#include <libsolidity/AST.h>
#include <libsolidity/Exceptions.h>
-#include <boost/assert.hpp>
+
+using namespace std;
namespace dev
{
@@ -68,7 +69,7 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
}
-DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes,
+DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode*, Scope>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
{
@@ -120,22 +121,22 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclaration&)
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
{
- std::map<ASTNode*, Scope>::iterator iter;
+ map<ASTNode*, Scope>::iterator iter;
bool newlyAdded;
- std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
- BOOST_ASSERT(newlyAdded);
+ tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
+ assert(newlyAdded);
m_currentScope = &iter->second;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
- BOOST_ASSERT(m_currentScope);
- m_currentScope = m_currentScope->getOuterScope();
+ assert(m_currentScope);
+ m_currentScope = m_currentScope->getEnclosingScope();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
- BOOST_ASSERT(m_currentScope);
+ assert(m_currentScope);
if (!m_currentScope->registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared."));
@@ -162,7 +163,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return)
{
- BOOST_ASSERT(m_returnParameters);
+ assert(m_returnParameters);
_return.setFunctionReturnParameters(*m_returnParameters);
return true;
}
diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h
index 7abcbb0c..bb7fcb98 100644
--- a/NameAndTypeResolver.h
+++ b/NameAndTypeResolver.h
@@ -33,8 +33,11 @@ namespace dev
namespace solidity
{
-//! Resolves name references, resolves all types and checks that all operations are valid for the
-//! inferred types. An exception is throw on the first error.
+/**
+ * Resolves name references, types and checks types of all expressions.
+ * Specifically, it checks that all operations are valid for the inferred types.
+ * An exception is throw on the first error.
+ */
class NameAndTypeResolver: private boost::noncopyable
{
public:
@@ -46,15 +49,17 @@ public:
private:
void reset();
- //! Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
- //! StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
+ /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
+ /// StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
std::map<ASTNode*, Scope> m_scopes;
Scope* m_currentScope;
};
-//! Traverses the given AST upon construction and fills _scopes with all declarations inside the
-//! AST.
+/**
+ * Traverses the given AST upon construction and fills _scopes with all declarations inside the
+ * AST.
+ */
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
@@ -78,8 +83,10 @@ private:
Scope* m_currentScope;
};
-//! Resolves references to declarations (of variables and types) and also establishes the link
-//! between a return statement and the return parameter list.
+/**
+ * Resolves references to declarations (of variables and types) and also establishes the link
+ * between a return statement and the return parameter list.
+ */
class ReferencesResolver: private ASTVisitor
{
public:
diff --git a/Parser.h b/Parser.h
index 14338dc2..eabc2274 100644
--- a/Parser.h
+++ b/Parser.h
@@ -44,8 +44,8 @@ private:
/// End position of the current token
int getEndPosition() const;
- /// Parsing functions for the AST nodes
- /// @{
+ ///@{
+ ///@name Parsing functions for the AST nodes
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition();
@@ -64,16 +64,17 @@ private:
ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
- /// @}
+ ///@}
+
+ ///@{
+ ///@name Helper functions
- /// Helper functions
- /// @{
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator();
ASTPointer<ASTString> expectIdentifierToken();
ASTPointer<ASTString> getLiteralAndAdvance();
- /// @}
+ ///@}
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
diff --git a/Scanner.cpp b/Scanner.cpp
index 35da248a..3148de52 100644
--- a/Scanner.cpp
+++ b/Scanner.cpp
@@ -50,11 +50,13 @@
* Solidity scanner.
*/
+#include <cassert>
#include <algorithm>
#include <tuple>
-
#include <libsolidity/Scanner.h>
+using namespace std;
+
namespace dev
{
namespace solidity
@@ -118,7 +120,7 @@ void Scanner::reset(CharStream const& _source)
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength)
{
- BOOST_ASSERT(_expectedLength <= 4); // prevent overflow
+ assert(_expectedLength <= 4); // prevent overflow
char x = 0;
for (int i = 0; i < _expectedLength; i++)
{
@@ -178,7 +180,7 @@ Token::Value Scanner::skipSingleLineComment()
Token::Value Scanner::skipMultiLineComment()
{
- BOOST_ASSERT(m_char == '*');
+ assert(m_char == '*');
advance();
while (!isSourcePastEndOfInput())
{
@@ -471,7 +473,7 @@ void Scanner::scanDecimalDigits()
Token::Value Scanner::scanNumber(bool _periodSeen)
{
- BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
+ assert(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
LiteralScope literal(this);
if (_periodSeen)
@@ -513,7 +515,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
// scan exponent, if any
if (m_char == 'e' || m_char == 'E')
{
- BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
+ assert(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent
addLiteralCharAndAdvance();
@@ -607,9 +609,9 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
KEYWORD("while", Token::WHILE) \
-static Token::Value KeywordOrIdentifierToken(std::string const& input)
+static Token::Value KeywordOrIdentifierToken(string const& input)
{
- BOOST_ASSERT(!input.empty());
+ assert(!input.empty());
int const kMinLength = 2;
int const kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength)
@@ -637,7 +639,7 @@ case ch:
Token::Value Scanner::scanIdentifierOrKeyword()
{
- BOOST_ASSERT(IsIdentifierStart(m_char));
+ assert(IsIdentifierStart(m_char));
LiteralScope literal(this);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
@@ -659,42 +661,41 @@ char CharStream::advanceAndGet()
char CharStream::rollback(size_t _amount)
{
- BOOST_ASSERT(m_pos >= _amount);
+ assert(m_pos >= _amount);
m_pos -= _amount;
return get();
}
-std::string CharStream::getLineAtPosition(int _position) const
+string CharStream::getLineAtPosition(int _position) const
{
// if _position points to \n, it returns the line before the \n
- using size_type = std::string::size_type;
- size_type searchStart = std::min<size_type>(m_source.size(), _position);
+ using size_type = string::size_type;
+ size_type searchStart = min<size_type>(m_source.size(), _position);
if (searchStart > 0)
searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
- if (lineStart == std::string::npos)
+ if (lineStart == string::npos)
lineStart = 0;
else
lineStart++;
- return m_source.substr(lineStart,
- std::min(m_source.find('\n', lineStart),
- m_source.size()) - lineStart);
+ return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
+ m_source.size()) - lineStart);
}
-std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
+tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{
- using size_type = std::string::size_type;
- size_type searchPosition = std::min<size_type>(m_source.size(), _position);
- int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n');
+ using size_type = string::size_type;
+ size_type searchPosition = min<size_type>(m_source.size(), _position);
+ int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0)
lineStart = 0;
else
{
lineStart = m_source.rfind('\n', searchPosition - 1);
- lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;
+ lineStart = lineStart == string::npos ? 0 : lineStart + 1;
}
- return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
+ return tuple<int, int>(lineNumber, searchPosition - lineStart);
}
diff --git a/Scanner.h b/Scanner.h
index adae10dc..fbaba9ed 100644
--- a/Scanner.h
+++ b/Scanner.h
@@ -52,8 +52,6 @@
#pragma once
-#include <boost/assert.hpp>
-
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonData.h>
@@ -81,12 +79,13 @@ public:
char advanceAndGet();
char rollback(size_t _amount);
+ ///@{
+ ///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
- /// @{
std::string getLineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
- /// @}
+ ///@}
private:
std::string m_source;
@@ -119,29 +118,31 @@ public:
/// Returns the next token and advances input.
Token::Value next();
- /// Information about the current token
- /// @{
+ ///@{
+ ///@name Information about the current token
/// Returns the current token
Token::Value getCurrentToken() { return m_current_token.token; }
Location getCurrentLocation() const { return m_current_token.location; }
const std::string& getCurrentLiteral() const { return m_current_token.literal; }
- /// @}
+ ///@}
+
+ ///@{
+ ///@name Information about the next token
- /// Information about the next token
- /// @{
/// Returns the next token without advancing input.
Token::Value peekNextToken() const { return m_next_token.token; }
Location peekLocation() const { return m_next_token.location; }
const std::string& peekLiteral() const { return m_next_token.literal; }
- /// @}
+ ///@}
- /// Functions that help pretty-printing parse errors.
+ ///@{
+ ///@name Error printing helper functions
+ /// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
- /// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
- /// @}
+ ///@}
private:
// Used for the current and look-ahead token.
@@ -152,13 +153,13 @@ private:
std::string literal;
};
- /// Literal buffer support
- /// @{
+ ///@{
+ ///@name Literal buffer support
inline void startNewLiteral() { m_next_token.literal.clear(); }
inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); }
inline void dropLiteral() { m_next_token.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
- /// @}
+ ///@}
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source.rollback(_amount); }
@@ -169,9 +170,10 @@ private:
bool scanHexNumber(char& o_scannedNumber, int _expectedLength);
- // Scans a single JavaScript token.
+ /// Scans a single JavaScript token.
void scanToken();
+ /// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace();
Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment();
diff --git a/Scope.cpp b/Scope.cpp
index 4fcd2f45..540c4120 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -41,8 +41,8 @@ Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const
auto result = m_declarations.find(_name);
if (result != m_declarations.end())
return result->second;
- if (_recursive && m_outerScope)
- return m_outerScope->resolveName(_name, true);
+ if (_recursive && m_enclosingScope)
+ return m_enclosingScope->resolveName(_name, true);
return nullptr;
}
diff --git a/Scope.h b/Scope.h
index 2e36e528..637c2d5c 100644
--- a/Scope.h
+++ b/Scope.h
@@ -32,18 +32,22 @@ namespace dev
namespace solidity
{
+/**
+ * Container that stores mappings betwee names and declarations. It also contains a link to the
+ * enclosing scope.
+ */
class Scope
{
public:
- explicit Scope(Scope* _outerScope = nullptr): m_outerScope(_outerScope) {}
+ explicit Scope(Scope* _enclosingScope = nullptr): m_enclosingScope(_enclosingScope) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared.
bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
- Scope* getOuterScope() const { return m_outerScope; }
+ Scope* getEnclosingScope() const { return m_enclosingScope; }
private:
- Scope* m_outerScope;
+ Scope* m_enclosingScope;
std::map<ASTString, Declaration*> m_declarations;
};
diff --git a/SourceReferenceFormatter.cpp b/SourceReferenceFormatter.cpp
index b270342c..d3f2152a 100644
--- a/SourceReferenceFormatter.cpp
+++ b/SourceReferenceFormatter.cpp
@@ -24,57 +24,59 @@
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
+using namespace std;
+
namespace dev
{
namespace solidity
{
-void SourceReferenceFormatter::printSourceLocation(std::ostream& _stream,
+void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
Location const& _location,
Scanner const& _scanner)
{
int startLine;
int startColumn;
- std::tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
+ tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
_stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n";
int endLine;
int endColumn;
- std::tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
+ tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
if (startLine == endLine)
{
- _stream << _scanner.getLineAtPosition(_location.start) << std::endl
- << std::string(startColumn, ' ') << "^";
+ _stream << _scanner.getLineAtPosition(_location.start) << endl
+ << string(startColumn, ' ') << "^";
if (endColumn > startColumn + 2)
- _stream << std::string(endColumn - startColumn - 2, '-');
+ _stream << string(endColumn - startColumn - 2, '-');
if (endColumn > startColumn + 1)
_stream << "^";
- _stream << std::endl;
+ _stream << endl;
}
else
- _stream << _scanner.getLineAtPosition(_location.start) << std::endl
- << std::string(startColumn, ' ') << "^\n"
+ _stream << _scanner.getLineAtPosition(_location.start) << endl
+ << string(startColumn, ' ') << "^\n"
<< "Spanning multiple lines.\n";
}
-void SourceReferenceFormatter::printSourcePosition(std::ostream& _stream,
+void SourceReferenceFormatter::printSourcePosition(ostream& _stream,
int _position,
const Scanner& _scanner)
{
int line;
int column;
- std::tie(line, column) = _scanner.translatePositionToLineColumn(_position);
- _stream << "at line " << (line + 1) << ", column " << (column + 1) << std::endl
- << _scanner.getLineAtPosition(_position) << std::endl
- << std::string(column, ' ') << "^" << std::endl;
+ tie(line, column) = _scanner.translatePositionToLineColumn(_position);
+ _stream << "at line " << (line + 1) << ", column " << (column + 1) << endl
+ << _scanner.getLineAtPosition(_position) << endl
+ << string(column, ' ') << "^" << endl;
}
-void SourceReferenceFormatter::printExceptionInformation(std::ostream& _stream,
+void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
Exception const& _exception,
- std::string const& _name,
+ string const& _name,
Scanner const& _scanner)
{
_stream << _name;
- if (std::string const* description = boost::get_error_info<errinfo_comment>(_exception))
+ if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description;
if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception))
diff --git a/Token.h b/Token.h
index 2db6e05d..c54f387c 100644
--- a/Token.h
+++ b/Token.h
@@ -42,8 +42,7 @@
#pragma once
-#include <boost/assert.hpp>
-
+#include <cassert>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
@@ -225,7 +224,7 @@ public:
// (e.g. "LT" for the token LT).
static char const* getName(Value tok)
{
- BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned
+ assert(tok < NUM_TOKENS); // tok is unsigned
return m_name[tok];
}
@@ -236,6 +235,7 @@ public:
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; }
+ static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
static bool isOrderedRelationalCompareOp(Value op)
{
@@ -251,7 +251,7 @@ public:
static Value negateCompareOp(Value op)
{
- BOOST_ASSERT(isArithmeticCompareOp(op));
+ assert(isArithmeticCompareOp(op));
switch (op)
{
case EQ:
@@ -267,14 +267,14 @@ public:
case GTE:
return LT;
default:
- BOOST_ASSERT(false); // should not get here
+ assert(false); // should not get here
return op;
}
}
static Value reverseCompareOp(Value op)
{
- BOOST_ASSERT(isArithmeticCompareOp(op));
+ assert(isArithmeticCompareOp(op));
switch (op)
{
case EQ:
@@ -290,14 +290,14 @@ public:
case GTE:
return LTE;
default:
- BOOST_ASSERT(false); // should not get here
+ assert(false); // should not get here
return op;
}
}
static Value AssignmentToBinaryOp(Value op)
{
- BOOST_ASSERT(isAssignmentOp(op) && op != ASSIGN);
+ assert(isAssignmentOp(op) && op != ASSIGN);
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
}
@@ -311,7 +311,7 @@ public:
// have a (unique) string (e.g. an IDENTIFIER).
static char const* toString(Value tok)
{
- BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
+ assert(tok < NUM_TOKENS); // tok is unsigned.
return m_string[tok];
}
@@ -319,7 +319,7 @@ public:
// operators; returns 0 otherwise.
static int precedence(Value tok)
{
- BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
+ assert(tok < NUM_TOKENS); // tok is unsigned.
return m_precedence[tok];
}
diff --git a/Types.cpp b/Types.cpp
index 301e9577..05b12df0 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -20,7 +20,9 @@
* Solidity data types
*/
+#include <cassert>
#include <libdevcore/CommonIO.h>
+#include <libdevcore/CommonData.h>
#include <libsolidity/Types.h>
#include <libsolidity/AST.h>
@@ -50,7 +52,7 @@ std::shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::BOOL)
return std::make_shared<BoolType>();
else
- BOOST_ASSERT(false); // @todo add other tyes
+ assert(false); // @todo add other tyes
return std::shared_ptr<Type>();
}
@@ -61,7 +63,7 @@ std::shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _
std::shared_ptr<Type> Type::fromMapping(Mapping const&)
{
- BOOST_ASSERT(false); //@todo not yet implemented
+ assert(false); //@todo not yet implemented
return std::shared_ptr<Type>();
}
@@ -92,12 +94,12 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
{
if (isAddress())
_bits = 160;
- BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0);
+ assert(_bits > 0 && _bits <= 256 && _bits % 8 == 0);
}
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
- if (_convertTo.getCategory() != Category::INTEGER)
+ if (_convertTo.getCategory() != getCategory())
return false;
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.m_bits < m_bits)
@@ -114,7 +116,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return _convertTo.getCategory() == Category::INTEGER;
+ return _convertTo.getCategory() == getCategory();
}
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@@ -129,7 +131,24 @@ bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const
{
- return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT);
+ if (_operator == Token::DELETE)
+ return true;
+ if (isAddress())
+ return false;
+ if (_operator == Token::BIT_NOT)
+ return true;
+ if (isHash())
+ return false;
+ return _operator == Token::ADD || _operator == Token::SUB ||
+ _operator == Token::INC || _operator == Token::DEC;
+}
+
+bool IntegerType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ IntegerType const& other = dynamic_cast<IntegerType const&>(_other);
+ return other.m_bits == m_bits && other.m_modifier == m_modifier;
}
std::string IntegerType::toString() const
@@ -140,11 +159,21 @@ std::string IntegerType::toString() const
return prefix + dev::toString(m_bits);
}
+bytes IntegerType::literalToBigEndian(const Literal& _literal) const
+{
+ bigint value(_literal.getValue());
+ if (!isSigned() && value < 0)
+ return bytes(); // @todo this should already be caught by "smallestTypeforLiteral"
+ //@todo check that the number of bits is correct
+ //@todo does "toCompactBigEndian" work for signed numbers?
+ return toCompactBigEndian(value);
+}
+
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address
// this is an example of explicit conversions being not transitive (though implicit should be)
- if (_convertTo.getCategory() == Category::INTEGER)
+ if (_convertTo.getCategory() == getCategory())
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (!convertTo.isAddress())
@@ -153,22 +182,55 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return isImplicitlyConvertibleTo(_convertTo);
}
-bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+bytes BoolType::literalToBigEndian(const Literal& _literal) const
{
- if (_convertTo.getCategory() != Category::CONTRACT)
+ if (_literal.getToken() == Token::TRUE_LITERAL)
+ return bytes(1, 1);
+ else if (_literal.getToken() == Token::FALSE_LITERAL)
+ return bytes(1, 0);
+ else
+ return NullBytes;
+}
+
+bool ContractType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
return false;
- ContractType const& convertTo = dynamic_cast<ContractType const&>(_convertTo);
- return &m_contract == &convertTo.m_contract;
+ ContractType const& other = dynamic_cast<ContractType const&>(_other);
+ return other.m_contract == m_contract;
}
-bool StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+bool StructType::operator==(const Type& _other) const
{
- if (_convertTo.getCategory() != Category::STRUCT)
+ if (_other.getCategory() != getCategory())
return false;
- StructType const& convertTo = dynamic_cast<StructType const&>(_convertTo);
- return &m_struct == &convertTo.m_struct;
+ StructType const& other = dynamic_cast<StructType const&>(_other);
+ return other.m_struct == m_struct;
}
+bool FunctionType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
+ return other.m_function == m_function;
+}
+
+bool MappingType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ MappingType const& other = dynamic_cast<MappingType const&>(_other);
+ return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
+}
+
+bool TypeType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ TypeType const& other = dynamic_cast<TypeType const&>(_other);
+ return *getActualType() == *other.getActualType();
+}
}
}
diff --git a/Types.h b/Types.h
index 82b54943..c9f6da57 100644
--- a/Types.h
+++ b/Types.h
@@ -25,7 +25,7 @@
#include <memory>
#include <string>
#include <boost/noncopyable.hpp>
-#include <boost/assert.hpp>
+#include <libdevcore/Common.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Token.h>
@@ -36,6 +36,9 @@ namespace solidity
// @todo realMxN, string<N>, mapping
+/**
+ * Abstract base class that forms the root of the type hierarchy.
+ */
class Type: private boost::noncopyable
{
public:
@@ -44,15 +47,19 @@ public:
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
};
- //! factory functions that convert an AST TypeName to a Type.
+ ///@{
+ ///@name Factory functions
+ /// 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> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
+ /// @}
+ /// Auto-detect the proper type for a literal
static std::shared_ptr<Type> forLiteral(Literal const& _literal);
virtual Category getCategory() const = 0;
- virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; }
+ virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo);
@@ -60,9 +67,16 @@ public:
virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
+ virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
+ virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
+
virtual std::string toString() const = 0;
+ virtual bytes literalToBigEndian(Literal const&) const { return NullBytes; }
};
+/**
+ * Any kind of integer type including hash and address.
+ */
class IntegerType: public Type
{
public:
@@ -81,7 +95,10 @@ public:
virtual bool acceptsBinaryOperator(Token::Value _operator) const override;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override;
+ virtual bool operator==(Type const& _other) const override;
+
virtual std::string toString() const override;
+ virtual bytes literalToBigEndian(Literal const& _literal) const override;
int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
@@ -93,14 +110,13 @@ private:
Modifier m_modifier;
};
+/**
+ * The boolean type.
+ */
class BoolType: public Type
{
public:
virtual Category getCategory() const { return Category::BOOL; }
- virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override
- {
- return _convertTo.getCategory() == Category::BOOL;
- }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override
{
@@ -110,15 +126,21 @@ public:
{
return _operator == Token::NOT || _operator == Token::DELETE;
}
+
virtual std::string toString() const override { return "bool"; }
+ virtual bytes literalToBigEndian(Literal const& _literal) const override;
};
+/**
+ * The type of a contract instance, there is one distinct type for each contract definition.
+ */
class ContractType: public Type
{
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
- virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const;
+
+ virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "contract{...}"; }
@@ -126,17 +148,20 @@ private:
ContractDefinition const& m_contract;
};
+/**
+ * The type of a struct instance, there is one distinct type per struct definition.
+ */
class StructType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {}
- virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{
return _operator == Token::DELETE;
}
+ virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "struct{...}"; }
@@ -144,6 +169,9 @@ private:
StructDefinition const& m_struct;
};
+/**
+ * The type of a function, there is one distinct type per function definition.
+ */
class FunctionType: public Type
{
public:
@@ -154,10 +182,15 @@ public:
virtual std::string toString() const override { return "function(...)returns(...)"; }
+ virtual bool operator==(Type const& _other) const override;
+
private:
FunctionDefinition const& m_function;
};
+/**
+ * The type of a mapping, there is one distinct type per key/value type pair.
+ */
class MappingType: public Type
{
public:
@@ -165,19 +198,30 @@ public:
MappingType() {}
virtual std::string toString() const override { return "mapping(...=>...)"; }
+ virtual bool operator==(Type const& _other) const override;
+
private:
- //@todo
+ std::shared_ptr<Type const> m_keyType;
+ std::shared_ptr<Type const> m_valueType;
};
-//@todo should be changed into "empty anonymous struct"
+/**
+ * The void type, can only be implicitly used as the type that is returned by functions without
+ * return parameters.
+ */
class VoidType: public Type
{
public:
virtual Category getCategory() const override { return Category::VOID; }
VoidType() {}
+
virtual std::string toString() const override { return "void"; }
};
+/**
+ * The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
+ * of a TypeType.
+ */
class TypeType: public Type
{
public:
@@ -186,6 +230,8 @@ public:
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
+ virtual bool operator==(Type const& _other) const override;
+
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private: