mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Makes array length access read-only.
This commit is contained in:
parent
94272d44aa
commit
ecaed1030f
@ -14,6 +14,7 @@ Breaking changes:
|
|||||||
* Syntax: Abstract contracts need to be marked explicitly as abstract by using the ``abstract`` keyword.
|
* Syntax: Abstract contracts need to be marked explicitly as abstract by using the ``abstract`` keyword.
|
||||||
* Inline Assembly: Only strict inline assembly is allowed.
|
* Inline Assembly: Only strict inline assembly is allowed.
|
||||||
* Inline Assembly: Variable declarations cannot shadow declarations outside the assembly block.
|
* Inline Assembly: Variable declarations cannot shadow declarations outside the assembly block.
|
||||||
|
* Syntax: ``length`` member of arrays is now always read-only, even for storage arrays.
|
||||||
* Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base.
|
* Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base.
|
||||||
* Natspec JSON Interface: Properly support multiple ``@return`` statements in ``@dev`` documentation and enforce named return parameters to be mentioned documentation.
|
* Natspec JSON Interface: Properly support multiple ``@return`` statements in ``@dev`` documentation and enforce named return parameters to be mentioned documentation.
|
||||||
* Source mappings: Add "modifier depth" as a fifth field in the source mappings.
|
* Source mappings: Add "modifier depth" as a fifth field in the source mappings.
|
||||||
|
@ -2398,14 +2398,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
if (auto const* structType = dynamic_cast<StructType const*>(exprType))
|
if (auto const* structType = dynamic_cast<StructType const*>(exprType))
|
||||||
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
|
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
|
||||||
else if (exprType->category() == Type::Category::Array)
|
else if (exprType->category() == Type::Category::Array)
|
||||||
{
|
annotation.isLValue = false;
|
||||||
auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
|
|
||||||
annotation.isLValue = (
|
|
||||||
memberName == "length" &&
|
|
||||||
arrayType.location() == DataLocation::Storage &&
|
|
||||||
arrayType.isDynamicallySized()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (exprType->category() == Type::Category::FixedBytes)
|
else if (exprType->category() == Type::Category::FixedBytes)
|
||||||
annotation.isLValue = false;
|
annotation.isLValue = false;
|
||||||
else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType))
|
else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType))
|
||||||
@ -2862,20 +2855,9 @@ void TypeChecker::requireLValue(Expression const& _expression)
|
|||||||
if (structType->dataStoredIn(DataLocation::CallData))
|
if (structType->dataStoredIn(DataLocation::CallData))
|
||||||
return "Calldata structs are read-only.";
|
return "Calldata structs are read-only.";
|
||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
else if (dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||||
if (
|
if (memberAccess->memberName() == "length")
|
||||||
memberAccess->memberName() == "length" ||
|
return "Member \"length\" is read-only and cannot be used to resize arrays.";
|
||||||
memberAccess->memberName() == "push"
|
|
||||||
)
|
|
||||||
switch (arrayType->location())
|
|
||||||
{
|
|
||||||
case DataLocation::Memory:
|
|
||||||
return "Memory arrays cannot be resized.";
|
|
||||||
case DataLocation::CallData:
|
|
||||||
return "Calldata arrays cannot be resized.";
|
|
||||||
case DataLocation::Storage:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
if (auto identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||||
|
@ -381,7 +381,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
||||||
if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage))
|
if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage))
|
||||||
mutability = writes ? StateMutability::NonPayable : StateMutability::View;
|
mutability = StateMutability::View;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -1565,7 +1565,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
setLValue<StorageArrayLength>(_memberAccess, type);
|
ArrayUtils(m_context).retrieveLength(type);
|
||||||
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
m_context << Instruction::MLOAD;
|
m_context << Instruction::MLOAD;
|
||||||
|
@ -478,36 +478,6 @@ void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeRefer
|
|||||||
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType):
|
|
||||||
LValue(_compilerContext, _arrayType.memberType("length")),
|
|
||||||
m_arrayType(_arrayType)
|
|
||||||
{
|
|
||||||
solAssert(m_arrayType.isDynamicallySized(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const
|
|
||||||
{
|
|
||||||
ArrayUtils(m_context).retrieveLength(m_arrayType);
|
|
||||||
if (_remove)
|
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _move) const
|
|
||||||
{
|
|
||||||
if (_move)
|
|
||||||
m_context << Instruction::SWAP1;
|
|
||||||
else
|
|
||||||
m_context << Instruction::DUP2;
|
|
||||||
ArrayUtils(m_context).resizeDynamicArray(m_arrayType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const
|
|
||||||
{
|
|
||||||
solAssert(_removeReference, "");
|
|
||||||
ArrayUtils(m_context).clearDynamicArray(m_arrayType);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TupleObject::TupleObject(
|
TupleObject::TupleObject(
|
||||||
CompilerContext& _compilerContext,
|
CompilerContext& _compilerContext,
|
||||||
std::vector<std::unique_ptr<LValue>>&& _lvalues
|
std::vector<std::unique_ptr<LValue>>&& _lvalues
|
||||||
|
@ -171,31 +171,6 @@ public:
|
|||||||
) const override;
|
) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the "length" member of a dynamically-sized array. This is an LValue with special
|
|
||||||
* semantics since assignments to it might reduce its length and thus arrays members have to be
|
|
||||||
* deleted.
|
|
||||||
*/
|
|
||||||
class StorageArrayLength: public LValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Constructs the LValue, assumes that the reference to the array head is already on the stack.
|
|
||||||
StorageArrayLength(CompilerContext& _compilerContext, ArrayType const& _arrayType);
|
|
||||||
void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override;
|
|
||||||
virtual void storeValue(
|
|
||||||
Type const& _sourceType,
|
|
||||||
langutil::SourceLocation const& _location = {},
|
|
||||||
bool _move = false
|
|
||||||
) const override;
|
|
||||||
virtual void setToZero(
|
|
||||||
langutil::SourceLocation const& _location = {},
|
|
||||||
bool _removeReference = true
|
|
||||||
) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ArrayType const& m_arrayType;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tuple object that can itself hold several LValues.
|
* Tuple object that can itself hold several LValues.
|
||||||
*/
|
*/
|
||||||
|
@ -800,14 +800,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
//m_context << Instruction::SWAP1 << Instruction::POP;
|
//m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
setLValue(_memberAccess, make_unique<IRStorageArrayLength>(
|
{
|
||||||
m_context.utils(),
|
string slot = m_context.variable(_memberAccess.expression());
|
||||||
m_context.variable(_memberAccess.expression()),
|
defineExpression(_memberAccess) <<
|
||||||
*_memberAccess.annotation().type,
|
m_utils.arrayLengthFunction(type) + "(" + slot + ")\n";
|
||||||
type
|
|
||||||
));
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
defineExpression(_memberAccess) <<
|
defineExpression(_memberAccess) <<
|
||||||
"mload(" <<
|
"mload(" <<
|
||||||
|
@ -148,39 +148,6 @@ string IRStorageItem::setToZero() const
|
|||||||
")\n";
|
")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
IRStorageArrayLength::IRStorageArrayLength(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
string _slot,
|
|
||||||
Type const& _type,
|
|
||||||
ArrayType const& _arrayType
|
|
||||||
):
|
|
||||||
IRLValue(std::move(_utils), &_type), m_arrayType(_arrayType), m_slot(move(_slot))
|
|
||||||
{
|
|
||||||
solAssert(*m_type == *TypeProvider::uint256(), "Must be uint256!");
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageArrayLength::retrieveValue() const
|
|
||||||
{
|
|
||||||
return m_utils.arrayLengthFunction(m_arrayType) + "(" + m_slot + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageArrayLength::storeValue(std::string const& _value, Type const& _type) const
|
|
||||||
{
|
|
||||||
solAssert(_type == *m_type, "Different type, but might not be an error.");
|
|
||||||
|
|
||||||
return m_utils.resizeDynamicArrayFunction(m_arrayType) +
|
|
||||||
"(" +
|
|
||||||
m_slot +
|
|
||||||
", " +
|
|
||||||
_value +
|
|
||||||
")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageArrayLength::setToZero() const
|
|
||||||
{
|
|
||||||
return storeValue("0", *TypeProvider::uint256());
|
|
||||||
}
|
|
||||||
|
|
||||||
IRMemoryItem::IRMemoryItem(
|
IRMemoryItem::IRMemoryItem(
|
||||||
YulUtilFunctions _utils,
|
YulUtilFunctions _utils,
|
||||||
std::string _address,
|
std::string _address,
|
||||||
|
@ -110,30 +110,6 @@ private:
|
|||||||
boost::variant<std::string, unsigned> const m_offset;
|
boost::variant<std::string, unsigned> const m_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the "length" member of a dynamically-sized storage array. This is an LValue with special
|
|
||||||
* semantics since assignments to it might reduce its length and thus the array's members have to be
|
|
||||||
* deleted.
|
|
||||||
*/
|
|
||||||
class IRStorageArrayLength: public IRLValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IRStorageArrayLength(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
std::string _slot,
|
|
||||||
Type const& _type,
|
|
||||||
ArrayType const& _arrayType
|
|
||||||
);
|
|
||||||
|
|
||||||
std::string retrieveValue() const override;
|
|
||||||
std::string storeValue(std::string const& _value, Type const& _type) const override;
|
|
||||||
std::string setToZero() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ArrayType const& m_arrayType;
|
|
||||||
std::string const m_slot;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IRMemoryItem: public IRLValue
|
class IRMemoryItem: public IRLValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user