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.
|
||||
* Inline Assembly: Only strict inline assembly is allowed.
|
||||
* 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.
|
||||
* 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.
|
||||
|
@ -2398,14 +2398,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
||||
if (auto const* structType = dynamic_cast<StructType const*>(exprType))
|
||||
annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData);
|
||||
else if (exprType->category() == Type::Category::Array)
|
||||
{
|
||||
auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
|
||||
annotation.isLValue = (
|
||||
memberName == "length" &&
|
||||
arrayType.location() == DataLocation::Storage &&
|
||||
arrayType.isDynamicallySized()
|
||||
);
|
||||
}
|
||||
annotation.isLValue = false;
|
||||
else if (exprType->category() == Type::Category::FixedBytes)
|
||||
annotation.isLValue = false;
|
||||
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))
|
||||
return "Calldata structs are read-only.";
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||
if (
|
||||
memberAccess->memberName() == "length" ||
|
||||
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;
|
||||
}
|
||||
else if (dynamic_cast<ArrayType const*>(type(memberAccess->expression())))
|
||||
if (memberAccess->memberName() == "length")
|
||||
return "Member \"length\" is read-only and cannot be used to resize arrays.";
|
||||
}
|
||||
|
||||
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);
|
||||
if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage))
|
||||
mutability = writes ? StateMutability::NonPayable : StateMutability::View;
|
||||
mutability = StateMutability::View;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1565,7 +1565,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
break;
|
||||
case DataLocation::Storage:
|
||||
setLValue<StorageArrayLength>(_memberAccess, type);
|
||||
ArrayUtils(m_context).retrieveLength(type);
|
||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
break;
|
||||
case DataLocation::Memory:
|
||||
m_context << Instruction::MLOAD;
|
||||
|
@ -478,36 +478,6 @@ void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeRefer
|
||||
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(
|
||||
CompilerContext& _compilerContext,
|
||||
std::vector<std::unique_ptr<LValue>>&& _lvalues
|
||||
|
@ -171,31 +171,6 @@ public:
|
||||
) 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.
|
||||
*/
|
||||
|
@ -800,14 +800,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
//m_context << Instruction::SWAP1 << Instruction::POP;
|
||||
break;
|
||||
case DataLocation::Storage:
|
||||
setLValue(_memberAccess, make_unique<IRStorageArrayLength>(
|
||||
m_context.utils(),
|
||||
m_context.variable(_memberAccess.expression()),
|
||||
*_memberAccess.annotation().type,
|
||||
type
|
||||
));
|
||||
|
||||
{
|
||||
string slot = m_context.variable(_memberAccess.expression());
|
||||
defineExpression(_memberAccess) <<
|
||||
m_utils.arrayLengthFunction(type) + "(" + slot + ")\n";
|
||||
break;
|
||||
}
|
||||
case DataLocation::Memory:
|
||||
defineExpression(_memberAccess) <<
|
||||
"mload(" <<
|
||||
|
@ -148,39 +148,6 @@ string IRStorageItem::setToZero() const
|
||||
")\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(
|
||||
YulUtilFunctions _utils,
|
||||
std::string _address,
|
||||
|
@ -110,30 +110,6 @@ private:
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user