mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1100 from chriseth/sol_arrays
Parsing support for arrays.
This commit is contained in:
commit
44db8c26db
41
AST.cpp
41
AST.cpp
@ -609,13 +609,48 @@ void MemberAccess::checkTypeRequirements()
|
|||||||
void IndexAccess::checkTypeRequirements()
|
void IndexAccess::checkTypeRequirements()
|
||||||
{
|
{
|
||||||
m_base->checkTypeRequirements();
|
m_base->checkTypeRequirements();
|
||||||
if (m_base->getType()->getCategory() != Type::Category::Mapping)
|
switch (m_base->getType()->getCategory())
|
||||||
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
|
{
|
||||||
m_base->getType()->toString() + ")"));
|
case Type::Category::Array:
|
||||||
|
{
|
||||||
|
ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
|
||||||
|
if (!m_index)
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
|
||||||
|
m_index->expectType(IntegerType(256));
|
||||||
|
m_type = type.getBaseType();
|
||||||
|
m_isLValue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::Mapping:
|
||||||
|
{
|
||||||
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
|
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
|
||||||
|
if (!m_index)
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
|
||||||
m_index->expectType(*type.getKeyType());
|
m_index->expectType(*type.getKeyType());
|
||||||
m_type = type.getValueType();
|
m_type = type.getValueType();
|
||||||
m_isLValue = true;
|
m_isLValue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::TypeType:
|
||||||
|
{
|
||||||
|
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
|
||||||
|
if (!m_index)
|
||||||
|
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_index->checkTypeRequirements();
|
||||||
|
auto length = dynamic_cast<IntegerConstantType const*>(m_index->getType().get());
|
||||||
|
if (!length)
|
||||||
|
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
|
||||||
|
m_type = make_shared<TypeType>(make_shared<ArrayType>(
|
||||||
|
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BOOST_THROW_EXCEPTION(m_base->createTypeError(
|
||||||
|
"Indexed expression has to be a type, mapping or array (is " + m_base->getType()->toString() + ")"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Identifier::checkTypeRequirements()
|
void Identifier::checkTypeRequirements()
|
||||||
|
33
AST.h
33
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
|
||||||
@ -1081,7 +1102,7 @@ public:
|
|||||||
virtual void checkTypeRequirements() override;
|
virtual void checkTypeRequirements() override;
|
||||||
|
|
||||||
Expression const& getBaseExpression() const { return *m_base; }
|
Expression const& getBaseExpression() const { return *m_base; }
|
||||||
Expression const& getIndexExpression() const { return *m_index; }
|
Expression const* getIndexExpression() const { return m_index.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<Expression> m_base;
|
ASTPointer<Expression> m_base;
|
||||||
|
@ -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;
|
||||||
|
@ -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--;
|
||||||
|
@ -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;
|
||||||
|
@ -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&) { }
|
||||||
|
24
AST_accept.h
24
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))
|
||||||
@ -604,6 +626,7 @@ void IndexAccess::accept(ASTVisitor& _visitor)
|
|||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
{
|
{
|
||||||
m_base->accept(_visitor);
|
m_base->accept(_visitor);
|
||||||
|
if (m_index)
|
||||||
m_index->accept(_visitor);
|
m_index->accept(_visitor);
|
||||||
}
|
}
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
@ -614,6 +637,7 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
|
|||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
{
|
{
|
||||||
m_base->accept(_visitor);
|
m_base->accept(_visitor);
|
||||||
|
if (m_index)
|
||||||
m_index->accept(_visitor);
|
m_index->accept(_visitor);
|
||||||
}
|
}
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
@ -561,7 +562,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
solAssert(baseType.getCategory() == Type::Category::Mapping, "");
|
solAssert(baseType.getCategory() == Type::Category::Mapping, "");
|
||||||
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
|
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
||||||
|
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
|
||||||
solAssert(baseType.getSizeOnStack() == 1,
|
solAssert(baseType.getSizeOnStack() == 1,
|
||||||
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
|
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
@ -1135,11 +1137,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 +1212,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
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
||||||
|
198
Parser.cpp
198
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);
|
||||||
|
ASTPointer<TypeName> type;
|
||||||
|
if (_lookAheadArrayType)
|
||||||
|
type = _lookAheadArrayType;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = parseTypeName(_options.allowVar);
|
||||||
if (type != nullptr)
|
if (type != nullptr)
|
||||||
nodeFactory.setEndPositionFromNode(type);
|
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,89 @@ 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.
|
||||||
|
switch (peekStatementType())
|
||||||
|
{
|
||||||
|
case LookAheadInfo::VariableDeclarationStatement:
|
||||||
return parseVariableDeclarationStatement();
|
return parseVariableDeclarationStatement();
|
||||||
else
|
case LookAheadInfo::ExpressionStatement:
|
||||||
return parseExpressionStatement();
|
return parseExpressionStatement();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement()
|
// 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();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
expectToken(Token::LBrack);
|
||||||
|
ASTPointer<Expression> index;
|
||||||
|
if (m_scanner->getCurrentToken() != Token::RBrack)
|
||||||
|
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)
|
||||||
|
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
|
||||||
|
else
|
||||||
|
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
|
||||||
|
}
|
||||||
|
|
||||||
|
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& _lookAheadIndexAccessStructure)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
|
||||||
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& _lookAheadIndexAccessStructure)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
|
||||||
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& _lookAheadIndexAccessStructure)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
|
||||||
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 +716,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseUnaryExpression()
|
ASTPointer<Expression> Parser::parseUnaryExpression(
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
|
||||||
|
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
Token::Value token = m_scanner->getCurrentToken();
|
||||||
if (Token::isUnaryOp(token) || Token::isCountOp(token))
|
if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
|
||||||
{
|
{
|
||||||
// prefix expression
|
// prefix expression
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -668,7 +733,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// potential postfix expression
|
// potential postfix expression
|
||||||
ASTPointer<Expression> subExpression = parseLeftHandSideExpression();
|
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
|
||||||
token = m_scanner->getCurrentToken();
|
token = m_scanner->getCurrentToken();
|
||||||
if (!Token::isCountOp(token))
|
if (!Token::isCountOp(token))
|
||||||
return subExpression;
|
return subExpression;
|
||||||
@ -678,11 +743,16 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
ASTPointer<Expression> Parser::parseLeftHandSideExpression(
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
|
||||||
|
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
|
||||||
|
|
||||||
ASTPointer<Expression> expression;
|
ASTPointer<Expression> expression;
|
||||||
if (m_scanner->getCurrentToken() == Token::New)
|
if (_lookAheadIndexAccessStructure)
|
||||||
|
expression = _lookAheadIndexAccessStructure;
|
||||||
|
else if (m_scanner->getCurrentToken() == Token::New)
|
||||||
{
|
{
|
||||||
expectToken(Token::New);
|
expectToken(Token::New);
|
||||||
ASTPointer<Identifier> contractName(parseIdentifier());
|
ASTPointer<Identifier> contractName(parseIdentifier());
|
||||||
@ -699,7 +769,9 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
|||||||
case Token::LBrack:
|
case Token::LBrack:
|
||||||
{
|
{
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
ASTPointer<Expression> index = parseExpression();
|
ASTPointer<Expression> index;
|
||||||
|
if (m_scanner->getCurrentToken() != Token::RBrack)
|
||||||
|
index = parseExpression();
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::RBrack);
|
expectToken(Token::RBrack);
|
||||||
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
||||||
@ -774,10 +846,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 +893,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Parser::LookAheadInfo Parser::peekStatementType() const
|
||||||
bool Parser::peekVariableDeclarationStatement()
|
|
||||||
{
|
{
|
||||||
// distinguish between variable declaration (and potentially assignment) and expression statement
|
// Distinguish between variable declaration (and potentially assignment) and expression statement
|
||||||
// (which include assignments to other expressions and pre-declared variables)
|
// (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, or
|
// We have a variable declaration if we get a keyword that specifies a type name.
|
||||||
// in the case of a user-defined type, we have two identifiers following each other.
|
// If it is an identifier or an elementary type name followed by an identifier, we also have
|
||||||
return (m_scanner->getCurrentToken() == Token::Mapping ||
|
// a variable declaration.
|
||||||
m_scanner->getCurrentToken() == Token::Var ||
|
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;").
|
||||||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
|
// In all other cases, we have an expression statement.
|
||||||
m_scanner->getCurrentToken() == Token::Identifier) &&
|
Token::Value token(m_scanner->getCurrentToken());
|
||||||
m_scanner->peekNextToken() == Token::Identifier));
|
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
|
||||||
|
if (token == Token::Mapping || token == Token::Var ||
|
||||||
|
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
|
||||||
|
return LookAheadInfo::VariableDeclarationStatement;
|
||||||
|
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
|
||||||
|
return LookAheadInfo::IndexAccessStructure;
|
||||||
|
return LookAheadInfo::ExpressionStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
|
||||||
|
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
|
||||||
|
{
|
||||||
|
ASTNodeFactory nodeFactory(*this, _primary);
|
||||||
|
ASTPointer<TypeName> type;
|
||||||
|
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
|
||||||
|
type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->getName()));
|
||||||
|
else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
|
||||||
|
type = nodeFactory.createNode<ElementaryTypeName>(typeName->getTypeToken());
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid type name for array look-ahead.");
|
||||||
|
for (auto const& lengthExpression: _indices)
|
||||||
|
{
|
||||||
|
nodeFactory.setLocation(lengthExpression.second);
|
||||||
|
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
|
||||||
|
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)
|
||||||
|
44
Parser.h
44
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& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
|
||||||
ASTPointer<Expression> parseLeftHandSideExpression();
|
ASTPointer<Expression> parseExpression(
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
|
||||||
|
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
|
||||||
|
ASTPointer<Expression> parseUnaryExpression(
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
|
||||||
|
ASTPointer<Expression> parseLeftHandSideExpression(
|
||||||
|
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = 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,24 @@ private:
|
|||||||
///@{
|
///@{
|
||||||
///@name Helper functions
|
///@name Helper functions
|
||||||
|
|
||||||
/// Peeks ahead in the scanner to determine if a variable declaration statement is going to follow
|
/// Used as return value of @see peekStatementType.
|
||||||
bool peekVariableDeclarationStatement();
|
enum class LookAheadInfo
|
||||||
|
{
|
||||||
|
IndexAccessStructure, VariableDeclarationStatement, ExpressionStatement
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Performs limited look-ahead to distinguish between variable declaration and expression statement.
|
||||||
|
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
|
||||||
|
/// decide with constant look-ahead.
|
||||||
|
LookAheadInfo peekStatementType() const;
|
||||||
|
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
|
||||||
|
ASTPointer<TypeName> typeNameIndexAccessStructure(
|
||||||
|
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> expressionFromIndexAccessStructure(
|
||||||
|
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
Types.cpp
64
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 + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
|
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 ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
|
||||||
|
|
||||||
bool ContractType::operator==(Type const& _other) const
|
bool ContractType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
@ -1040,7 +1076,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)},
|
||||||
|
44
Types.h
44
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
|
||||||
@ -281,32 +279,50 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a byte array, prototype for a general array.
|
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
|
||||||
|
* and dynamically-sized array (<type>[]).
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,8 +18,9 @@ ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
|
|||||||
// semantic restriction: mappings and structs (recursively) containing mappings
|
// semantic restriction: mappings and structs (recursively) containing mappings
|
||||||
// are not allowed in argument lists
|
// are not allowed in argument lists
|
||||||
VariableDeclaration = TypeName Identifier
|
VariableDeclaration = TypeName Identifier
|
||||||
TypeName = ElementaryTypeName | Identifier | Mapping
|
TypeName = ElementaryTypeName | Identifier | Mapping | ArrayTypeName
|
||||||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
||||||
|
ArrayTypeName = TypeName '[' (Expression)? ']'
|
||||||
|
|
||||||
Block = '{' Statement* '}'
|
Block = '{' Statement* '}'
|
||||||
Statement = IfStatement | WhileStatement | Block |
|
Statement = IfStatement | WhileStatement | Block |
|
||||||
@ -42,5 +43,5 @@ Assignment = Expression (AssignmentOp Expression)
|
|||||||
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
||||||
NewExpression = 'new' Identifier
|
NewExpression = 'new' Identifier
|
||||||
MemberAccess = Expression '.' Identifier
|
MemberAccess = Expression '.' Identifier
|
||||||
IndexAccess = Expression '[' Expresison ']'
|
IndexAccess = Expression '[' (Expresison)? ']'
|
||||||
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
|
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
|
||||||
|
Loading…
Reference in New Issue
Block a user