[Sol->Yul] Implement delete for fixed-sized storage arrays

This commit is contained in:
Mathias Baumann 2019-07-10 12:41:31 +02:00
parent 146993409a
commit e072885031
4 changed files with 176 additions and 71 deletions

View File

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

View File

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

View File

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

View File

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