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:
committed by
Erik Kundt
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 functionName = "clear_storage_range_" + _type.identifier();
|
||||
|
||||
@@ -121,6 +121,14 @@ public:
|
||||
/// signature: (array, newLen)
|
||||
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
|
||||
/// by the start and end (exclusive) parameters (slots).
|
||||
/// signature: (start, end)
|
||||
|
||||
@@ -640,6 +640,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
array <<
|
||||
"))\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;
|
||||
}
|
||||
default:
|
||||
@@ -788,31 +816,45 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
||||
|
||||
solAssert(member == "length", "");
|
||||
|
||||
if (!type.isDynamicallySized())
|
||||
defineExpression(_memberAccess) << type.length() << "\n";
|
||||
if (member == "length")
|
||||
{
|
||||
if (!type.isDynamicallySized())
|
||||
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
|
||||
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;
|
||||
}
|
||||
solAssert(false, "Invalid array member access.");
|
||||
|
||||
break;
|
||||
}
|
||||
case Type::Category::FixedBytes:
|
||||
|
||||
Reference in New Issue
Block a user