From e07288503134e09a64adb83e4cd6f6ede8b7f356 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 10 Jul 2019 12:41:31 +0200 Subject: [PATCH] [Sol->Yul] Implement delete for fixed-sized storage arrays --- libsolidity/codegen/YulUtilFunctions.cpp | 80 +++++++++++- libsolidity/codegen/YulUtilFunctions.h | 11 +- libsolidity/codegen/ir/IRLValue.cpp | 13 +- test/libsolidity/SolidityEndToEndTest.cpp | 143 ++++++++++++---------- 4 files changed, 176 insertions(+), 71 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 8e54e417d..5f5171b07 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -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 (start, end) { - for {} lt(start, end) { start := add(start, 1) } + for {} lt(start, end) { start := add(start, ) } { - sstore(start, 0) + (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 (slot) { + + (slot, 0) + + (slot, add(slot, ())) + + } + )") + ("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(); }); } @@ -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 (slot, offset) { + (slot, offset, ()) + } + )") + ("functionName", functionName) + ("store", updateStorageValueFunction(_type)) + ("zeroValue", zeroValueFunction(_type)) + .render(); + else if (_type.category() == Type::Category::Array) + return Whiskers(R"( + function (slot, offset) { + (slot) + } + )") + ("functionName", functionName) + ("clearArray", clearStorageArrayFunction(dynamic_cast(_type))) + .render(); + else + solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!"); + }); +} + string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to) { string functionName = diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 04228d5bb..65d4f713c 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -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. diff --git a/libsolidity/codegen/ir/IRLValue.cpp b/libsolidity/codegen/ir/IRLValue.cpp index 1f7aa330f..bf1c08c67 100644 --- a/libsolidity/codegen/ir/IRLValue.cpp +++ b/libsolidity/codegen/ir/IRLValue.cpp @@ -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(m_offset)) : + get(m_offset) + ) + + ")\n"; } IRStorageArrayLength::IRStorageArrayLength(IRGenerationContext& _context, string _slot, Type const& _type, ArrayType const& _arrayType): diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 66a9af271..813946f28 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -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 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)