mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Implement delete for fixed-sized storage arrays
This commit is contained in:
parent
146993409a
commit
e072885031
@ -557,7 +557,6 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
||||||
solUnimplementedAssert(_type.baseType()->isValueType(), "...");
|
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
||||||
solUnimplementedAssert(_type.baseType()->storageSize() == 1, "");
|
solUnimplementedAssert(_type.baseType()->storageSize() == 1, "");
|
||||||
|
|
||||||
@ -598,18 +597,62 @@ string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
|
|||||||
{
|
{
|
||||||
string functionName = "clear_storage_range_" + _type.identifier();
|
string functionName = "clear_storage_range_" + _type.identifier();
|
||||||
|
|
||||||
solUnimplementedAssert(_type.isValueType(), "...");
|
solAssert(_type.storageBytes() >= 32, "Expected smaller value for storage bytes");
|
||||||
|
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(start, end) {
|
function <functionName>(start, end) {
|
||||||
for {} lt(start, end) { start := add(start, 1) }
|
for {} lt(start, end) { start := add(start, <increment>) }
|
||||||
{
|
{
|
||||||
sstore(start, 0)
|
<setToZero>(start, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
|
("setToZero", storageSetToZeroFunction(_type))
|
||||||
|
("increment", _type.storageSize().str())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
|
||||||
|
{
|
||||||
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
|
if (_type.baseType()->storageBytes() < 32)
|
||||||
|
{
|
||||||
|
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_type.baseType()->isValueType())
|
||||||
|
solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
|
||||||
|
|
||||||
|
string functionName = "clear_storage_array_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) {
|
||||||
|
<?dynamic>
|
||||||
|
<resizeArray>(slot, 0)
|
||||||
|
<!dynamic>
|
||||||
|
<clearRange>(slot, add(slot, <lenToSize>(<len>)))
|
||||||
|
</dynamic>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("dynamic", _type.isDynamicallySized())
|
||||||
|
("resizeArray", _type.isDynamicallySized() ? resizeDynamicArrayFunction(_type) : "")
|
||||||
|
(
|
||||||
|
"clearRange",
|
||||||
|
clearStorageRangeFunction(
|
||||||
|
(_type.baseType()->storageBytes() < 32) ?
|
||||||
|
*TypeProvider::uint256() :
|
||||||
|
*_type.baseType()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
("lenToSize", arrayConvertLengthToSize(_type))
|
||||||
|
("len", _type.length().str())
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1634,6 +1677,35 @@ string YulUtilFunctions::zeroValueFunction(Type const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
string const functionName = "storage_set_to_zero_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
if (_type.isValueType())
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, offset) {
|
||||||
|
<store>(slot, offset, <zeroValue>())
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("store", updateStorageValueFunction(_type))
|
||||||
|
("zeroValue", zeroValueFunction(_type))
|
||||||
|
.render();
|
||||||
|
else if (_type.category() == Type::Category::Array)
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, offset) {
|
||||||
|
<clearArray>(slot)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
||||||
|
.render();
|
||||||
|
else
|
||||||
|
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
|
string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
|
||||||
{
|
{
|
||||||
string functionName =
|
string functionName =
|
||||||
|
@ -122,10 +122,14 @@ public:
|
|||||||
std::string resizeDynamicArrayFunction(ArrayType const& _type);
|
std::string resizeDynamicArrayFunction(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). Only works for value types.
|
/// by the start and end (exclusive) parameters (slots).
|
||||||
/// signature: (start, end)
|
/// signature: (start, end)
|
||||||
std::string clearStorageRangeFunction(Type const& _type);
|
std::string clearStorageRangeFunction(Type const& _type);
|
||||||
|
|
||||||
|
/// @returns the name of a function that will clear the given storage array
|
||||||
|
/// signature: (slot) ->
|
||||||
|
std::string clearStorageArrayFunction(ArrayType const& _type);
|
||||||
|
|
||||||
/// Returns the name of a function that will convert a given length to the
|
/// Returns the name of a function that will convert a given length to the
|
||||||
/// size in memory (number of storage slots or calldata/memory bytes) it
|
/// size in memory (number of storage slots or calldata/memory bytes) it
|
||||||
/// will require.
|
/// will require.
|
||||||
@ -262,6 +266,11 @@ public:
|
|||||||
/// @returns the name of a function that returns the zero value for the
|
/// @returns the name of a function that returns the zero value for the
|
||||||
/// provided type
|
/// provided type
|
||||||
std::string zeroValueFunction(Type const& _type);
|
std::string zeroValueFunction(Type const& _type);
|
||||||
|
|
||||||
|
/// @returns the name of a function that will set the given storage item to
|
||||||
|
/// zero
|
||||||
|
/// signature: (slot, offset) ->
|
||||||
|
std::string storageSetToZeroFunction(Type const& _type);
|
||||||
private:
|
private:
|
||||||
/// Special case of conversionFunction - handles everything that does not
|
/// Special case of conversionFunction - handles everything that does not
|
||||||
/// use exactly one variable to hold the value.
|
/// use exactly one variable to hold the value.
|
||||||
|
@ -135,8 +135,17 @@ string IRStorageItem::storeValue(string const& _value, Type const& _sourceType)
|
|||||||
|
|
||||||
string IRStorageItem::setToZero() const
|
string IRStorageItem::setToZero() const
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(m_type->isValueType(), "");
|
return
|
||||||
return storeValue(m_context.utils().zeroValueFunction(*m_type) + "()", *m_type);
|
m_context.utils().storageSetToZeroFunction(*m_type) +
|
||||||
|
"(" +
|
||||||
|
m_slot +
|
||||||
|
", " +
|
||||||
|
(
|
||||||
|
m_offset.type() == typeid(unsigned) ?
|
||||||
|
to_string(get<unsigned>(m_offset)) :
|
||||||
|
get<string>(m_offset)
|
||||||
|
) +
|
||||||
|
")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
IRStorageArrayLength::IRStorageArrayLength(IRGenerationContext& _context, string _slot, Type const& _type, ArrayType const& _arrayType):
|
IRStorageArrayLength::IRStorageArrayLength(IRGenerationContext& _context, string _slot, Type const& _type, ArrayType const& _arrayType):
|
||||||
|
@ -3139,6 +3139,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
ALSO_VIA_YUL(
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
u256 x(42);
|
u256 x(42);
|
||||||
callContractFunction("createEvent(uint256)", x);
|
callContractFunction("createEvent(uint256)", x);
|
||||||
@ -3147,6 +3148,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
|
|||||||
BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
|
BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
|
||||||
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
|
BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
|
||||||
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
|
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(event_indexed_string)
|
BOOST_AUTO_TEST_CASE(event_indexed_string)
|
||||||
@ -4384,12 +4386,14 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
|
|||||||
function clear() public { delete data; }
|
function clear() public { delete data; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
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());
|
||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
ABI_CHECK(callContractFunction("clear()"), bytes());
|
ABI_CHECK(callContractFunction("clear()"), bytes());
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
|
BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
|
||||||
@ -4405,12 +4409,14 @@ BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
|
|||||||
function clear() public { delete data; }
|
function clear() public { delete data; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
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());
|
||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
ABI_CHECK(callContractFunction("clear()"), bytes());
|
ABI_CHECK(callContractFunction("clear()"), bytes());
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
|
BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
|
||||||
@ -4427,6 +4433,7 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
|
|||||||
function fullClear() public { delete dynamic; }
|
function fullClear() public { delete dynamic; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
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());
|
||||||
@ -4435,6 +4442,7 @@ 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)
|
||||||
@ -6651,9 +6659,10 @@ BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
|
||||||
|
ALSO_VIA_YUL(
|
||||||
compileAndRun(sourceCode, 0, "Test");
|
compileAndRun(sourceCode, 0, "Test");
|
||||||
|
|
||||||
vector<u256> data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
|
|
||||||
BOOST_REQUIRE(callContractFunction(
|
BOOST_REQUIRE(callContractFunction(
|
||||||
"set(uint24[3][],uint256,uint256)",
|
"set(uint24[3][],uint256,uint256)",
|
||||||
u256(0x60),
|
u256(0x60),
|
||||||
@ -6662,6 +6671,7 @@ BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access)
|
|||||||
u256(data.size() / 3),
|
u256(data.size() / 3),
|
||||||
data
|
data
|
||||||
) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
|
) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
|
BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
|
||||||
@ -6674,9 +6684,11 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
string data("abcdefgh");
|
||||||
|
|
||||||
|
ALSO_VIA_YUL(
|
||||||
compileAndRun(sourceCode, 0, "Test");
|
compileAndRun(sourceCode, 0, "Test");
|
||||||
|
|
||||||
string data("abcdefgh");
|
|
||||||
BOOST_REQUIRE(callContractFunction(
|
BOOST_REQUIRE(callContractFunction(
|
||||||
"set(bytes,uint256)",
|
"set(bytes,uint256)",
|
||||||
u256(0x40),
|
u256(0x40),
|
||||||
@ -6684,6 +6696,7 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
|
|||||||
u256(data.size()),
|
u256(data.size()),
|
||||||
data
|
data
|
||||||
) == encodeArgs(u256(data.size()), string("d")));
|
) == encodeArgs(u256(data.size()), string("d")));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(storage_array_ref)
|
BOOST_AUTO_TEST_CASE(storage_array_ref)
|
||||||
@ -6720,6 +6733,7 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
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());
|
||||||
@ -6737,6 +6751,7 @@ 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)
|
||||||
|
Loading…
Reference in New Issue
Block a user