Browse Source

Parsing of array types and basic implementation.

cl-refactor
Christian 10 years ago
parent
commit
30bd6f271f
  1. 31
      libsolidity/AST.h
  2. 1
      libsolidity/ASTForward.h
  3. 12
      libsolidity/ASTPrinter.cpp
  4. 2
      libsolidity/ASTPrinter.h
  5. 4
      libsolidity/ASTVisitor.h
  6. 22
      libsolidity/AST_accept.h
  7. 32
      libsolidity/CompilerUtils.cpp
  8. 4
      libsolidity/CompilerUtils.h
  9. 23
      libsolidity/ExpressionCompiler.cpp
  10. 4
      libsolidity/ExpressionCompiler.h
  11. 6
      libsolidity/NameAndTypeResolver.cpp
  12. 195
      libsolidity/Parser.cpp
  13. 40
      libsolidity/Parser.h
  14. 64
      libsolidity/Types.cpp
  15. 41
      libsolidity/Types.h
  16. 9
      test/SolidityNameAndTypeResolution.cpp
  17. 40
      test/SolidityParser.cpp

31
libsolidity/AST.h

@ -441,7 +441,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
TypeName const* getTypeName() const { return m_typeName.get(); } TypeName* getTypeName() { return m_typeName.get(); }
ASTPointer<Expression> const& getValue() const { return m_value; } ASTPointer<Expression> const& getValue() const { return m_value; }
/// 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
@ -588,7 +588,7 @@ public:
/// 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 const> toType() const = 0; virtual std::shared_ptr<Type const> toType() = 0;
}; };
/** /**
@ -605,7 +605,7 @@ public:
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); } virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; } Token::Value getTypeName() const { return m_type; }
@ -623,7 +623,7 @@ public:
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} TypeName(_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 accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); } virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
@ -646,7 +646,7 @@ public:
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 void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); } virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); }
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; }
@ -656,6 +656,27 @@ private:
ASTPointer<TypeName> m_valueType; ASTPointer<TypeName> m_valueType;
}; };
/**
* An array type, can be "typename[]" or "typename[<expression>]".
*/
class ArrayTypeName: public TypeName
{
public:
ArrayTypeName(Location const& _location, ASTPointer<TypeName> const& _baseType,
ASTPointer<Expression> const& _length):
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); }
TypeName const& getBaseType() const { return *m_baseType; }
Expression const* getLength() const { return m_length.get(); }
private:
ASTPointer<TypeName> m_baseType;
ASTPointer<Expression> m_length; ///< Length of the array, might be empty.
};
/// @} /// @}
/// Statements /// Statements

1
libsolidity/ASTForward.h

@ -53,6 +53,7 @@ class TypeName;
class ElementaryTypeName; class ElementaryTypeName;
class UserDefinedTypeName; class UserDefinedTypeName;
class Mapping; class Mapping;
class ArrayTypeName;
class Statement; class Statement;
class Block; class Block;
class PlaceholderStatement; class PlaceholderStatement;

12
libsolidity/ASTPrinter.cpp

@ -155,6 +155,13 @@ bool ASTPrinter::visit(Mapping const& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ArrayTypeName const& _node)
{
writeLine("ArrayTypeName");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Statement const& _node) bool ASTPrinter::visit(Statement const& _node)
{ {
writeLine("Statement"); writeLine("Statement");
@ -419,6 +426,11 @@ void ASTPrinter::endVisit(Mapping const&)
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ArrayTypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Statement const&) void ASTPrinter::endVisit(Statement const&)
{ {
m_indentation--; m_indentation--;

2
libsolidity/ASTPrinter.h

@ -58,6 +58,7 @@ public:
bool visit(ElementaryTypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override; bool visit(Mapping const& _node) override;
bool visit(ArrayTypeName const& _node) override;
bool visit(Statement const& _node) override; bool visit(Statement const& _node) override;
bool visit(Block const& _node) override; bool visit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override; bool visit(PlaceholderStatement const& _node) override;
@ -99,6 +100,7 @@ public:
void endVisit(ElementaryTypeName const&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override; void endVisit(Mapping const&) override;
void endVisit(ArrayTypeName const&) override;
void endVisit(Statement const&) override; void endVisit(Statement const&) override;
void endVisit(Block const&) override; void endVisit(Block const&) override;
void endVisit(PlaceholderStatement const&) override; void endVisit(PlaceholderStatement const&) override;

4
libsolidity/ASTVisitor.h

@ -59,6 +59,7 @@ public:
virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; } virtual bool visit(Mapping&) { return true; }
virtual bool visit(ArrayTypeName&) { return true; }
virtual bool visit(Statement&) { return true; } virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; } virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; } virtual bool visit(PlaceholderStatement&) { return true; }
@ -102,6 +103,7 @@ public:
virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { } virtual void endVisit(Mapping&) { }
virtual void endVisit(ArrayTypeName&) { }
virtual void endVisit(Statement&) { } virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { } virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { } virtual void endVisit(PlaceholderStatement&) { }
@ -149,6 +151,7 @@ public:
virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; } virtual bool visit(Mapping const&) { return true; }
virtual bool visit(ArrayTypeName const&) { return true; }
virtual bool visit(Statement const&) { return true; } virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; } virtual bool visit(Block const&) { return true; }
virtual bool visit(PlaceholderStatement const&) { return true; } virtual bool visit(PlaceholderStatement const&) { return true; }
@ -192,6 +195,7 @@ public:
virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { } virtual void endVisit(Mapping const&) { }
virtual void endVisit(ArrayTypeName const&) { }
virtual void endVisit(Statement const&) { } virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { } virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { } virtual void endVisit(PlaceholderStatement const&) { }

22
libsolidity/AST_accept.h

@ -327,6 +327,28 @@ void Mapping::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void ArrayTypeName::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ArrayTypeName::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))

32
libsolidity/CompilerUtils.cpp

@ -36,14 +36,14 @@ const unsigned int CompilerUtils::dataStartOffset = 4;
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type, unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
bool _fromCalldata, bool _padToWordBoundaries) bool _fromCalldata, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type."); solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically load dynamic type.");
m_context << u256(_offset); m_context << u256(_offset);
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
} }
void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented."); solAssert(_type.getCategory() != Type::Category::Array, "Arrays not yet implemented.");
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
// update memory counter // update memory counter
@ -55,7 +55,7 @@ void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata,
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries) unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type."); solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0) if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE; m_context << u256(_offset) << eth::Instruction::MSTORE;
@ -64,11 +64,12 @@ unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{ {
if (_type.getCategory() == Type::Category::ByteArray) if (_type.getCategory() == Type::Category::Array)
{ {
auto const& type = dynamic_cast<ByteArrayType const&>(_type); auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.getLocation() == ByteArrayType::Location::CallData) if (type.getLocation() == ArrayType::Location::CallData)
{ {
// stack: target source_offset source_len // stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -79,7 +80,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
} }
else else
{ {
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented."); solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes // stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero // jump to end if length is zero
@ -163,16 +164,18 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
} }
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, void CompilerUtils::copyByteArrayToStorage(
ByteArrayType const& _sourceType) const ArrayType const& _targetType, ArrayType const& _sourceType) const
{ {
// stack layout: [source_ref] target_ref (top) // stack layout: [source_ref] target_ref (top)
// need to leave target_ref on the stack at the end // need to leave target_ref on the stack at the end
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, ""); solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
solAssert(_targetType.isByteArray(), "Non byte arrays not yet implemented here.");
solAssert(_sourceType.isByteArray(), "Non byte arrays not yet implemented here.");
switch (_sourceType.getLocation()) switch (_sourceType.getLocation())
{ {
case ByteArrayType::Location::CallData: case ArrayType::Location::CallData:
{ {
// This also assumes that after "length" we only have zeros, i.e. it cannot be used to // This also assumes that after "length" we only have zeros, i.e. it cannot be used to
// slice a byte array from calldata. // slice a byte array from calldata.
@ -224,7 +227,7 @@ void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
<< eth::Instruction::POP << eth::Instruction::POP; << eth::Instruction::POP << eth::Instruction::POP;
break; break;
} }
case ByteArrayType::Location::Storage: case ArrayType::Location::Storage:
{ {
// this copies source to target and also clears target if it was larger // this copies source to target and also clears target if it was larger
@ -313,9 +316,10 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
return numBytes; return numBytes;
} }
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const void CompilerUtils::clearByteArray(ArrayType const& _type) const
{ {
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, ""); solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.isByteArray(), "Non byte arrays not yet implemented here.");
// fetch length // fetch length
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;

4
libsolidity/CompilerUtils.h

@ -82,11 +82,11 @@ public:
/// Copies a byte array to a byte array in storage. /// Copies a byte array to a byte array in storage.
/// Stack pre: [source_reference] target_reference /// Stack pre: [source_reference] target_reference
/// Stack post: target_reference /// Stack post: target_reference
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
/// Clears the length and data elements of the byte array referenced on the stack. /// Clears the length and data elements of the byte array referenced on the stack.
/// Stack pre: reference /// Stack pre: reference
/// Stack post: /// Stack post:
void clearByteArray(ByteArrayType const& _type) const; void clearByteArray(ArrayType const& _type) const;
/// Bytes we need to the start of call data. /// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier. /// - The size in bytes of the function (hash) identifier.

23
libsolidity/ExpressionCompiler.cpp

@ -530,20 +530,21 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << enumType->getMemberValue(_memberAccess.getMemberName()); m_context << enumType->getMemberValue(_memberAccess.getMemberName());
break; break;
} }
case Type::Category::ByteArray: case Type::Category::Array:
{ {
solAssert(member == "length", "Illegal bytearray member."); solAssert(member == "length", "Illegal array member.");
auto const& type = dynamic_cast<ByteArrayType const&>(*_memberAccess.getExpression().getType()); auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType());
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
switch (type.getLocation()) switch (type.getLocation())
{ {
case ByteArrayType::Location::CallData: case ArrayType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break; break;
case ByteArrayType::Location::Storage: case ArrayType::Location::Storage:
m_context << eth::Instruction::SLOAD; m_context << eth::Instruction::SLOAD;
break; break;
default: default:
solAssert(false, "Unsupported byte array location."); solAssert(false, "Unsupported array location.");
break; break;
} }
break; break;
@ -1135,11 +1136,11 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co
else else
{ {
solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment."); solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment.");
if (m_dataType->getCategory() == Type::Category::ByteArray) if (m_dataType->getCategory() == Type::Category::Array)
{ {
CompilerUtils(*m_context).copyByteArrayToStorage( CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*m_dataType), dynamic_cast<ArrayType const&>(*m_dataType),
dynamic_cast<ByteArrayType const&>(_sourceType)); dynamic_cast<ArrayType const&>(_sourceType));
if (_move) if (_move)
*m_context << eth::Instruction::POP; *m_context << eth::Instruction::POP;
} }
@ -1210,8 +1211,8 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
if (m_dataType->getCategory() == Type::Category::ByteArray) if (m_dataType->getCategory() == Type::Category::Array)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType)); CompilerUtils(*m_context).clearByteArray(dynamic_cast<ArrayType const&>(*m_dataType));
else if (m_dataType->getCategory() == Type::Category::Struct) else if (m_dataType->getCategory() == Type::Category::Struct)
{ {
// stack layout: ref // stack layout: ref

4
libsolidity/ExpressionCompiler.h

@ -39,7 +39,7 @@ namespace solidity {
class CompilerContext; class CompilerContext;
class Type; class Type;
class IntegerType; class IntegerType;
class ByteArrayType; class ArrayType;
class StaticStringType; class StaticStringType;
/** /**
@ -165,7 +165,7 @@ private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(bool _remove = false) const; void retrieveValueFromStorage(bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack. /// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
CompilerContext* m_context; CompilerContext* m_context;
LValueType m_type = LValueType::None; LValueType m_type = LValueType::None;

6
libsolidity/NameAndTypeResolver.cpp

@ -334,10 +334,10 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName()) if (_variable.getTypeName())
{ {
TypePointer type = _variable.getTypeName()->toType(); TypePointer type = _variable.getTypeName()->toType();
// All byte array parameter types should point to call data // All array parameter types should point to call data
if (_variable.isExternalFunctionParameter()) if (_variable.isExternalFunctionParameter())
if (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get())) if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData); type = arrayType->copyForLocation(ArrayType::Location::CallData);
_variable.setType(type); _variable.setType(type);
if (!_variable.getType()) if (!_variable.getType())

195
libsolidity/Parser.cpp

@ -41,8 +41,11 @@ class Parser::ASTNodeFactory
public: public:
ASTNodeFactory(Parser const& _parser): ASTNodeFactory(Parser const& _parser):
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
m_parser(_parser), m_location(_childNode->getLocation()) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocation(Location const& _location) { m_location = _location; }
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty() { m_location.end = m_location.start; }
/// Set the end position to the one of the given node. /// Set the end position to the one of the given node.
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
@ -299,12 +302,20 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
return nodeFactory.createNode<EnumDefinition>(name, members); return nodeFactory.createNode<EnumDefinition>(name, members);
} }
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTPointer<TypeName> type = parseTypeName(_options.allowVar); ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
if (type != nullptr) ASTPointer<TypeName> type;
nodeFactory.setEndPositionFromNode(type); if (_lookAheadArrayType)
type = _lookAheadArrayType;
else
{
type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
}
bool isIndexed = false; bool isIndexed = false;
ASTPointer<ASTString> identifier; ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
@ -407,6 +418,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isElementaryTypeName(token)) if (Token::isElementaryTypeName(token))
@ -421,9 +433,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::Mapping) else if (token == Token::Mapping)
{
type = parseMapping(); type = parseMapping();
}
else if (token == Token::Identifier) else if (token == Token::Identifier)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -432,6 +442,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
} }
else else
BOOST_THROW_EXCEPTION(createParserError("Expected type name")); BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
// Parse "[...]" postfixes for arrays.
while (m_scanner->getCurrentToken() == Token::LBrack)
{
m_scanner->next();
ASTPointer<Expression> length;
if (m_scanner->getCurrentToken() != Token::RBrack)
length = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBrack);
type = nodeFactory.createNode<ArrayTypeName>(type, length);
}
return type; return type;
} }
@ -530,7 +552,7 @@ ASTPointer<Statement> Parser::parseStatement()
} }
// fall-through // fall-through
default: default:
statement = parseVarDeclOrExprStmt(); statement = parseSimpleStatement();
} }
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return statement; return statement;
@ -579,7 +601,7 @@ ASTPointer<ForStatement> Parser::parseForStatement()
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDeclOrExprStmt(); initExpression = parseSimpleStatement();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
@ -598,48 +620,88 @@ ASTPointer<ForStatement> Parser::parseForStatement()
body); body);
} }
ASTPointer<Statement> Parser::parseVarDeclOrExprStmt() ASTPointer<Statement> Parser::parseSimpleStatement()
{ {
if (peekVariableDeclarationStatement()) // These two cases are very hard to distinguish:
// x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
// In the first case, x is a type name, in the second it is the name of a variable.
int isVariableDeclaration = peekVariableDeclarationStatement();
if (isVariableDeclaration == 1)
return parseVariableDeclarationStatement(); return parseVariableDeclarationStatement();
else else if (isVariableDeclaration == -1)
return parseExpressionStatement(); return parseExpressionStatement();
// At this point, we have '(Identifier|ElementaryTypeName) "["'.
// We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over
// to ExpressionStatement or create a VariableDeclarationStatement out of it.
ASTPointer<PrimaryExpression> primary;
if (m_scanner->getCurrentToken() == Token::Identifier)
primary = parseIdentifier();
else
{
primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->getCurrentToken());
m_scanner->next();
}
vector<pair<ASTPointer<Expression>, Location>> indices;
solAssert(m_scanner->getCurrentToken() == Token::LBrack, "");
Location indexLocation = primary->getLocation();
bool encounteredEmptyBrackets = false;
do
{
expectToken(Token::LBrack);
ASTPointer<Expression> index;
if (m_scanner->getCurrentToken() == Token::RBrack)
encounteredEmptyBrackets = true;
else
index = parseExpression();
indexLocation.end = getEndPosition();
indices.push_back(make_pair(index, indexLocation));
expectToken(Token::RBrack);
}
while (m_scanner->getCurrentToken() == Token::LBrack);
if (m_scanner->getCurrentToken() == Token::Identifier || encounteredEmptyBrackets)
return parseVariableDeclarationStatement(typeNameFromArrayIndexStructure(primary, indices));
else
return parseExpressionStatement(expressionFromArrayIndexStructure(primary, indices));
} }
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement() ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this);
VarDeclParserOptions options; VarDeclParserOptions options;
options.allowVar = true; options.allowVar = true;
options.allowInitialValue = true; options.allowInitialValue = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options); ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable); return nodeFactory.createNode<VariableDeclarationStatement>(variable);
} }
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement() ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadArrayExpression)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseExpression(_lookAheadArrayExpression);
ASTPointer<Expression> expression = parseExpression(); return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression);
nodeFactory.setEndPositionFromNode(expression);
return nodeFactory.createNode<ExpressionStatement>(expression);
} }
ASTPointer<Expression> Parser::parseExpression() ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadArrayExpression);
ASTPointer<Expression> expression = parseBinaryExpression();
if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression; return expression;
Token::Value assignmentOperator = expectAssignmentOperator(); Token::Value assignmentOperator = expectAssignmentOperator();
ASTPointer<Expression> rightHandSide = parseExpression(); ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(rightHandSide); nodeFactory.setEndPositionFromNode(rightHandSide);
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
} }
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
ASTPointer<Expression> const& _lookAheadArrayExpression)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadArrayExpression);
ASTPointer<Expression> expression = parseUnaryExpression(); ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
@ -653,11 +715,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
return expression; return expression;
} }
ASTPointer<Expression> Parser::parseUnaryExpression() ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadArrayExpression ?
ASTNodeFactory(*this, _lookAheadArrayExpression) : ASTNodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token)) if (!_lookAheadArrayExpression && (Token::isUnaryOp(token) || Token::isCountOp(token)))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
@ -668,7 +732,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
else else
{ {
// potential postfix expression // potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadArrayExpression);
token = m_scanner->getCurrentToken(); token = m_scanner->getCurrentToken();
if (!Token::isCountOp(token)) if (!Token::isCountOp(token))
return subExpression; return subExpression;
@ -678,11 +742,16 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
} }
} }
ASTPointer<Expression> Parser::parseLeftHandSideExpression() ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadArrayExpression ?
ASTNodeFactory(*this, _lookAheadArrayExpression) : ASTNodeFactory(*this);
ASTPointer<Expression> expression; ASTPointer<Expression> expression;
if (m_scanner->getCurrentToken() == Token::New) if (_lookAheadArrayExpression)
expression = _lookAheadArrayExpression;
else if (m_scanner->getCurrentToken() == Token::New)
{ {
expectToken(Token::New); expectToken(Token::New);
ASTPointer<Identifier> contractName(parseIdentifier()); ASTPointer<Identifier> contractName(parseIdentifier());
@ -774,10 +843,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next(); m_scanner->next();
} }
else else
{
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
return ASTPointer<Expression>(); // this is not reached
}
break; break;
} }
return expression; return expression;
@ -824,18 +890,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
return ret; return ret;
} }
int Parser::peekVariableDeclarationStatement() const
{
// Distinguish between variable declaration (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables).
// We have a variable declaration if we get a keyword that specifies a type name.
// If it is an identifier or an elementary type name followed by an identifier, we also have
// a variable declaration.
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;").
// In all other cases, we have an expression statement.
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return 1;
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
return 0;
return -1;
}
bool Parser::peekVariableDeclarationStatement() ASTPointer<TypeName> Parser::typeNameFromArrayIndexStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{ {
// distinguish between variable declaration (and potentially assignment) and expression statement ASTNodeFactory nodeFactory(*this, _primary);
// (which include assignments to other expressions and pre-declared variables) ASTPointer<TypeName> type;
// We have a variable declaration if we get a keyword that specifies a type name, or if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
// in the case of a user-defined type, we have two identifiers following each other. type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->getName()));
return (m_scanner->getCurrentToken() == Token::Mapping || else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
m_scanner->getCurrentToken() == Token::Var || type = nodeFactory.createNode<ElementaryTypeName>(typeName->getTypeToken());
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || else
m_scanner->getCurrentToken() == Token::Identifier) && solAssert(false, "Invalid type name for array look-ahead.");
m_scanner->peekNextToken() == Token::Identifier)); for (auto const& lengthExpression: _indices)
{
nodeFactory.setLocation(lengthExpression.second);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
}
return type;
}
ASTPointer<Expression> Parser::expressionFromArrayIndexStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<Expression> expression(_primary);
for (auto const& index: _indices)
{
nodeFactory.setLocation(index.second);
expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
}
return expression;
} }
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)

40
libsolidity/Parser.h

@ -64,7 +64,9 @@ private:
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue(); ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer<VariableDeclaration> parseVariableDeclaration(
VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<ModifierInvocation> parseModifierInvocation();
@ -77,13 +79,20 @@ private:
ASTPointer<IfStatement> parseIfStatement(); ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement(); ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<ForStatement> parseForStatement(); ASTPointer<ForStatement> parseForStatement();
ASTPointer<Statement> parseVarDeclOrExprStmt(); /// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(); ASTPointer<Statement> parseSimpleStatement();
ASTPointer<ExpressionStatement> parseExpressionStatement(); ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
ASTPointer<Expression> parseExpression(); ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4); ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<Expression> parseUnaryExpression(); ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>());
ASTPointer<Expression> parseLeftHandSideExpression(); ASTPointer<Expression> parseExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>());
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>());
ASTPointer<Expression> parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>());
ASTPointer<Expression> parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>());
ASTPointer<Expression> parsePrimaryExpression(); ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments(); std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments(); std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
@ -92,9 +101,18 @@ private:
///@{ ///@{
///@name Helper functions ///@name Helper functions
/// Peeks ahead in the scanner to determine if a variable declaration statement is going to follow /// Performs limited look-ahead to distinguish between variable declaration and expression statement.
bool peekVariableDeclarationStatement(); /// @returns 1 if it is a variable declaration, -1 if it is an expression statement and 0 if
/// it might be an array-typed variable declaration or an index access to an existing variable.
int peekVariableDeclarationStatement() const;
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<TypeName> typeNameFromArrayIndexStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices);
/// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<Expression> expressionFromArrayIndexStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices);
/// If current token value is not _value, throw exception otherwise advance token. /// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value); void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator(); Token::Value expectAssignmentOperator();

64
libsolidity/Types.cpp

@ -58,7 +58,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0)); return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage); return make_shared<ArrayType>(ArrayType::Location::Storage);
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."));
@ -83,17 +83,35 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
return TypePointer(); return TypePointer();
} }
TypePointer Type::fromMapping(Mapping const& _typeName) TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
{ {
TypePointer keyType = _typeName.getKeyType().toType(); TypePointer keyType = _keyType.toType();
if (!keyType) if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
TypePointer valueType = _typeName.getValueType().toType(); TypePointer valueType = _valueType.toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
{
TypePointer baseType = _baseTypeName.toType();
if (!baseType)
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
if (_length)
{
if (!_length->getType())
_length->checkTypeRequirements();
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
}
else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
}
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
@ -517,27 +535,27 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{ {
return _convertTo.getCategory() == getCategory(); return _convertTo.getCategory() == getCategory();
} }
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const
{ {
if (_operator == Token::Delete) if (_operator == Token::Delete)
return make_shared<VoidType>(); return make_shared<VoidType>();
return TypePointer(); return TypePointer();
} }
bool ByteArrayType::operator==(Type const& _other) const bool ArrayType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other); ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
return other.m_location == m_location; return other.m_location == m_location;
} }
unsigned ByteArrayType::getSizeOnStack() const unsigned ArrayType::getSizeOnStack() const
{ {
if (m_location == Location::CallData) if (m_location == Location::CallData)
// offset, length (stack top) // offset, length (stack top)
@ -547,12 +565,30 @@ unsigned ByteArrayType::getSizeOnStack() const
return 1; return 1;
} }
shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const string ArrayType::toString() const
{ {
return make_shared<ByteArrayType>(_location); if (isByteArray())
return "bytes";
string ret = getBaseType()->toString() + "[";
if (!isDynamicallySized())
ret += getLength().str();
return ret + "]";
}
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
{
auto copy = make_shared<ArrayType>(_location);
copy->m_isByteArray = m_isByteArray;
if (m_baseType->getCategory() == Type::Category::Array)
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
else
copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength;
copy->m_length = m_length;
return copy;
} }
const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared<IntegerType>(256)}}); const MemberList ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
bool ContractType::operator==(Type const& _other) const bool ContractType::operator==(Type const& _other) const
{ {
@ -1033,7 +1069,7 @@ MagicType::MagicType(MagicType::Kind _kind):
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}}); {"data", make_shared<ArrayType>(ArrayType::Location::CallData)}});
break; break;
case Kind::Transaction: case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

41
libsolidity/Types.h

@ -36,8 +36,6 @@ namespace dev
namespace solidity namespace solidity
{ {
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward class Type; // forward
class FunctionType; // forward class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
@ -78,7 +76,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public: public:
enum class Category enum class Category
{ {
Integer, IntegerConstant, Bool, Real, ByteArray, Integer, IntegerConstant, Bool, Real, Array,
String, Contract, Struct, Function, Enum, String, Contract, Struct, Function, Enum,
Mapping, Void, TypeType, Modifier, Magic Mapping, Void, TypeType, Modifier, Magic
}; };
@ -89,8 +87,8 @@ public:
static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name); static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName); static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType);
static TypePointer fromFunction(FunctionDefinition const& _function); static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length);
/// @} /// @}
/// 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
@ -283,30 +281,47 @@ public:
/** /**
* The type of a byte array, prototype for a general array. * The type of a byte array, prototype for a general array.
*/ */
class ByteArrayType: public Type class ArrayType: public Type
{ {
public: public:
enum class Location { Storage, CallData, Memory }; enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; } virtual Category getCategory() const override { return Category::Array; }
explicit ByteArrayType(Location _location): m_location(_location) {}
/// Constructor for a byte array ("bytes")
explicit ArrayType(Location _location):
m_location(_location), m_isByteArray(true), m_baseType(std::make_shared<IntegerType>(8)) {}
/// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType):
m_location(_location), m_baseType(_baseType) {}
/// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
m_location(_location), m_baseType(_baseType), m_hasDynamicLength(false), m_length(_length) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(const Type& _other) const override; virtual bool operator==(const Type& _other) const override;
virtual bool isDynamicallySized() const { return true; } virtual bool isDynamicallySized() const { return m_hasDynamicLength; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override { return "bytes"; } virtual std::string toString() const override;
virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; } virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
Location getLocation() const { return m_location; } Location getLocation() const { return m_location; }
bool isByteArray() const { return m_isByteArray; }
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
/// @returns a copy of this type with location changed to @a _location /// @returns a copy of this type with location changed to @a _location
/// @todo this might move as far up as Type later /// @todo this might move as far up as Type later
std::shared_ptr<ByteArrayType> copyForLocation(Location _location) const; std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
private: private:
Location m_location; Location m_location;
static const MemberList s_byteArrayMemberList; bool m_isByteArray = false; ///< Byte arrays ("bytes") have different semantics from ordinary arrays.
TypePointer m_baseType;
bool m_hasDynamicLength = true;
u256 m_length;
static const MemberList s_arrayTypeMemberList;
}; };
/** /**

9
test/SolidityNameAndTypeResolution.cpp

@ -1176,6 +1176,15 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(sourceCode)); BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(sourceCode));
} }
BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
{
char const* text = R"(
contract c {
function f(uint a) { uint8[a] x; }
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

40
test/SolidityParser.cpp

@ -480,6 +480,7 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function fun() {\n" " function fun() {\n"
" uint64(2);\n" " uint64(2);\n"
" uint64[7](3);\n"
" }\n" " }\n"
"}\n"; "}\n";
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
@ -753,6 +754,45 @@ BOOST_AUTO_TEST_CASE(external_variable)
BOOST_CHECK_THROW(parseText(text), ParserError); BOOST_CHECK_THROW(parseText(text), ParserError);
} }
BOOST_AUTO_TEST_CASE(arrays_in_storage)
{
char const* text = R"(
contract c {
uint[10] a;
uint[] a2;
struct x { uint[2**20] b; y[0] c; }
struct y { uint d; mapping(uint=>x)[] e; }
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(arrays_in_events)
{
char const* text = R"(
contract c {
event e(uint[10] a, string7[8] indexed b, c[3] x);
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(arrays_in_expressions)
{
char const* text = R"(
contract c {
function f() { c[10] a = 7; uint8[10 * 2] x; }
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multi_arrays)
{
char const* text = R"(
contract c {
mapping(uint => mapping(uint => int8)[8][][9])[] x;
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save