mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implement array push and pop for yul and replace assignments in via yul tests.
This commit is contained in:
parent
2d2fb547e7
commit
372df6b9e1
@ -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)
|
||||||
|
@ -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,31 +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:
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::FixedBytes:
|
case Type::Category::FixedBytes:
|
||||||
|
@ -2,7 +2,9 @@ contract C {
|
|||||||
uint[] storageArray;
|
uint[] storageArray;
|
||||||
function test_indices(uint256 len) public
|
function test_indices(uint256 len) public
|
||||||
{
|
{
|
||||||
storageArray = new uint[](len);
|
// storageArray = new uint[](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;
|
||||||
|
|
||||||
@ -10,6 +12,8 @@ contract C {
|
|||||||
require(storageArray[i] == i + 1);
|
require(storageArray[i] == i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// test_indices(uint256): 1 ->
|
// test_indices(uint256): 1 ->
|
||||||
// test_indices(uint256): 129 ->
|
// test_indices(uint256): 129 ->
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
contract C {
|
||||||
|
uint[] storageArray;
|
||||||
|
function test_boundary_check(uint256 len, uint256 access) public returns (uint256)
|
||||||
|
{
|
||||||
|
// storageArray = new uint[](len);
|
||||||
|
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,18 +0,0 @@
|
|||||||
contract C {
|
|
||||||
uint[] storageArray;
|
|
||||||
function test_boundery_check(uint256 len, uint256 access) public returns (uint256)
|
|
||||||
{
|
|
||||||
storageArray = new uint[](len);
|
|
||||||
return storageArray[access];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ----
|
|
||||||
// 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,18 @@ contract C {
|
|||||||
uint[] storageArray;
|
uint[] storageArray;
|
||||||
function test_zeroed_indicies(uint256 len) public
|
function test_zeroed_indicies(uint256 len) public
|
||||||
{
|
{
|
||||||
storageArray = new uint[](len);
|
//storageArray = new uint[](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 = new uint[](3);
|
//storageArray = new uint[](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 +29,10 @@ contract C {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storageArray = new uint[](0);
|
//storageArray = new uint[](0);
|
||||||
storageArray = new uint[](len);
|
while(storageArray.length > 0) storageArray.pop();
|
||||||
|
//storageArray = new uint[](len);
|
||||||
|
while(storageArray.length < len) storageArray.push();
|
||||||
|
|
||||||
for (uint i = 0; i < len; i++)
|
for (uint i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
@ -41,6 +47,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// test_zeroed_indicies(uint256): 1 ->
|
// test_zeroed_indicies(uint256): 1 ->
|
||||||
// test_zeroed_indicies(uint256): 5 ->
|
// test_zeroed_indicies(uint256): 5 ->
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
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 = new uint[](len);
|
storageArray.push();
|
||||||
return storageArray.length;
|
return storageArray.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,7 +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): 0xFFF -> 0xFFF
|
// set_get_length(uint256): 0xFFF -> 0xFFF
|
||||||
// set_get_length(uint256): 0xFFFF -> FAILURE # Out-of-gas #
|
// 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 #
|
Loading…
Reference in New Issue
Block a user