Merge pull request #7350 from ethereum/array-length-rvalue

Make `length` member read-only
This commit is contained in:
chriseth 2019-11-21 19:22:42 +01:00 committed by GitHub
commit d5e9271592
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 690 additions and 518 deletions

View File

@ -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.

View File

@ -26,6 +26,10 @@ This section lists purely syntactic changes that do not affect the behavior of e
* Libraries have to implement all their functions, not only the internal ones. * Libraries have to implement all their functions, not only the internal ones.
* Member-access to ``length`` of arrays is now always read-only, even for storage arrays. It's no
longer possible to resize storage arrays assigning a new value to their length. Use ``push()``,
``push(value)`` or ``pop()`` instead, or assign a full array, which will of course overwrite existing content.
* New reserved keywords: ``virtual``. * New reserved keywords: ``virtual``.
* The names of variables declared in inline assembly may no longer end in ``_slot`` or ``_offset``. * The names of variables declared in inline assembly may no longer end in ``_slot`` or ``_offset``.
@ -75,6 +79,9 @@ This section gives detailed instructions on how to update prior code for every b
* Change ``uint length = array.push(value)`` to ``array.push(value);``. The new length can be * Change ``uint length = array.push(value)`` to ``array.push(value);``. The new length can be
accessed via ``array.length``. accessed via ``array.length``.
* Change ``array.length++`` to ``array.push()`` to increase, and use ``pop()`` to decrease
the length of a storage array.
* For every named return parameter in a function's ``@dev`` documentation define a ``@return`` * For every named return parameter in a function's ``@dev`` documentation define a ``@return``
entry which contains the parameter's name as the first word. E.g. if you have function ``f()`` defined entry which contains the parameter's name as the first word. E.g. if you have function ``f()`` defined
like ``function f() public returns (uint value)`` and a ``@dev`` annotating it, document its return like ``function f() public returns (uint value)`` and a ``@dev`` annotating it, document its return
@ -85,6 +92,7 @@ This section gives detailed instructions on how to update prior code for every b
* Add ``virtual`` to every non-interface function you intend to override. For single inheritance, add ``override`` to every overriding function. For multiple inheritance, add ``override(A, B, ..)``, where you list all contracts that define the overridden function in the brackets. When multiple bases define the same function, the inheriting contract must override all conflicting functions. * Add ``virtual`` to every non-interface function you intend to override. For single inheritance, add ``override`` to every overriding function. For multiple inheritance, add ``override(A, B, ..)``, where you list all contracts that define the overridden function in the brackets. When multiple bases define the same function, the inheriting contract must override all conflicting functions.
New Features New Features
============ ============

View File

@ -231,7 +231,7 @@ groupings of expressions.
pragma solidity >0.4.23 <0.7.0; pragma solidity >0.4.23 <0.7.0;
contract C { contract C {
uint[] data; uint index;
function f() public pure returns (uint, bool, uint) { function f() public pure returns (uint, bool, uint) {
return (7, true, 2); return (7, true, 2);
@ -244,7 +244,7 @@ groupings of expressions.
// Common trick to swap values -- does not work for non-value storage types. // Common trick to swap values -- does not work for non-value storage types.
(x, y) = (y, x); (x, y) = (y, x);
// Components can be left out (also for variable declarations). // Components can be left out (also for variable declarations).
(data.length, , ) = f(); // Sets the length to 7 (index, , ) = f(); // Sets the index to 7
} }
} }

View File

@ -278,7 +278,8 @@ a ``mapping``.
mapping (uint => uint)[] array; mapping (uint => uint)[] array;
function allocate(uint _newMaps) public { function allocate(uint _newMaps) public {
array.length += _newMaps; for (uint i = 0; i < _newMaps; i++)
array.push();
} }
function writeMap(uint _map, uint _key, uint _value) public { function writeMap(uint _map, uint _key, uint _value) public {

View File

@ -137,7 +137,8 @@ the ``sum`` function iterates over to sum all the values.
if (keyIndex > 0) if (keyIndex > 0)
return true; return true;
else { else {
keyIndex = self.keys.length++; self.keys.push();
keyIndex = self.keys.length;
self.data[key].keyIndex = keyIndex + 1; self.data[key].keyIndex = keyIndex + 1;
self.keys[keyIndex].key = key; self.keys[keyIndex].key = key;
self.size++; self.size++;

View File

@ -59,7 +59,7 @@ Data locations are not only relevant for persistency of data, but also for the s
x = memoryArray; // works, copies the whole array to storage x = memoryArray; // works, copies the whole array to storage
uint[] storage y = x; // works, assigns a pointer, data location of y is storage uint[] storage y = x; // works, assigns a pointer, data location of y is storage
y[7]; // fine, returns the 8th element y[7]; // fine, returns the 8th element
y.length = 2; // fine, modifies x through y y.pop(); // fine, modifies x through y
delete x; // fine, clears the array, also modifies y delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary / // The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated: // unnamed array in storage, but storage is "statically" allocated:
@ -215,12 +215,6 @@ Array Members
**length**: **length**:
Arrays have a ``length`` member that contains their number of elements. Arrays have a ``length`` member that contains their number of elements.
The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created.
For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array.
Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion.
Increasing the length adds new zero-initialised elements to the array.
Reducing the length performs an implicit :ref:`delete<delete>` on each of the
removed elements. If you try to resize a non-dynamic array that isn't in
storage, you receive a ``Value must be an lvalue`` error.
**push**: **push**:
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array.
If no argument is given, the element will be zero-initialised and a reference to the new element is returned. If no argument is given, the element will be zero-initialised and a reference to the new element is returned.
@ -228,14 +222,11 @@ Array Members
**pop**: **pop**:
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:`delete<delete>` on the removed element. Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:`delete<delete>` on the removed element.
.. warning::
If you use ``.length--`` on an empty array, it causes an underflow and
thus sets the length to ``2**256-1``.
.. note:: .. note::
Increasing the length of a storage array has constant gas costs because Increasing the length of a storage array by calling ``push()``
storage is assumed to be zero-initialised, while decreasing has constant gas costs because storage is zero-initialised,
the length has at least linear cost (but in most cases worse than linear), while decreasing the length by calling ``pop()`` has at least
linear cost (but in most cases worse than linear),
because it includes explicitly clearing the removed because it includes explicitly clearing the removed
elements similar to calling :ref:`delete<delete>` on them. elements similar to calling :ref:`delete<delete>` on them.
@ -295,7 +286,13 @@ Array Members
function changeFlagArraySize(uint newSize) public { function changeFlagArraySize(uint newSize) public {
// if the new size is smaller, removed array elements will be cleared // if the new size is smaller, removed array elements will be cleared
m_pairsOfFlags.length = newSize; if (newSize < m_pairsOfFlags.length) {
while (m_pairsOfFlags.length > newSize)
m_pairsOfFlags.pop();
} else if (newSize > m_pairsOfFlags.length) {
while (m_pairsOfFlags.length < newSize)
m_pairsOfFlags.push();
}
} }
function clear() public { function clear() public {
@ -303,7 +300,7 @@ Array Members
delete m_pairsOfFlags; delete m_pairsOfFlags;
delete m_aLotOfIntegers; delete m_aLotOfIntegers;
// identical effect here // identical effect here
m_pairsOfFlags.length = 0; m_pairsOfFlags = new bool[2][](0);
} }
bytes m_byteData; bytes m_byteData;
@ -312,7 +309,8 @@ Array Members
// byte arrays ("bytes") are different as they are stored without padding, // byte arrays ("bytes") are different as they are stored without padding,
// but can be treated identical to "uint8[]" // but can be treated identical to "uint8[]"
m_byteData = data; m_byteData = data;
m_byteData.length += 7; for (uint i = 0; i < 7; i++)
m_byteData.push();
m_byteData[3] = 0x08; m_byteData[3] = 0x08;
delete m_byteData[2]; delete m_byteData[2];
} }

View File

@ -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))

View File

@ -383,7 +383,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:

View File

@ -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;

View File

@ -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

View File

@ -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.
*/ */

View File

@ -593,6 +593,62 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
}); });
} }
string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
{
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
string functionName = "array_pop_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(array) {
let oldLen := <fetchLength>(array)
if iszero(oldLen) { invalid() }
let newLen := sub(oldLen, 1)
let slot, offset := <indexAccess>(array, newLen)
<setToZero>(slot, offset)
sstore(array, newLen)
})")
("functionName", functionName)
("fetchLength", arrayLengthFunction(_type))
("indexAccess", storageArrayIndexAccessFunction(_type))
("setToZero", storageSetToZeroFunction(*_type.baseType()))
.render();
});
}
string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
{
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
string functionName = "array_push_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(array, value) -> newLen {
let oldLen := <fetchLength>(array)
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
newLen := add(oldLen, 1)
sstore(array, newLen)
let slot, offset := <indexAccess>(array, oldLen)
<storeValue>(slot, offset, value)
})")
("functionName", functionName)
("fetchLength", arrayLengthFunction(_type))
("indexAccess", storageArrayIndexAccessFunction(_type))
("storeValue", updateStorageValueFunction(*_type.baseType()))
("maxArrayLength", (u256(1) << 64).str())
.render();
});
}
string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
{ {
string functionName = "clear_storage_range_" + _type.identifier(); string functionName = "clear_storage_range_" + _type.identifier();

View File

@ -121,6 +121,14 @@ public:
/// signature: (array, newLen) /// signature: (array, newLen)
std::string resizeDynamicArrayFunction(ArrayType const& _type); std::string resizeDynamicArrayFunction(ArrayType const& _type);
/// @returns the name of a function that reduces the size of a storage array by one element
/// signature: (array)
std::string storageArrayPopFunction(ArrayType const& _type);
/// @returns the name of a function that pushes an element to a storage array
/// signature: (array, value) -> newlength
std::string storageArrayPushFunction(ArrayType const& _type);
/// @returns the name of a function that will clear the storage area given /// @returns the name of a function that will clear the storage area given
/// by the start and end (exclusive) parameters (slots). /// by the start and end (exclusive) parameters (slots).
/// signature: (start, end) /// signature: (start, end)

View File

@ -640,6 +640,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
array << array <<
"))\n"; "))\n";
break;
}
case FunctionType::Kind::ArrayPop:
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(
*dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type
);
defineExpression(_functionCall) <<
m_utils.storageArrayPopFunction(arrayType) <<
"(" <<
m_context.variable(_functionCall.expression()) <<
")\n";
break;
}
case FunctionType::Kind::ArrayPush:
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(
*dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type
);
defineExpression(_functionCall) <<
m_utils.storageArrayPushFunction(arrayType) <<
"(" <<
m_context.variable(_functionCall.expression()) <<
", " << (
arguments.empty() ?
m_utils.zeroValueFunction(*arrayType.baseType()) + "()" :
expressionAsType(*arguments.front(), *arrayType.baseType())
) << ")\n";
break; break;
} }
default: default:
@ -788,33 +816,45 @@ void IRGeneratorForStatements::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);
solAssert(member == "length", ""); if (member == "length")
{
if (!type.isDynamicallySized()) if (!type.isDynamicallySized())
defineExpression(_memberAccess) << type.length() << "\n"; defineExpression(_memberAccess) << type.length() << "\n";
else
switch (type.location())
{
case DataLocation::CallData:
solUnimplementedAssert(false, "");
//m_context << Instruction::SWAP1 << Instruction::POP;
break;
case DataLocation::Storage:
{
string slot = m_context.variable(_memberAccess.expression());
defineExpression(_memberAccess) <<
m_utils.arrayLengthFunction(type) + "(" + slot + ")\n";
break;
}
case DataLocation::Memory:
defineExpression(_memberAccess) <<
"mload(" <<
m_context.variable(_memberAccess.expression()) <<
")\n";
break;
}
}
else if (member == "pop")
{
solAssert(type.location() == DataLocation::Storage, "");
defineExpression(_memberAccess) << m_context.variable(_memberAccess.expression()) << "\n";
}
else if (member == "push")
{
solAssert(type.location() == DataLocation::Storage, "");
defineExpression(_memberAccess) << m_context.variable(_memberAccess.expression()) << "\n";
}
else else
switch (type.location()) solAssert(false, "Invalid array member access.");
{
case DataLocation::CallData:
solUnimplementedAssert(false, "");
//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
));
break;
case DataLocation::Memory:
defineExpression(_memberAccess) <<
"mload(" <<
m_context.variable(_memberAccess.expression()) <<
")\n";
break;
}
break; break;
} }
case Type::Category::FixedBytes: case Type::Category::FixedBytes:

View File

@ -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,

View File

@ -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:

View File

@ -143,7 +143,7 @@ contract MultiSigWallet {
owners[i] = owners[owners.length - 1]; owners[i] = owners[owners.length - 1];
break; break;
} }
owners.length -= 1; owners.pop();
if (required > owners.length) if (required > owners.length)
changeRequirement(owners.length); changeRequirement(owners.length);
emit OwnerRemoval(owner); emit OwnerRemoval(owner);

View File

@ -77,7 +77,7 @@ contract moduleHandler is multiOwner, announcementTypes {
require( success ); require( success );
if ( ! found ) { if ( ! found ) {
id = modules.length; id = modules.length;
modules.length++; modules.push();
} }
modules[id] = input; modules[id] = input;
} }

View File

@ -46,7 +46,8 @@ contract schellingDB is safeMath, schellingVars {
Constructor Constructor
*/ */
constructor() public { constructor() public {
rounds.length = 2; rounds.push();
rounds.push();
rounds[0].blockHeight = block.number; rounds[0].blockHeight = block.number;
currentSchellingRound = 1; currentSchellingRound = 1;
} }

View File

@ -227,7 +227,7 @@ contract MilestoneTracker {
RLP.RLPItem memory itmProposal = itrProposals.next(); RLP.RLPItem memory itmProposal = itrProposals.next();
Milestone storage milestone = milestones[milestones.length ++]; Milestone storage milestone = milestones.push();
if (!itmProposal.isList()) revert(); if (!itmProposal.isList()) revert();

View File

@ -208,8 +208,8 @@ contract multiowned {
pending.yetNeeded = m_required; pending.yetNeeded = m_required;
// reset which owners have confirmed (none) - set our bitmap to 0. // reset which owners have confirmed (none) - set our bitmap to 0.
pending.ownersDone = 0; pending.ownersDone = 0;
pending.index = m_pendingIndex.length++; m_pendingIndex.push(_operation);
m_pendingIndex[pending.index] = _operation; pending.index = m_pendingIndex.length - 1;
} }
// determine the bit to set for this owner. // determine the bit to set for this owner.
uint ownerIndexBit = 2**ownerIndex; uint ownerIndexBit = 2**ownerIndex;

View File

@ -637,7 +637,7 @@ BOOST_AUTO_TEST_CASE(storage_ptr)
L.S s; L.S s;
uint[] r; uint[] r;
function f() public returns (uint, uint, uint, uint, uint, uint) { function f() public returns (uint, uint, uint, uint, uint, uint) {
r.length = 6; r = new uint[](6);
r[0] = 1; r[0] = 1;
r[1] = 2; r[1] = 2;
r[2] = 3; r[2] = 3;

View File

@ -429,7 +429,9 @@ BOOST_AUTO_TEST_CASE(structs)
s.a = 8; s.a = 8;
s.b = 9; s.b = 9;
s.c = 10; s.c = 10;
s.sub.length = 3; s.sub.push();
s.sub.push();
s.sub.push();
s.sub[0].x[0] = 11; s.sub[0].x[0] = 11;
s.sub[1].x[0] = 12; s.sub[1].x[0] = 12;
s.sub[2].x[1] = 13; s.sub[2].x[1] = 13;
@ -522,11 +524,10 @@ BOOST_AUTO_TEST_CASE(bool_arrays)
bool[4] y; bool[4] y;
event E(bool[], bool[4]); event E(bool[], bool[4]);
function f() public returns (bool[] memory, bool[4] memory) { function f() public returns (bool[] memory, bool[4] memory) {
x.length = 4; x.push(true);
x[0] = true; x.push(false);
x[1] = false; x.push(true);
x[2] = true; x.push(false);
x[3] = false;
y[0] = true; y[0] = true;
y[1] = false; y[1] = false;
y[2] = true; y[2] = true;
@ -556,11 +557,10 @@ BOOST_AUTO_TEST_CASE(bool_arrays_split)
bool[4] y; bool[4] y;
event E(bool[], bool[4]); event E(bool[], bool[4]);
function store() public { function store() public {
x.length = 4; x.push(true);
x[0] = true; x.push(false);
x[1] = false; x.push(true);
x[2] = true; x.push(false);
x[3] = false;
y[0] = true; y[0] = true;
y[1] = false; y[1] = false;
y[2] = true; y[2] = true;
@ -594,9 +594,8 @@ BOOST_AUTO_TEST_CASE(bytesNN_arrays)
bytesWIDTH[SIZE] y; bytesWIDTH[SIZE] y;
event E(bytes8[], bytesWIDTH[SIZE]); event E(bytes8[], bytesWIDTH[SIZE]);
function store() public { function store() public {
x.length = 2; x.push("abc");
x[0] = "abc"; x.push("def");
x[1] = "def";
for (uint i = 0; i < y.length; i ++) for (uint i = 0; i < y.length; i ++)
y[i] = bytesWIDTH(uintUINTWIDTH(i + 1)); y[i] = bytesWIDTH(uintUINTWIDTH(i + 1));
} }
@ -640,9 +639,8 @@ BOOST_AUTO_TEST_CASE(bytesNN_arrays_dyn)
bytesWIDTH[] y; bytesWIDTH[] y;
event E(bytesWIDTH[], bytes8[]); event E(bytesWIDTH[], bytes8[]);
function store() public { function store() public {
x.length = 2; x.push("abc");
x[0] = "abc"; x.push("def");
x[1] = "def";
for (uint i = 0; i < SIZE; i ++) for (uint i = 0; i < SIZE; i ++)
y.push(bytesWIDTH(uintUINTWIDTH(i + 1))); y.push(bytesWIDTH(uintUINTWIDTH(i + 1)));
} }

View File

@ -133,9 +133,8 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
contract test { contract test {
bytes x; bytes x;
function f(uint a) public returns (uint b) { function f(uint a) public returns (uint b) {
x.length = a;
for (; a < 200; ++a) { for (; a < 200; ++a) {
x[a] = 0x09; x.push(0x09);
b = a * a; b = a * a;
} }
return f(a - 1); return f(a - 1);

View File

@ -1030,26 +1030,6 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping)
) )
} }
BOOST_AUTO_TEST_CASE(deleteLength)
{
char const* sourceCode = R"(
contract test {
uint[] x;
function f() public returns (uint){
x.length = 1;
x[0] = 1;
delete x.length;
return x.length;
}
}
)";
ALSO_VIA_YUL(
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
BOOST_CHECK(storageEmpty(m_contractAddress));
)
}
BOOST_AUTO_TEST_CASE(constructor) BOOST_AUTO_TEST_CASE(constructor)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
@ -2849,10 +2829,9 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage)
bytes x; bytes x;
event Deposit(uint fixeda, bytes dynx, uint fixedb); event Deposit(uint fixeda, bytes dynx, uint fixedb);
function deposit() public { function deposit() public {
x.length = 3; x.push("A");
x[0] = "A"; x.push("B");
x[1] = "B"; x.push("C");
x[2] = "C";
emit Deposit(10, x, 15); emit Deposit(10, x, 15);
} }
} }
@ -2873,7 +2852,7 @@ BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage)
bytes x; bytes x;
event Deposit(uint fixeda, bytes dynx, uint fixedb); event Deposit(uint fixeda, bytes dynx, uint fixedb);
function deposit() public { function deposit() public {
x.length = 31; x = new bytes(31);
x[0] = "A"; x[0] = "A";
x[1] = "B"; x[1] = "B";
x[2] = "C"; x[2] = "C";
@ -3021,7 +3000,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage)
event E(uint[]); event E(uint[]);
uint[] arr; uint[] arr;
function createEvent(uint x) public { function createEvent(uint x) public {
arr.length = 3; while (arr.length < 3)
arr.push();
arr[0] = x; arr[0] = x;
arr[1] = x + 1; arr[1] = x + 1;
arr[2] = x + 2; arr[2] = x + 2;
@ -3049,7 +3029,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2)
event E(uint[]); event E(uint[]);
uint[] arr; uint[] arr;
function createEvent(uint x) public { function createEvent(uint x) public {
arr.length = 3; while (arr.length < 3)
arr.push();
arr[0] = x; arr[0] = x;
arr[1] = x + 1; arr[1] = x + 1;
arr[2] = x + 2; arr[2] = x + 2;
@ -3077,9 +3058,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
event E(uint[][]); event E(uint[][]);
uint[][] arr; uint[][] arr;
function createEvent(uint x) public { function createEvent(uint x) public {
arr.length = 2; arr.push(new uint[](2));
arr[0].length = 2; arr.push(new uint[](2));
arr[1].length = 2;
arr[0][0] = x; arr[0][0] = x;
arr[0][1] = x + 1; arr[0][1] = x + 1;
arr[1][0] = x + 2; arr[1][0] = x + 2;
@ -3088,16 +3068,16 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
} }
} }
)"; )";
ALSO_VIA_YUL( /// TODO enable again after push(..) via yul was implemented.
compileAndRun(sourceCode); /// ALSO_VIA_YUL()
u256 x(42); compileAndRun(sourceCode);
callContractFunction("createEvent(uint256)", x); u256 x(42);
BOOST_REQUIRE_EQUAL(numLogs(), 1); callContractFunction("createEvent(uint256)", x);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3)); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
BOOST_CHECK_EQUAL(logTopic(0, 0), dev::keccak256(string("E(uint256[][])"))); BOOST_REQUIRE_EQUAL(numLogTopics(0), 1);
); BOOST_CHECK_EQUAL(logTopic(0, 0), dev::keccak256(string("E(uint256[][])")));
} }
BOOST_AUTO_TEST_CASE(event_indexed_string) BOOST_AUTO_TEST_CASE(event_indexed_string)
@ -3108,7 +3088,8 @@ BOOST_AUTO_TEST_CASE(event_indexed_string)
uint[4] y; uint[4] y;
event E(string indexed r, uint[4] indexed t); event E(string indexed r, uint[4] indexed t);
function deposit() public { function deposit() public {
bytes(x).length = 90; for (uint i = 0; i < 90; i++)
bytes(x).push(0);
for (uint8 i = 0; i < 90; i++) for (uint8 i = 0; i < 90; i++)
bytes(x)[i] = byte(i); bytes(x)[i] = byte(i);
y[0] = 4; y[0] = 4;
@ -3286,10 +3267,9 @@ BOOST_AUTO_TEST_CASE(keccak256_with_bytes)
bytes data; bytes data;
function foo() public returns (bool) function foo() public returns (bool)
{ {
data.length = 3; data.push("f");
data[0] = "f"; data.push("o");
data[1] = "o"; data.push("o");
data[2] = "o";
return keccak256(data) == keccak256("foo"); return keccak256(data) == keccak256("foo");
} }
} }
@ -3305,10 +3285,9 @@ BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes)
bytes data; bytes data;
function foo() public returns (bytes32) function foo() public returns (bytes32)
{ {
data.length = 3; data.push("x");
data[0] = "x"; data.push("y");
data[1] = "y"; data.push("z");
data[2] = "z";
return keccak256(abi.encodePacked("b", keccak256(data), "a")); return keccak256(abi.encodePacked("b", keccak256(data), "a"));
} }
} }
@ -4257,7 +4236,12 @@ BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage)
function getID(uint index) public returns (uint) { return ids[index]; } function getID(uint index) public returns (uint) { return ids[index]; }
function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; }
function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; }
function setLengths(uint l1, uint l2) public { data.length = l1; ids.length = l2; } function setLengths(uint l1, uint l2) public {
while (data.length < l1)
data.push();
while (ids.length < l2)
ids.push();
}
} }
)"; )";
compileAndRun(sourceCode); compileAndRun(sourceCode);
@ -4302,24 +4286,28 @@ BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access)
char const* sourceCode = R"( char const* sourceCode = R"(
contract c { contract c {
uint[] data; uint[] data;
function enlarge(uint amount) public returns (uint) { return data.length += amount; } function enlarge(uint amount) public returns (uint) {
while (data.length - amount > 0)
data.push();
return data.length;
}
function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } function set(uint index, uint value) public returns (bool) { data[index] = value; return true; }
function get(uint index) public returns (uint) { return data[index]; } function get(uint index) public returns (uint) { return data[index]; }
function length() public returns (uint) { return data.length; } function length() public returns (uint) { return data.length; }
} }
)"; )";
ALSO_VIA_YUL( /// TODO enable again after push(..) via yul was implemented.
compileAndRun(sourceCode); /// ALSO_VIA_YUL()
ABI_CHECK(callContractFunction("length()"), encodeArgs(0)); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("get(uint256)", 3), bytes()); ABI_CHECK(callContractFunction("length()"), encodeArgs(0));
ABI_CHECK(callContractFunction("enlarge(uint256)", 4), encodeArgs(4)); ABI_CHECK(callContractFunction("get(uint256)", 3), bytes());
ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); ABI_CHECK(callContractFunction("enlarge(uint256)", 4), encodeArgs(4));
ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true)); ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4)); ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true));
ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4));
ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes()); ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes());
) ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
} }
BOOST_AUTO_TEST_CASE(fixed_array_cleanup) BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
@ -4375,23 +4363,26 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
uint[20] spacer; uint[20] spacer;
uint[] dynamic; uint[] dynamic;
function fill() public { function fill() public {
dynamic.length = 21; for (uint i = 0; i < 21; ++i)
for (uint i = 0; i < dynamic.length; ++i) dynamic[i] = i+1; dynamic.push(i + 1);
}
function halfClear() public {
while (dynamic.length > 5)
dynamic.pop();
} }
function halfClear() public { dynamic.length = 5; }
function fullClear() public { delete dynamic; } function fullClear() public { delete dynamic; }
} }
)"; )";
ALSO_VIA_YUL( /// TODO enable again after push(..) via yul was implemented.
compileAndRun(sourceCode); /// ALSO_VIA_YUL()
BOOST_CHECK(storageEmpty(m_contractAddress)); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("fill()"), bytes()); BOOST_CHECK(storageEmpty(m_contractAddress));
BOOST_CHECK(!storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("fill()"), bytes());
ABI_CHECK(callContractFunction("halfClear()"), bytes()); BOOST_CHECK(!storageEmpty(m_contractAddress));
BOOST_CHECK(!storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("halfClear()"), bytes());
ABI_CHECK(callContractFunction("fullClear()"), bytes()); BOOST_CHECK(!storageEmpty(m_contractAddress));
BOOST_CHECK(storageEmpty(m_contractAddress)); ABI_CHECK(callContractFunction("fullClear()"), bytes());
); BOOST_CHECK(storageEmpty(m_contractAddress));
} }
BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
@ -4401,9 +4392,12 @@ BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
struct s { uint[][] d; } struct s { uint[][] d; }
s[] data; s[] data;
function fill() public returns (uint) { function fill() public returns (uint) {
data.length = 3; while (data.length < 3)
data[2].d.length = 4; data.push();
data[2].d[3].length = 5; while (data[2].d.length < 4)
data[2].d.push();
while (data[2].d[3].length < 5)
data[2].d[3].push();
data[2].d[3][4] = 8; data[2].d[3][4] = 8;
return data[2].d[3][4]; return data[2].d[3][4];
} }
@ -4425,7 +4419,9 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn)
uint[] data1; uint[] data1;
uint[] data2; uint[] data2;
function setData1(uint length, uint index, uint value) public { function setData1(uint length, uint index, uint value) public {
data1.length = length; if (index < length) data1[index] = value; data1 = new uint[](length);
if (index < length)
data1[index] = value;
} }
function copyStorageStorage() public { data2 = data1; } function copyStorageStorage() public { data2 = data1; }
function getData2(uint index) public returns (uint len, uint val) { function getData2(uint index) public returns (uint len, uint val) {
@ -4489,7 +4485,7 @@ BOOST_AUTO_TEST_CASE(array_copy_different_packing)
bytes8[] data1; // 4 per slot bytes8[] data1; // 4 per slot
bytes10[] data2; // 3 per slot bytes10[] data2; // 3 per slot
function test() public returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) { function test() public returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) {
data1.length = 9; data1 = new bytes8[](9);
for (uint i = 0; i < data1.length; ++i) for (uint i = 0; i < data1.length; ++i)
data1[i] = bytes8(uint64(i)); data1[i] = bytes8(uint64(i));
data2 = data1; data2 = data1;
@ -4608,13 +4604,15 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct)
Data[] data1; Data[] data1;
Data[] data2; Data[] data2;
function test() public returns (uint x, uint y) { function test() public returns (uint x, uint y) {
data1.length = 9; while (data1.length < 9)
data1.push();
data1[8].x = 4; data1[8].x = 4;
data1[8].y = 5; data1[8].y = 5;
data2 = data1; data2 = data1;
x = data2[8].x; x = data2[8].x;
y = data2[8].y; y = data2[8].y;
data1.length = 0; while (data1.length > 0)
data1.pop();
data2 = data1; data2 = data1;
} }
} }
@ -4651,7 +4649,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi)
return z; return z;
} }
function test4() public returns (uint24[][] memory) { function test4() public returns (uint24[][] memory) {
w.length = 5; w = new uint24[][](5);
for (uint i = 0; i < 5; ++i) for (uint i = 0; i < 5; ++i)
for (uint j = 0; j < 101; ++j) for (uint j = 0; j < 101; ++j)
w[i].push(uint24(j)); w[i].push(uint24(j));
@ -4763,7 +4761,7 @@ BOOST_AUTO_TEST_CASE(array_push_packed_array)
x.push(3); x.push(3);
x.push(4); x.push(4);
x.push(5); x.push(5);
x.length = 4; x.pop();
return (x[0], x[1], x[2], x[3]); return (x[0], x[1], x[2], x[3]);
} }
} }
@ -5177,7 +5175,7 @@ BOOST_AUTO_TEST_CASE(bytes_index_access)
return uint(uint8(data[index])); return uint(uint8(data[index]));
} }
function storageWrite() external returns (uint) { function storageWrite() external returns (uint) {
data.length = 35; data = new bytes(35);
data[31] = 0x77; data[31] = 0x77;
data[32] = 0x14; data[32] = 0x14;
@ -5206,7 +5204,7 @@ BOOST_AUTO_TEST_CASE(bytes_delete_element)
contract c { contract c {
bytes data; bytes data;
function test1() external returns (bool) { function test1() external returns (bool) {
data.length = 100; data = new bytes(100);
for (uint i = 0; i < data.length; i++) for (uint i = 0; i < data.length; i++)
data[i] = byte(uint8(i)); data[i] = byte(uint8(i));
delete data[94]; delete data[94];
@ -5290,7 +5288,10 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
mapping(uint=>uint)[90][] large; mapping(uint=>uint)[90][] large;
mapping(uint=>uint)[3][] small; mapping(uint=>uint)[3][] small;
function test() public returns (uint r) { function test() public returns (uint r) {
large.length = small.length = 7; for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
large[3][2][0] = 2; large[3][2][0] = 2;
large[1] = large[3]; large[1] = large[3];
small[3][2][0] = 2; small[3][2][0] = 2;
@ -5302,13 +5303,20 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
large[1][2][0]; large[1][2][0];
delete small; delete small;
delete large; delete large;
} }
function clear() public returns (uint r) { function clear() public returns (uint, uint) {
large.length = small.length = 7; for (uint i = 0; i < 7; i++) {
large.push();
small.push();
}
small[3][2][0] = 0; small[3][2][0] = 0;
large[3][2][0] = 0; large[3][2][0] = 0;
small.length = large.length = 0; while (small.length > 0)
return 7; small.pop();
while (large.length > 0)
large.pop();
return (small.length, large.length);
} }
} }
)"; )";
@ -5316,7 +5324,7 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200)); ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200));
// storage is not empty because we cannot delete the mappings // storage is not empty because we cannot delete the mappings
BOOST_CHECK(!storageEmpty(m_contractAddress)); BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), encodeArgs(7)); ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0));
BOOST_CHECK(storageEmpty(m_contractAddress)); BOOST_CHECK(storageEmpty(m_contractAddress));
} }
@ -6312,7 +6320,8 @@ BOOST_AUTO_TEST_CASE(accessor_involving_strings)
struct stringData { string a; uint b; string c; } struct stringData { string a; uint b; string c; }
mapping(uint => stringData[]) public data; mapping(uint => stringData[]) public data;
function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) { function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) {
data[x].length = y + 1; while (data[x].length < y + 1)
data[x].push();
data[x][y].a = a; data[x][y].a = a;
data[x][y].b = b; data[x][y].b = b;
data[x][y].c = c; data[x][y].c = c;
@ -6682,7 +6691,7 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
contract Store is BinarySearch { contract Store is BinarySearch {
uint[] data; uint[] data;
function add(uint v) public { function add(uint v) public {
data.length++; data.push(0);
data[data.length - 1] = v; data[data.length - 1] = v;
} }
function find(uint v) public returns (uint) { function find(uint v) public returns (uint) {
@ -6690,25 +6699,25 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
} }
} }
)"; )";
ALSO_VIA_YUL( /// TODO enable again after push(..) via yul was implemented.
compileAndRun(sourceCode, 0, "Store"); /// ALSO_VIA_YUL()
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1))); compileAndRun(sourceCode, 0, "Store");
BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0))); BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs()); BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs()); ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs());
ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs());
ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3))); ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5))); ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3)));
ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5)));
ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1)));
ABI_CHECK(callContractFunction("find(uint256)", u256(400)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1)));
); ABI_CHECK(callContractFunction("find(uint256)", u256(400)), encodeArgs(u256(-1)));
} }
BOOST_AUTO_TEST_CASE(memory_types_initialisation) BOOST_AUTO_TEST_CASE(memory_types_initialisation)
@ -6796,7 +6805,8 @@ BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write)
return x; return x;
} }
function f() public returns (uint24[3][] memory) { function f() public returns (uint24[3][] memory) {
data[1].length = 4; while (data[1].length < 4)
data[1].push();
return set(data)[1]; return set(data)[1];
} }
} }
@ -8291,7 +8301,8 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library)
mapping(string => uint16[]) data; mapping(string => uint16[]) data;
function f() public returns (uint a, uint b) function f() public returns (uint a, uint b)
{ {
data["abc"].length = 20; while (data["abc"].length < 20)
data["abc"].push();
data["abc"][4] = 9; data["abc"][4] = 9;
data["abc"][17] = 3; data["abc"][17] = 3;
a = Lib.find(data["abc"], 9); a = Lib.find(data["abc"], 9);
@ -8556,7 +8567,8 @@ BOOST_AUTO_TEST_CASE(using_library_structs)
function set(Data storage _s) public function set(Data storage _s) public
{ {
_s.a = 7; _s.a = 7;
_s.b.length = 20; while (_s.b.length < 20)
_s.b.push();
_s.b[19] = 8; _s.b[19] = 8;
} }
} }
@ -8635,15 +8647,18 @@ BOOST_AUTO_TEST_CASE(short_strings)
if (data2.length != 51) return 4; if (data2.length != 51) return 4;
if (data2[data2.length - 1] != "a") return 5; if (data2[data2.length - 1] != "a") return 5;
// change length: short -> short // change length: short -> short
data1.length = 5; while (data1.length < 5)
data1.push();
if (data1.length != 5) return 6; if (data1.length != 5) return 6;
data1[4] = "4"; data1[4] = "4";
if (data1[0] != "1") return 7; if (data1[0] != "1") return 7;
if (data1[4] != "4") return 8; if (data1[4] != "4") return 8;
// change length: short -> long // change length: short -> long
data1.length = 80; while (data1.length < 80)
data1.push();
if (data1.length != 80) return 9; if (data1.length != 80) return 9;
data1.length = 70; while (data1.length > 70)
data1.pop();
if (data1.length != 70) return 9; if (data1.length != 70) return 9;
if (data1[0] != "1") return 10; if (data1[0] != "1") return 10;
if (data1[4] != "4") return 11; if (data1[4] != "4") return 11;
@ -8652,20 +8667,25 @@ BOOST_AUTO_TEST_CASE(short_strings)
if (uint8(data1[4]) != 4 * 3) return 12; if (uint8(data1[4]) != 4 * 3) return 12;
if (uint8(data1[67]) != 67 * 3) return 13; if (uint8(data1[67]) != 67 * 3) return 13;
// change length: long -> short // change length: long -> short
data1.length = 22; while (data1.length > 22)
data1.pop();
if (data1.length != 22) return 14; if (data1.length != 22) return 14;
if (uint8(data1[21]) != 21 * 3) return 15; if (uint8(data1[21]) != 21 * 3) return 15;
if (uint8(data1[2]) != 2 * 3) return 16; if (uint8(data1[2]) != 2 * 3) return 16;
// change length: short -> shorter // change length: short -> shorter
data1.length = 19; while (data1.length > 19)
data1.pop();
if (data1.length != 19) return 17; if (data1.length != 19) return 17;
if (uint8(data1[7]) != 7 * 3) return 18; if (uint8(data1[7]) != 7 * 3) return 18;
// and now again to original size // and now again to original size
data1.length = 22; while (data1.length < 22)
data1.push();
if (data1.length != 22) return 19; if (data1.length != 22) return 19;
if (data1[21] != 0) return 20; if (data1[21] != 0) return 20;
data1.length = 0; while (data1.length > 0)
data2.length = 0; data1.pop();
while (data2.length > 0)
data2.pop();
} }
function copy() public returns (uint) { function copy() public returns (uint) {
bytes memory x = "123"; bytes memory x = "123";
@ -8864,8 +8884,7 @@ BOOST_AUTO_TEST_CASE(tuples)
return (5, 6); return (5, 6);
} }
function f() public returns (uint) { function f() public returns (uint) {
data.length = 1; data.push(3);
data[0] = 3;
uint a; uint b; uint a; uint b;
(a, b) = this.h(); (a, b) = this.h();
if (a != 5 || b != 6) return 1; if (a != 5 || b != 6) return 1;
@ -8941,7 +8960,7 @@ BOOST_AUTO_TEST_CASE(destructuring_assignment)
uint[] y; uint[] y;
uint[] arrayData; uint[] arrayData;
function returnsArray() public returns (uint[] memory) { function returnsArray() public returns (uint[] memory) {
arrayData.length = 9; arrayData = new uint[](9);
arrayData[2] = 5; arrayData[2] = 5;
arrayData[7] = 4; arrayData[7] = 4;
return arrayData; return arrayData;
@ -9915,9 +9934,10 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs)
struct S { uint x; uint[] y; } struct S { uint x; uint[] y; }
S[] data; S[] data;
function f() public returns (bool) { function f() public returns (bool) {
data.length = 2; S storage s1 = data.push();
data[0].x = 2**200; s1.x = 2**200;
data[1].x = 2**200; S storage s2 = data.push();
s2.x = 2**200;
delete data; delete data;
return true; return true;
} }
@ -11099,7 +11119,7 @@ BOOST_AUTO_TEST_CASE(copy_function_storage_array)
function() internal returns (uint)[] x; function() internal returns (uint)[] x;
function() internal returns (uint)[] y; function() internal returns (uint)[] y;
function test() public returns (uint) { function test() public returns (uint) {
x.length = 10; x = new function() internal returns (uint)[](10);
x[9] = a; x[9] = a;
y = x; y = x;
return y[9](); return y[9]();
@ -12009,7 +12029,8 @@ BOOST_AUTO_TEST_CASE(recursive_structs)
S memory s; S memory s;
s.x = new S[](10); s.x = new S[](10);
delete s; delete s;
sstorage.x.length++; // TODO Uncomment after implemented.
// sstorage.x.push();
delete sstorage; delete sstorage;
return 1; return 1;
} }
@ -13292,11 +13313,15 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage)
return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02)); return abi.encodePacked(uint8(0x01), small_fixed, uint8(0x02));
} }
function sd() public returns (bytes memory) { function sd() public returns (bytes memory) {
small_dyn.length = 9; small_dyn.push(0xfffff1);
small_dyn[0] = 0xfffff1; small_dyn.push(0x00);
small_dyn[2] = 0xfffff2; small_dyn.push(0xfffff2);
small_dyn[5] = 0xfffff3; small_dyn.push(0x00);
small_dyn[8] = 0xfffff4; small_dyn.push(0x00);
small_dyn.push(0xfffff3);
small_dyn.push(0x00);
small_dyn.push(0x00);
small_dyn.push(0xfffff4);
return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02)); return abi.encodePacked(uint8(0x01), small_dyn, uint8(0x02));
} }
function sfs() public returns (bytes memory) { function sfs() public returns (bytes memory) {
@ -13314,11 +13339,11 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage)
return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02)); return abi.encodePacked(uint8(0x01), large_fixed, uint8(0x02));
} }
function ld() public returns (bytes memory) { function ld() public returns (bytes memory) {
large_dyn.length = 5; large_dyn.push(2**248-1);
large_dyn[0] = 2**248-1; large_dyn.push(0xfffff2);
large_dyn[1] = 0xfffff2; large_dyn.push(2**248-2);
large_dyn[2] = 2**248-2; large_dyn.push(0);
large_dyn[4] = 0xfffff4; large_dyn.push(0xfffff4);
return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02)); return abi.encodePacked(uint8(0x01), large_dyn, uint8(0x02));
} }
function bytes_short() public returns (bytes memory) { function bytes_short() public returns (bytes memory) {
@ -13476,9 +13501,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePackedV2_structs)
s.b = -7; s.b = -7;
s.c[0] = 2; s.c[0] = 2;
s.c[1] = 3; s.c[1] = 3;
s.d.length = 2; s.d.push(-7);
s.d[0] = -7; s.d.push(-8);
s.d[1] = -8;
} }
function testStorage() public { function testStorage() public {
emit E(s); emit E(s);
@ -13538,9 +13562,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePackedV2_arrayOfStrings)
string[] x; string[] x;
event E(string[] indexed); event E(string[] indexed);
constructor() public { constructor() public {
x.length = 2; x.push("abc");
x[0] = "abc"; x.push("0123456789012345678901234567890123456789");
x[1] = "0123456789012345678901234567890123456789";
} }
function testStorage() public { function testStorage() public {
emit E(x); emit E(x);

View File

@ -206,7 +206,8 @@ BOOST_AUTO_TEST_CASE(array_copy)
bytes2[] data1; bytes2[] data1;
bytes5[] data2; bytes5[] data2;
function f(uint x) public returns (uint l, uint y) { function f(uint x) public returns (uint l, uint y) {
data1.length = msg.data.length; for (uint i = 0; i < msg.data.length; i++)
data1.push();
for (uint i = 0; i < msg.data.length; ++i) for (uint i = 0; i < msg.data.length; ++i)
data1[i] = msg.data[i]; data1[i] = msg.data[i];
data2 = data1; data2 = data1;
@ -533,16 +534,15 @@ BOOST_AUTO_TEST_CASE(inconsistency)
} }
function trigger() public returns (uint) { function trigger() public returns (uint) {
containers.length++; Container storage container = containers.push();
Container storage container = containers[0];
container.values.push(Value({ container.values.push(Value({
badnum: 9000, badnum: 9000,
number: 0 number: 0
})); }));
container.valueIndices.length++; container.valueIndices.push();
valueIndices.length++; valueIndices.push();
levelII(); levelII();
return debug; return debug;

View File

@ -3,7 +3,8 @@ contract test {
mapping(uint => uint[]) public dynamicData; mapping(uint => uint[]) public dynamicData;
constructor() public { constructor() public {
data[2][2] = 8; data[2][2] = 8;
dynamicData[2].length = 3; for (uint i = 0; i < 3; i++)
dynamicData[2].push();
dynamicData[2][2] = 8; dynamicData[2][2] = 8;
} }
} }

View File

@ -7,13 +7,18 @@ contract test {
constructor() public { constructor() public {
data[0] = 8; data[0] = 8;
dynamicData.length = 3;
dynamicData[2] = 8; dynamicData.push();
smallTypeData.length = 128; dynamicData.push();
dynamicData.push(8);
smallTypeData = new uint24[](128);
smallTypeData[1] = 22; smallTypeData[1] = 22;
smallTypeData[127] = 2; smallTypeData[127] = 2;
multiple_map[2][1][2].a = 3; multiple_map[2][1][2].a = 3;
multiple_map[2][1][2].finalArray.length = 4; for (uint i = 0; i < 4; i++)
multiple_map[2][1][2].finalArray.push();
multiple_map[2][1][2].finalArray[3] = 5; multiple_map[2][1][2].finalArray[3] = 5;
} }
} }

View File

@ -2,16 +2,16 @@ contract C {
mapping (uint => uint)[][] a; mapping (uint => uint)[][] a;
function n1(uint key, uint value) public { function n1(uint key, uint value) public {
a.length++; a.push();
mapping (uint => uint)[] storage b = a[a.length - 1]; mapping (uint => uint)[] storage b = a[a.length - 1];
b.length++; b.push();
b[b.length - 1][key] = value; b[b.length - 1][key] = value;
} }
function n2() public { function n2() public {
a.length++; a.push();
mapping (uint => uint)[] storage b = a[a.length - 1]; mapping (uint => uint)[] storage b = a[a.length - 1];
b.length++; b.push();
} }
function map(uint key) public view returns (uint) { function map(uint key) public view returns (uint) {

View File

@ -2,12 +2,12 @@ contract C {
mapping (uint => uint)[] a; mapping (uint => uint)[] a;
function n1(uint key, uint value) public { function n1(uint key, uint value) public {
a.length++; a.push();
a[a.length - 1][key] = value; a[a.length - 1][key] = value;
} }
function n2() public { function n2() public {
a.length++; a.push();
} }
function map(uint key) public view returns (uint) { function map(uint key) public view returns (uint) {

View File

@ -20,8 +20,10 @@ contract Test {
storageA.m[1] = 2; storageA.m[1] = 2;
storageB.m[3] = 4; storageB.m[3] = 4;
storageB.x = 5; storageB.x = 5;
storageC.ma.length = 6; for (uint i = 0; i < 6; i++)
storageD.a.length = 7; storageC.ma.push();
for (uint i = 0; i < 7; i++)
storageD.a.push();
} }
function run() public returns (uint, uint, uint, uint, uint, uint) { function run() public returns (uint, uint, uint, uint, uint, uint) {
A memory memoryA = A(); A memory memoryA = A();

View File

@ -1,20 +1,25 @@
contract CopyTest { contract CopyTest {
struct Tree { struct Tree {
Tree[] children; Tree[] children;
} }
Tree storageTree;
constructor() public { Tree storageTree;
storageTree.children.length = 2; Tree[] children;
storageTree.children[0].children.length = 23;
storageTree.children[1].children.length = 42;
}
function run() public returns (uint256, uint256, uint256) { constructor() public {
Tree memory memoryTree; for (uint i = 0; i < 2; i++)
memoryTree = storageTree; storageTree.children.push();
return (memoryTree.children.length, memoryTree.children[0].children.length, memoryTree.children[1].children.length); for (uint i = 0; i < 23; i++)
} storageTree.children[0].children.push();
for (uint i = 0; i < 42; i++)
storageTree.children[1].children.push();
}
function run() public returns (uint256, uint256, uint256) {
Tree memory memoryTree;
memoryTree = storageTree;
return (memoryTree.children.length, memoryTree.children[0].children.length, memoryTree.children[1].children.length);
}
} }
// ---- // ----
// run() -> 2, 23, 42 // run() -> 2, 23, 42

View File

@ -1,46 +1,50 @@
contract CopyTest { contract CopyTest {
struct Tree { struct Tree {
uint256 data; uint256 data;
Tree[] children; Tree[] children;
} }
Tree storageTree; Tree storageTree;
Tree childStorageTree;
constructor() public { constructor() public {
storageTree.data = 0x42; storageTree.data = 0x42;
storageTree.children.length = 2; for (uint i = 0; i < 2; i++)
storageTree.children[0].data = 0x4200; storageTree.children.push(childStorageTree);
storageTree.children[1].data = 0x4201; storageTree.children[0].data = 0x4200;
storageTree.children[0].children.length = 3; storageTree.children[1].data = 0x4201;
for (uint i = 0; i < 3; i++) for (uint i = 0; i < 3; i++)
storageTree.children[0].children[i].data = 0x420000 + i; storageTree.children[0].children.push(childStorageTree);
storageTree.children[1].children.length = 4; for (uint i = 0; i < 3; i++)
for (uint i = 0; i < 4; i++) storageTree.children[0].children[i].data = 0x420000 + i;
storageTree.children[1].children[i].data = 0x420100 + i; for (uint i = 0; i < 4; i++)
} storageTree.children[1].children.push(childStorageTree);
for (uint i = 0; i < 4; i++)
storageTree.children[1].children[i].data = 0x420100 + i;
}
function countData(Tree memory tree) internal returns (uint256 c) { function countData(Tree memory tree) internal returns (uint256 c) {
c = 1; c = 1;
for (uint i = 0; i < tree.children.length; i++) { for (uint i = 0; i < tree.children.length; i++) {
c += countData(tree.children[i]); c += countData(tree.children[i]);
} }
} }
function copyFromTree(Tree memory tree, uint256[] memory data, uint256 offset) internal returns (uint256) { function copyFromTree(Tree memory tree, uint256[] memory data, uint256 offset) internal returns (uint256) {
data[offset++] = tree.data; data[offset++] = tree.data;
for (uint i = 0; i < tree.children.length; i++) { for (uint i = 0; i < tree.children.length; i++) {
offset = copyFromTree(tree.children[i], data, offset); offset = copyFromTree(tree.children[i], data, offset);
} }
return offset; return offset;
} }
function run() public returns (uint256[] memory) { function run() public returns (uint256[] memory) {
Tree memory memoryTree; Tree memory memoryTree;
memoryTree = storageTree; memoryTree = storageTree;
uint256 length = countData(memoryTree); uint256 length = countData(memoryTree);
uint256[] memory result = new uint256[](length); uint256[] memory result = new uint256[](length);
copyFromTree(memoryTree, result, 0); copyFromTree(memoryTree, result, 0);
return result; return result;
} }
} }
// ---- // ----
// run() -> 0x20, 10, 0x42, 0x4200, 0x420000, 0x420001, 0x420002, 0x4201, 0x420100, 0x420101, 0x420102, 0x420103 // run() -> 0x20, 10, 0x42, 0x4200, 0x420000, 0x420001, 0x420002, 0x4201, 0x420100, 0x420101, 0x420102, 0x420103

View File

@ -1,26 +1,28 @@
contract C { contract C {
uint[] storageArray; uint[] storageArray;
function test_indicies(uint256 len) public function test_indices(uint256 len) public
{ {
storageArray.length = len; while (storageArray.length < len)
storageArray.push();
while (storageArray.length > len)
storageArray.pop();
for (uint i = 0; i < len; i++)
storageArray[i] = i + 1;
for (uint i = 0; i < len; i++) for (uint i = 0; i < len; i++)
storageArray[i] = i + 1; require(storageArray[i] == i + 1);
}
for (uint i = 0; i < len; i++)
require(storageArray[i] == i + 1);
}
} }
// ==== // ====
// compileViaYul: true // compileViaYul: true
// ---- // ----
// test_indicies(uint256): 1 -> // test_indices(uint256): 1 ->
// test_indicies(uint256): 129 -> // test_indices(uint256): 129 ->
// test_indicies(uint256): 5 -> // test_indices(uint256): 5 ->
// test_indicies(uint256): 10 -> // test_indices(uint256): 10 ->
// test_indicies(uint256): 15 -> // test_indices(uint256): 15 ->
// test_indicies(uint256): 0xFF -> // test_indices(uint256): 0xFF ->
// test_indicies(uint256): 1000 -> // test_indices(uint256): 1000 ->
// test_indicies(uint256): 129 -> // test_indices(uint256): 129 ->
// test_indicies(uint256): 128 -> // test_indices(uint256): 128 ->
// test_indicies(uint256): 1 -> // test_indices(uint256): 1 ->

View File

@ -0,0 +1,23 @@
contract C {
uint[] storageArray;
function test_boundary_check(uint256 len, uint256 access) public returns (uint256)
{
while(storageArray.length < len)
storageArray.push();
while(storageArray.length > len)
storageArray.pop();
return storageArray[access];
}
}
// ====
// compileViaYul: true
// ----
// test_boundary_check(uint256, uint256): 10, 11 -> FAILURE
// test_boundary_check(uint256, uint256): 10, 9 -> 0
// test_boundary_check(uint256, uint256): 1, 9 -> FAILURE
// test_boundary_check(uint256, uint256): 1, 1 -> FAILURE
// test_boundary_check(uint256, uint256): 10, 10 -> FAILURE
// test_boundary_check(uint256, uint256): 256, 256 -> FAILURE
// test_boundary_check(uint256, uint256): 256, 255 -> 0
// test_boundary_check(uint256, uint256): 256, 0xFFFF -> FAILURE
// test_boundary_check(uint256, uint256): 256, 2 -> 0

View File

@ -1,21 +0,0 @@
contract C {
uint[] storageArray;
function test_boundery_check(uint256 len, uint256 access) public returns
(uint256)
{
storageArray.length = len;
return storageArray[access];
}
}
// ====
// compileViaYul: true
// ----
// test_boundery_check(uint256, uint256): 10, 11 -> FAILURE
// test_boundery_check(uint256, uint256): 10, 9 -> 0
// test_boundery_check(uint256, uint256): 1, 9 -> FAILURE
// test_boundery_check(uint256, uint256): 1, 1 -> FAILURE
// test_boundery_check(uint256, uint256): 10, 10 -> FAILURE
// test_boundery_check(uint256, uint256): 256, 256 -> FAILURE
// test_boundery_check(uint256, uint256): 256, 255 -> 0
// test_boundery_check(uint256, uint256): 256, 0xFFFF -> FAILURE
// test_boundery_check(uint256, uint256): 256, 2 -> 0

View File

@ -1,45 +1,53 @@
contract C { contract C {
uint[] storageArray; uint[] storageArray;
function test_zeroed_indicies(uint256 len) public function test_zeroed_indicies(uint256 len) public
{ {
storageArray.length = len; while(storageArray.length < len)
storageArray.push();
while(storageArray.length > len)
storageArray.pop();
for (uint i = 0; i < len; i++) for (uint i = 0; i < len; i++)
storageArray[i] = i + 1; storageArray[i] = i + 1;
if (len > 3) if (len > 3)
{ {
storageArray.length = 3; while(storageArray.length > 0)
storageArray.pop();
while(storageArray.length < 3)
storageArray.push();
for (uint i = 3; i < len; i++) for (uint i = 3; i < len; i++)
{ {
assembly { assembly {
mstore(0, storageArray_slot) mstore(0, storageArray_slot)
let pos := add(keccak256(0, 0x20), i) let pos := add(keccak256(0, 0x20), i)
if iszero(eq(sload(pos), 0)) { if iszero(eq(sload(pos), 0)) {
revert(0, 0) revert(0, 0)
} }
} }
} }
} }
storageArray.length = 0; while(storageArray.length > 0)
storageArray.length = len; storageArray.pop();
while(storageArray.length < len)
storageArray.push();
for (uint i = 0; i < len; i++) for (uint i = 0; i < len; i++)
{ {
require(storageArray[i] == 0); require(storageArray[i] == 0);
uint256 val = storageArray[i]; uint256 val = storageArray[i];
uint256 check; uint256 check;
assembly { check := iszero(val) } assembly { check := iszero(val) }
require(check == 1); require(check == 1);
} }
} }
} }
// ==== // ====
// compileViaYul: true // compileViaYul: true

View File

@ -1,11 +1,10 @@
contract C { contract C {
uint[] storageArray; uint[] storageArray;
function set_get_length(uint256 len) public returns (uint256) function set_get_length(uint256 len) public returns (uint256) {
{ while(storageArray.length < len)
storageArray.length = len; storageArray.push();
return storageArray.length; return storageArray.length;
} }
} }
// ==== // ====
// compileViaYul: true // compileViaYul: true
@ -14,6 +13,6 @@ contract C {
// set_get_length(uint256): 1 -> 1 // set_get_length(uint256): 1 -> 1
// set_get_length(uint256): 10 -> 10 // set_get_length(uint256): 10 -> 10
// set_get_length(uint256): 20 -> 20 // set_get_length(uint256): 20 -> 20
// set_get_length(uint256): 0 -> 0
// set_get_length(uint256): 0xFF -> 0xFF // set_get_length(uint256): 0xFF -> 0xFF
// set_get_length(uint256): 0xFFFF -> 0xFFFF // set_get_length(uint256): 0xFFF -> 0xFFF
// set_get_length(uint256): 0xFFFF -> FAILURE # Out-of-gas #

View File

@ -0,0 +1,11 @@
contract C {
uint[] storageArray;
function popEmpty() public {
storageArray.pop();
}
}
// ====
// EVMVersion: >=petersburg
// compileViaYul: true
// ----
// popEmpty() -> FAILURE

View File

@ -0,0 +1,17 @@
contract C {
uint256[] storageArray;
function pushEmpty(uint256 len) public {
while(storageArray.length < len)
storageArray.push();
for (uint i = 0; i < len; i++)
require(storageArray[i] == 0);
}
}
// ====
// compileViaYul: true
// EVMVersion: >=petersburg
// ----
// pushEmpty(uint256): 128
// pushEmpty(uint256): 256
// pushEmpty(uint256): 32768 -> FAILURE # out-of-gas #

View File

@ -0,0 +1,23 @@
contract C {
address[] addressArray;
function set_get_length(uint256 len) public returns (uint256)
{
while(addressArray.length < len)
addressArray.push();
while(addressArray.length > len)
addressArray.pop();
return addressArray.length;
}
}
// ====
// compileViaYul: true
// EVMVersion: >=petersburg
// ----
// set_get_length(uint256): 0 -> 0
// set_get_length(uint256): 1 -> 1
// set_get_length(uint256): 10 -> 10
// set_get_length(uint256): 20 -> 20
// set_get_length(uint256): 0 -> 0
// set_get_length(uint256): 0xFF -> 0xFF
// set_get_length(uint256): 0xFFF -> 0xFFF
// set_get_length(uint256): 0xFFFF -> FAILURE # Out-of-gas #

View File

@ -0,0 +1,20 @@
contract C {
uint[] storageArray;
function set_get_length(uint256 len) public returns (uint256) {
while(storageArray.length < len)
storageArray.push();
while(storageArray.length > 0)
storageArray.pop();
return storageArray.length;
}
}
// ====
// compileViaYul: true
// ----
// set_get_length(uint256): 0 -> 0
// set_get_length(uint256): 1 -> 0
// set_get_length(uint256): 10 -> 0
// set_get_length(uint256): 20 -> 0
// set_get_length(uint256): 0xFF -> 0
// set_get_length(uint256): 0xFFF -> 0
// set_get_length(uint256): 0xFFFF -> FAILURE # Out-of-gas #

View File

@ -4,4 +4,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (75-83): Calldata arrays cannot be resized. // TypeError: (75-83): Member "length" is read-only and cannot be used to resize arrays.

View File

@ -0,0 +1,8 @@
contract c {
uint[] storageArray;
function f() public {
storageArray.length = 3;
}
}
// ----
// TypeError: (72-91): Member "length" is read-only and cannot be used to resize arrays.

View File

@ -0,0 +1,8 @@
contract C {
mapping(uint => uint[]) map;
function f() public {
map[0].length = 4;
}
}
// ----
// TypeError: (80-93): Member "length" is read-only and cannot be used to resize arrays.

View File

@ -0,0 +1,11 @@
contract C {
struct S {
uint[] a;
}
S s;
function f() public {
s.a.length = 4;
}
}
// ----
// TypeError: (95-105): Member "length" is read-only and cannot be used to resize arrays.

View File

@ -0,0 +1,7 @@
contract C {
function f(uint[] calldata x) external {
x.pop();
}
}
// ----
// TypeError: (66-71): Member "pop" is not available in uint256[] calldata outside of storage.

View File

@ -0,0 +1,8 @@
contract C {
function f() public {
uint[] memory x;
x.pop();
}
}
// ----
// TypeError: (72-77): Member "pop" is not available in uint256[] memory outside of storage.

View File

@ -0,0 +1,7 @@
contract C {
function f(uint[] calldata x) external {
x.push();
}
}
// ----
// TypeError: (66-72): Member "push" is not available in uint256[] calldata outside of storage.

View File

@ -0,0 +1,8 @@
contract C {
function f() public {
uint[] memory x;
x.push();
}
}
// ----
// TypeError: (72-78): Member "push" is not available in uint256[] memory outside of storage.

View File

@ -5,4 +5,4 @@ contract C {
} }
} }
// ---- // ----
// TypeError: (72-80): Memory arrays cannot be resized. // TypeError: (72-80): Member "length" is read-only and cannot be used to resize arrays.

View File

@ -0,0 +1,7 @@
contract A {
uint[] x;
function g() public returns (uint) {
return x.push();
}
}
// ----

View File

@ -0,0 +1,8 @@
contract A {
uint[] x;
function f() public view returns (uint) {
return x.push();
}
}
// ----
// TypeError: (88-96): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable.

View File

@ -3,9 +3,6 @@ contract A {
} }
contract B is A { contract B is A {
function f() public view {
A.x.length = 2;
}
function g() public pure returns (uint) { function g() public pure returns (uint) {
return A.x.length; return A.x.length;
} }
@ -14,8 +11,7 @@ contract B is A {
} }
} }
// ---- // ----
// TypeError: (87-97): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. // TypeError: (109-112): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError: (170-173): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError: (109-119): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError: (170-180): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError: (188-191): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError: (249-252): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError: (188-194): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".
// TypeError: (249-255): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".