Merge pull request #7077 from ethereum/sol-yul-delete-fixed-storage

[Sol->Yul] Implement delete for fixed-sized storage arrays
This commit is contained in:
chriseth 2019-08-08 16:47:16 +02:00 committed by GitHub
commit 9955c51769
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.isDynamicallySized(), "");
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
solUnimplementedAssert(_type.baseType()->isValueType(), "...");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
solUnimplementedAssert(_type.baseType()->storageSize() == 1, "");
@ -598,18 +597,62 @@ string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
{
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 Whiskers(R"(
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)
("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();
});
}
@ -1631,6 +1674,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 functionName =

View File

@ -122,10 +122,14 @@ public:
std::string resizeDynamicArrayFunction(ArrayType const& _type);
/// @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)
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
/// size in memory (number of storage slots or calldata/memory bytes) it
/// will require.
@ -262,6 +266,11 @@ public:
/// @returns the name of a function that returns the zero value for the
/// provided 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:
/// Special case of conversionFunction - handles everything that does not
/// 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
{
solUnimplementedAssert(m_type->isValueType(), "");
return storeValue(m_context.utils().zeroValueFunction(*m_type) + "()", *m_type);
return
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):

View File

@ -3139,14 +3139,16 @@ BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
}
}
)";
compileAndRun(sourceCode);
u256 x(42);
callContractFunction("createEvent(uint256)", x);
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
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_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
ALSO_VIA_YUL(
compileAndRun(sourceCode);
u256 x(42);
callContractFunction("createEvent(uint256)", x);
BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
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_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
);
}
BOOST_AUTO_TEST_CASE(event_indexed_string)
@ -4384,12 +4386,14 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
function clear() public { delete data; }
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
ALSO_VIA_YUL(
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
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; }
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
ALSO_VIA_YUL(
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
@ -4427,14 +4433,16 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
function fullClear() public { delete dynamic; }
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("halfClear()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fullClear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
ALSO_VIA_YUL(
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("halfClear()"), bytes());
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fullClear()"), bytes());
BOOST_CHECK(storageEmpty(m_contractAddress));
);
}
BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
@ -6651,17 +6659,19 @@ BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access)
}
}
)";
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(
"set(uint24[3][],uint256,uint256)",
u256(0x60),
u256(3),
u256(2),
u256(data.size() / 3),
data
) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
ALSO_VIA_YUL(
compileAndRun(sourceCode, 0, "Test");
BOOST_REQUIRE(callContractFunction(
"set(uint24[3][],uint256,uint256)",
u256(0x60),
u256(3),
u256(2),
u256(data.size() / 3),
data
) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2])));
);
}
BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
@ -6674,16 +6684,19 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
}
}
)";
compileAndRun(sourceCode, 0, "Test");
string data("abcdefgh");
BOOST_REQUIRE(callContractFunction(
"set(bytes,uint256)",
u256(0x40),
u256(3),
u256(data.size()),
data
) == encodeArgs(u256(data.size()), string("d")));
ALSO_VIA_YUL(
compileAndRun(sourceCode, 0, "Test");
BOOST_REQUIRE(callContractFunction(
"set(bytes,uint256)",
u256(0x40),
u256(3),
u256(data.size()),
data
) == encodeArgs(u256(data.size()), string("d")));
);
}
BOOST_AUTO_TEST_CASE(storage_array_ref)
@ -6720,23 +6733,25 @@ BOOST_AUTO_TEST_CASE(storage_array_ref)
}
}
)";
compileAndRun(sourceCode, 0, "Store");
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs());
ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3)));
ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5)));
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(400)), encodeArgs(u256(-1)));
ALSO_VIA_YUL(
compileAndRun(sourceCode, 0, "Store");
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1)));
BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs());
BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs());
ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs());
ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3)));
ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5)));
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(400)), encodeArgs(u256(-1)));
);
}
BOOST_AUTO_TEST_CASE(memory_types_initialisation)