mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7350 from ethereum/array-length-rvalue
Make `length` member read-only
This commit is contained in:
commit
d5e9271592
@ -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.
|
||||||
|
@ -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
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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++;
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
@ -642,6 +642,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
||||||
}
|
}
|
||||||
@ -788,8 +816,8 @@ 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
|
else
|
||||||
@ -800,14 +828,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(" <<
|
||||||
@ -815,6 +841,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
")\n";
|
")\n";
|
||||||
break;
|
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
|
||||||
|
solAssert(false, "Invalid array member access.");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::FixedBytes:
|
case Type::Category::FixedBytes:
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,7 +3068,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
ALSO_VIA_YUL(
|
/// TODO enable again after push(..) via yul was implemented.
|
||||||
|
/// ALSO_VIA_YUL()
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
u256 x(42);
|
u256 x(42);
|
||||||
callContractFunction("createEvent(uint256)", x);
|
callContractFunction("createEvent(uint256)", x);
|
||||||
@ -3097,7 +3078,6 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
|
|||||||
BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
|
BOOST_CHECK(logData(0) == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
|
||||||
BOOST_REQUIRE_EQUAL(numLogTopics(0), 1);
|
BOOST_REQUIRE_EQUAL(numLogTopics(0), 1);
|
||||||
BOOST_CHECK_EQUAL(logTopic(0, 0), dev::keccak256(string("E(uint256[][])")));
|
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,13 +4286,18 @@ 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.
|
||||||
|
/// ALSO_VIA_YUL()
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
ABI_CHECK(callContractFunction("length()"), encodeArgs(0));
|
ABI_CHECK(callContractFunction("length()"), encodeArgs(0));
|
||||||
ABI_CHECK(callContractFunction("get(uint256)", 3), bytes());
|
ABI_CHECK(callContractFunction("get(uint256)", 3), bytes());
|
||||||
@ -4319,7 +4308,6 @@ BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access)
|
|||||||
ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
|
ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
|
||||||
ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes());
|
ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes());
|
||||||
ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
|
ABI_CHECK(callContractFunction("length()"), encodeArgs(4));
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
|
BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
|
||||||
@ -4375,14 +4363,18 @@ 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.
|
||||||
|
/// ALSO_VIA_YUL()
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
ABI_CHECK(callContractFunction("fill()"), bytes());
|
ABI_CHECK(callContractFunction("fill()"), bytes());
|
||||||
@ -4391,7 +4383,6 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
|
|||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
ABI_CHECK(callContractFunction("fullClear()"), bytes());
|
ABI_CHECK(callContractFunction("fullClear()"), bytes());
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
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, uint) {
|
||||||
|
for (uint i = 0; i < 7; i++) {
|
||||||
|
large.push();
|
||||||
|
small.push();
|
||||||
}
|
}
|
||||||
function clear() public returns (uint r) {
|
|
||||||
large.length = small.length = 7;
|
|
||||||
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,7 +6699,8 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
ALSO_VIA_YUL(
|
/// TODO enable again after push(..) via yul was implemented.
|
||||||
|
/// ALSO_VIA_YUL()
|
||||||
compileAndRun(sourceCode, 0, "Store");
|
compileAndRun(sourceCode, 0, "Store");
|
||||||
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
|
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
|
||||||
BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
|
BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
|
||||||
@ -6708,7 +6718,6 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
|
|||||||
ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1)));
|
ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1)));
|
||||||
ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1)));
|
ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1)));
|
||||||
ABI_CHECK(callContractFunction("find(uint256)", u256(400)), 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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -2,12 +2,17 @@ contract CopyTest {
|
|||||||
struct Tree {
|
struct Tree {
|
||||||
Tree[] children;
|
Tree[] children;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree storageTree;
|
Tree storageTree;
|
||||||
|
Tree[] children;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public {
|
||||||
storageTree.children.length = 2;
|
for (uint i = 0; i < 2; i++)
|
||||||
storageTree.children[0].children.length = 23;
|
storageTree.children.push();
|
||||||
storageTree.children[1].children.length = 42;
|
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) {
|
function run() public returns (uint256, uint256, uint256) {
|
||||||
|
@ -4,16 +4,20 @@ contract CopyTest {
|
|||||||
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.push(childStorageTree);
|
||||||
storageTree.children[0].data = 0x4200;
|
storageTree.children[0].data = 0x4200;
|
||||||
storageTree.children[1].data = 0x4201;
|
storageTree.children[1].data = 0x4201;
|
||||||
storageTree.children[0].children.length = 3;
|
for (uint i = 0; i < 3; i++)
|
||||||
|
storageTree.children[0].children.push(childStorageTree);
|
||||||
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[i].data = 0x420000 + i;
|
||||||
storageTree.children[1].children.length = 4;
|
for (uint i = 0; i < 4; i++)
|
||||||
|
storageTree.children[1].children.push(childStorageTree);
|
||||||
for (uint i = 0; i < 4; i++)
|
for (uint i = 0; i < 4; i++)
|
||||||
storageTree.children[1].children[i].data = 0x420100 + i;
|
storageTree.children[1].children[i].data = 0x420100 + i;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
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++)
|
for (uint i = 0; i < len; i++)
|
||||||
storageArray[i] = i + 1;
|
storageArray[i] = i + 1;
|
||||||
|
|
||||||
@ -14,13 +16,13 @@ contract C {
|
|||||||
// ====
|
// ====
|
||||||
// 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 ->
|
||||||
|
@ -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
|
@ -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
|
|
@ -2,14 +2,20 @@ 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++)
|
||||||
{
|
{
|
||||||
@ -25,8 +31,10 @@ contract C {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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++)
|
||||||
{
|
{
|
||||||
|
@ -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 #
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
uint[] storageArray;
|
||||||
|
function popEmpty() public {
|
||||||
|
storageArray.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >=petersburg
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// popEmpty() -> FAILURE
|
@ -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 #
|
@ -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 #
|
@ -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 #
|
@ -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.
|
||||||
|
@ -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.
|
@ -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.
|
@ -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.
|
7
test/libsolidity/syntaxTests/array/pop/calldata_pop.sol
Normal file
7
test/libsolidity/syntaxTests/array/pop/calldata_pop.sol
Normal 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.
|
8
test/libsolidity/syntaxTests/array/pop/memory_pop.sol
Normal file
8
test/libsolidity/syntaxTests/array/pop/memory_pop.sol
Normal 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.
|
@ -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.
|
8
test/libsolidity/syntaxTests/array/push/memory_push.sol
Normal file
8
test/libsolidity/syntaxTests/array/push/memory_push.sol
Normal 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.
|
@ -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.
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
contract A {
|
||||||
|
uint[] x;
|
||||||
|
function g() public returns (uint) {
|
||||||
|
return x.push();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -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.
|
@ -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".
|
|
Loading…
Reference in New Issue
Block a user