From 749ec04708059ce70a57840a0322f9e14831c15b Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 09:53:15 -0400 Subject: [PATCH 01/12] Add empty LazyInit.h header --- libsolutil/CMakeLists.txt | 1 + libsolutil/LazyInit.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 libsolutil/LazyInit.h diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 2c6b6b26f..3672657e8 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -19,6 +19,7 @@ set(sources JSON.h Keccak256.cpp Keccak256.h + LazyInit.h picosha2.h Result.h StringUtils.cpp diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h new file mode 100644 index 000000000..39e4786df --- /dev/null +++ b/libsolutil/LazyInit.h @@ -0,0 +1,18 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once From 197f6bf3c895678e53797f2ac4a1c43102e08721 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 10:30:55 -0400 Subject: [PATCH 02/12] Add implementation of LazyInit --- libsolutil/LazyInit.h | 84 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h index 39e4786df..92d28e0c2 100644 --- a/libsolutil/LazyInit.h +++ b/libsolutil/LazyInit.h @@ -16,3 +16,87 @@ */ #pragma once + +#include +#include + +#include +#include +#include +#include + +namespace solidity::util +{ + +DEV_SIMPLE_EXCEPTION(BadLazyInitAccess); + +/** + * Represents a lazy-initialized value. + * @tparam T the type of the lazy-initialized object; may not be a function, reference, array, or void type; may be const-qualified. + */ +template +class LazyInit +{ +public: + using value_type = T; + + static_assert(std::is_object_v, "Function, reference, and void types are not supported"); + static_assert(!std::is_array_v, "Array types are not supported."); + static_assert(!std::is_volatile_v, "Volatile-qualified types are not supported."); + + LazyInit() = default; + + LazyInit(LazyInit const&) = delete; + LazyInit& operator=(LazyInit const&) = delete; + + // Move constructor must be overridden to ensure that moved-from object is left empty. + constexpr LazyInit(LazyInit&& _other) noexcept: + m_value(std::move(_other.m_value)) + { + _other.m_value.reset(); + } + + // Copy and swap idiom. + LazyInit& operator=(LazyInit&& _other) noexcept + { + this->m_value.swap(_other.m_value); + _other.m_value.reset(); + } + + template + value_type& init(F&& _fun) + { + doInit(std::forward(_fun)); + return m_value.value(); + } + + template + value_type const& init(F&& _fun) const + { + doInit(std::forward(_fun)); + return m_value.value(); + } + +private: + constexpr void assertInitialized() const + { + assertThrow( + m_value.has_value(), + BadLazyInitAccess, + "Attempt to access an uninitialized LazyInit" + ); + } + + /// Although not quite logically const, this is marked const for pragmatic reasons. It doesn't change the platonic + /// value of the object (which is something that is initialized to some computed value on first use). + template + void doInit(F&& _fun) const + { + if (!m_value.has_value()) + m_value.emplace(std::forward(_fun)()); + } + + mutable std::optional m_value; +}; + +} From 0e26700f6521db81bfd43d7219b73b0d57965f45 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 13:53:36 -0400 Subject: [PATCH 03/12] Use LazyInit for MemberList::m_storageOffsets --- libsolidity/ast/Types.cpp | 31 ++++++++++++++++++------------- libsolidity/ast/Types.h | 5 ++++- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index aa888d26b..4c9ef4776 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -207,26 +207,31 @@ void MemberList::combine(MemberList const & _other) pair const* MemberList::memberStorageOffset(string const& _name) const { - if (!m_storageOffsets) - { - TypePointers memberTypes; - memberTypes.reserve(m_memberTypes.size()); - for (auto const& member: m_memberTypes) - memberTypes.push_back(member.type); - m_storageOffsets = std::make_unique(); - m_storageOffsets->computeOffsets(memberTypes); - } + StorageOffsets const& offsets = storageOffsets(); + for (size_t index = 0; index < m_memberTypes.size(); ++index) if (m_memberTypes[index].name == _name) - return m_storageOffsets->offset(index); + return offsets.offset(index); return nullptr; } u256 const& MemberList::storageSize() const { - // trigger lazy computation - memberStorageOffset(""); - return m_storageOffsets->storageSize(); + return storageOffsets().storageSize(); +} + +StorageOffsets const& MemberList::storageOffsets() const { + return m_storageOffsets.init([&]{ + TypePointers memberTypes; + memberTypes.reserve(m_memberTypes.size()); + for (auto const& member: m_memberTypes) + memberTypes.push_back(member.type); + + StorageOffsets storageOffsets; + storageOffsets.computeOffsets(memberTypes); + + return storageOffsets; + }); } /// Helper functions for type identifier diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d549d4f24..cdccbc69b 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -139,8 +140,10 @@ public: MemberMap::const_iterator end() const { return m_memberTypes.end(); } private: + StorageOffsets const& storageOffsets() const; + MemberMap m_memberTypes; - mutable std::unique_ptr m_storageOffsets; + util::LazyInit m_storageOffsets; }; static_assert(std::is_nothrow_move_constructible::value, "MemberList should be noexcept move constructible"); From 413fa92ceedec4d676632831e7c3fea71cfb0470 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 13:57:20 -0400 Subject: [PATCH 04/12] Use LazyInit for ContractDefinition::m_interfaceEvents --- libsolidity/ast/AST.cpp | 13 +++++++------ libsolidity/ast/AST.h | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 373a5a284..c81f7d6be 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -153,10 +153,10 @@ FunctionDefinition const* ContractDefinition::receiveFunction() const vector const& ContractDefinition::interfaceEvents() const { - if (!m_interfaceEvents) - { + return m_interfaceEvents.init([&]{ set eventsSeen; - m_interfaceEvents = make_unique>(); + vector interfaceEvents; + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (EventDefinition const* e: contract->events()) { @@ -169,11 +169,12 @@ vector const& ContractDefinition::interfaceEvents() cons if (eventsSeen.count(eventSignature) == 0) { eventsSeen.insert(eventSignature); - m_interfaceEvents->push_back(e); + interfaceEvents.push_back(e); } } - } - return *m_interfaceEvents; + + return interfaceEvents; + }); } vector, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 3c79912c2..c1e90ba6f 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -530,7 +531,7 @@ private: bool m_abstract{false}; mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList[2]; - mutable std::unique_ptr> m_interfaceEvents; + util::LazyInit> m_interfaceEvents; }; class InheritanceSpecifier: public ASTNode From cffd1eaff160e1ed19abf4624795a5cc0f74cb2f Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 13:59:15 -0400 Subject: [PATCH 05/12] Use LazyInit for ContractDefinition::m_interfaceFunctionList --- libsolidity/ast/AST.cpp | 13 +++++++------ libsolidity/ast/AST.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index c81f7d6be..e553e45bd 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -179,10 +179,10 @@ vector const& ContractDefinition::interfaceEvents() cons vector, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const { - if (!m_interfaceFunctionList[_includeInheritedFunctions]) - { + return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{ set signaturesSeen; - m_interfaceFunctionList[_includeInheritedFunctions] = make_unique, FunctionTypePointer>>>(); + vector, FunctionTypePointer>> interfaceFunctionList; + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) { if (_includeInheritedFunctions == false && contract != this) @@ -204,12 +204,13 @@ vector, FunctionTypePointer>> const& ContractDefinition: { signaturesSeen.insert(functionSignature); util::FixedHash<4> hash(util::keccak256(functionSignature)); - m_interfaceFunctionList[_includeInheritedFunctions]->emplace_back(hash, fun); + interfaceFunctionList.emplace_back(hash, fun); } } } - } - return *m_interfaceFunctionList[_includeInheritedFunctions]; + + return interfaceFunctionList; + }); } TypePointer ContractDefinition::type() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index c1e90ba6f..285740f81 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -530,7 +530,7 @@ private: ContractKind m_contractKind; bool m_abstract{false}; - mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList[2]; + util::LazyInit, FunctionTypePointer>>> m_interfaceFunctionList[2]; util::LazyInit> m_interfaceEvents; }; From 24dfa89ee7269b4293ffd8e080e0b9ddb811d196 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Tue, 12 May 2020 10:04:13 -0400 Subject: [PATCH 06/12] Use LazyInit for cached contract compilations --- test/contracts/AuctionRegistrar.cpp | 12 ++++++++---- test/contracts/FixedFeeRegistrar.cpp | 12 ++++++++---- test/contracts/Wallet.cpp | 12 ++++++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index cf15f4ab4..71e47aa0c 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -24,9 +24,12 @@ #include #include +#include + #include #include +#include using namespace std; using namespace solidity; @@ -212,17 +215,18 @@ contract GlobalRegistrar is Registrar, AuctionSystem { } )DELIMITER"; -static unique_ptr s_compiledRegistrar; +static LazyInit s_compiledRegistrar; class AuctionRegistrarTestFramework: public SolidityExecutionFramework { protected: void deployRegistrar() { - if (!s_compiledRegistrar) - s_compiledRegistrar = make_unique(compileContract(registrarCode, "GlobalRegistrar")); + bytes const& compiled = s_compiledRegistrar.init([&]{ + return compileContract(registrarCode, "GlobalRegistrar"); + }); - sendMessage(*s_compiledRegistrar, true); + sendMessage(compiled, true); BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 9535c1907..a2998379f 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -20,8 +20,11 @@ * Tests for a fixed fee registrar contract. */ +#include + #include #include +#include #if defined(_MSC_VER) #pragma warning(push) @@ -122,17 +125,18 @@ contract FixedFeeRegistrar is Registrar { } )DELIMITER"; -static unique_ptr s_compiledRegistrar; +static LazyInit s_compiledRegistrar; class RegistrarTestFramework: public SolidityExecutionFramework { protected: void deployRegistrar() { - if (!s_compiledRegistrar) - s_compiledRegistrar = make_unique(compileContract(registrarCode, "FixedFeeRegistrar")); + bytes const& compiled = s_compiledRegistrar.init([&]{ + return compileContract(registrarCode, "FixedFeeRegistrar"); + }); - sendMessage(*s_compiledRegistrar, true); + sendMessage(compiled, true); BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index b692b7954..46800fa78 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -20,8 +20,11 @@ * Tests for a (comparatively) complex multisig wallet contract. */ +#include + #include #include +#include #if defined(_MSC_VER) #pragma warning(push) @@ -435,7 +438,7 @@ contract Wallet is multisig, multiowned, daylimit { } )DELIMITER"; -static unique_ptr s_compiledWallet; +static LazyInit s_compiledWallet; class WalletTestFramework: public SolidityExecutionFramework { @@ -447,11 +450,12 @@ protected: u256 _dailyLimit = 0 ) { - if (!s_compiledWallet) - s_compiledWallet = make_unique(compileContract(walletCode, "Wallet")); + bytes const& compiled = s_compiledWallet.init([&]{ + return compileContract(walletCode, "Wallet"); + }); bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); - sendMessage(*s_compiledWallet + args, true, _value); + sendMessage(compiled + args, true, _value); BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } From eae31559be3cc83f2338eecc14f2928fa8a10ae6 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 13 May 2020 14:02:40 -0400 Subject: [PATCH 07/12] Use LazyInit for CompilerStack::Contract members --- libsolidity/interface/CompilerStack.cpp | 30 +++++-------------------- libsolidity/interface/CompilerStack.h | 11 ++++----- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index da561050b..76450cf60 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -733,11 +733,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const solAssert(_contract.contract, ""); - // caches the result - if (!_contract.abi) - _contract.abi = make_unique(ABI::generate(*_contract.contract)); - - return *_contract.abi; + return _contract.abi.init([&]{ return ABI::generate(*_contract.contract); }); } Json::Value const& CompilerStack::storageLayout(string const& _contractName) const @@ -755,11 +751,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const solAssert(_contract.contract, ""); - // caches the result - if (!_contract.storageLayout) - _contract.storageLayout = make_unique(StorageLayout().generate(*_contract.contract)); - - return *_contract.storageLayout; + return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract); }); } Json::Value const& CompilerStack::natspecUser(string const& _contractName) const @@ -777,11 +769,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const solAssert(_contract.contract, ""); - // caches the result - if (!_contract.userDocumentation) - _contract.userDocumentation = make_unique(Natspec::userDocumentation(*_contract.contract)); - - return *_contract.userDocumentation; + return _contract.userDocumentation.init([&]{ return Natspec::userDocumentation(*_contract.contract); }); } Json::Value const& CompilerStack::natspecDev(string const& _contractName) const @@ -799,11 +787,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const solAssert(_contract.contract, ""); - // caches the result - if (!_contract.devDocumentation) - _contract.devDocumentation = make_unique(Natspec::devDocumentation(*_contract.contract)); - - return *_contract.devDocumentation; + return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); }); } Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const @@ -832,11 +816,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); - // cache the result - if (!_contract.metadata) - _contract.metadata = make_unique(createMetadata(_contract)); - - return *_contract.metadata; + return _contract.metadata.init([&]{ return createMetadata(_contract); }); } Scanner const& CompilerStack::scanner(string const& _sourceName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 8bc8828c6..e699101af 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -342,11 +343,11 @@ private: std::string yulIROptimized; ///< Optimized experimental Yul IR code. std::string ewasm; ///< Experimental Ewasm text representation evmasm::LinkerObject ewasmObject; ///< Experimental Ewasm code - mutable std::unique_ptr metadata; ///< The metadata json that will be hashed into the chain. - mutable std::unique_ptr abi; - mutable std::unique_ptr storageLayout; - mutable std::unique_ptr userDocumentation; - mutable std::unique_ptr devDocumentation; + util::LazyInit metadata; ///< The metadata json that will be hashed into the chain. + util::LazyInit abi; + util::LazyInit storageLayout; + util::LazyInit userDocumentation; + util::LazyInit devDocumentation; mutable std::unique_ptr sourceMapping; mutable std::unique_ptr runtimeSourceMapping; }; From a392d5d63aa38e49d383855774046a97b0081242 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 14 May 2020 10:42:00 -0400 Subject: [PATCH 08/12] Remove misleading comment from LazyInit --- libsolutil/LazyInit.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h index 92d28e0c2..bc8c512d7 100644 --- a/libsolutil/LazyInit.h +++ b/libsolutil/LazyInit.h @@ -56,7 +56,6 @@ public: _other.m_value.reset(); } - // Copy and swap idiom. LazyInit& operator=(LazyInit&& _other) noexcept { this->m_value.swap(_other.m_value); From 9d8ee5cc8f0d1dc1421e2c52368628651de6b5f9 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 14 May 2020 10:42:19 -0400 Subject: [PATCH 09/12] Remove unused assertInitialized from LazyInit --- libsolutil/LazyInit.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h index bc8c512d7..aec126a65 100644 --- a/libsolutil/LazyInit.h +++ b/libsolutil/LazyInit.h @@ -77,15 +77,6 @@ public: } private: - constexpr void assertInitialized() const - { - assertThrow( - m_value.has_value(), - BadLazyInitAccess, - "Attempt to access an uninitialized LazyInit" - ); - } - /// Although not quite logically const, this is marked const for pragmatic reasons. It doesn't change the platonic /// value of the object (which is something that is initialized to some computed value on first use). template From 648bb3aac77323090bbaa1458b778dea72ac96a2 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 14 May 2020 10:45:18 -0400 Subject: [PATCH 10/12] Update documentation of LazyInit --- libsolutil/LazyInit.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h index aec126a65..fe2f6a916 100644 --- a/libsolutil/LazyInit.h +++ b/libsolutil/LazyInit.h @@ -31,8 +31,10 @@ namespace solidity::util DEV_SIMPLE_EXCEPTION(BadLazyInitAccess); /** - * Represents a lazy-initialized value. - * @tparam T the type of the lazy-initialized object; may not be a function, reference, array, or void type; may be const-qualified. + * A value that is initialized at some point after construction of the LazyInit. The stored value can only be accessed + * while calling "init", which initializes the stored value (if it has not already been initialized). + * + * @tparam T the type of the stored value; may not be a function, reference, array, or void type; may be const-qualified. */ template class LazyInit From 26406e40d0b1cb42793dc22d57bdf89ff345f17e Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 14 May 2020 10:48:50 -0400 Subject: [PATCH 11/12] Add empty LazyInit test suite --- test/CMakeLists.txt | 1 + test/libsolutil/LazyInit.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 test/libsolutil/LazyInit.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff545faf3..1834d3b30 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -34,6 +34,7 @@ set(libsolutil_sources libsolutil/IterateReplacing.cpp libsolutil/JSON.cpp libsolutil/Keccak256.cpp + libsolutil/LazyInit.cpp libsolutil/StringUtils.cpp libsolutil/SwarmHash.cpp libsolutil/UTF8.cpp diff --git a/test/libsolutil/LazyInit.cpp b/test/libsolutil/LazyInit.cpp new file mode 100644 index 000000000..22ee9d541 --- /dev/null +++ b/test/libsolutil/LazyInit.cpp @@ -0,0 +1,28 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +namespace solidity::util::test +{ + +BOOST_AUTO_TEST_SUITE(LazyInit) + + +BOOST_AUTO_TEST_SUITE_END() + +} From a093962115cb9adf696def48da6b5f6772a64cfc Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 14 May 2020 11:27:56 -0400 Subject: [PATCH 12/12] Add tests --- test/libsolutil/LazyInit.cpp | 92 +++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/test/libsolutil/LazyInit.cpp b/test/libsolutil/LazyInit.cpp index 22ee9d541..346805c82 100644 --- a/test/libsolutil/LazyInit.cpp +++ b/test/libsolutil/LazyInit.cpp @@ -15,13 +15,103 @@ along with solidity. If not, see . */ +#include + #include +#include + namespace solidity::util::test { -BOOST_AUTO_TEST_SUITE(LazyInit) +namespace +{ +template +void assertInitCalled(LazyInit lazyInit, bool target) +{ + bool initCalled = false; + + lazyInit.init([&]{ + initCalled = true; + return T(); + }); + + BOOST_REQUIRE_EQUAL(initCalled, target); +} + +// Take ownership to ensure that it doesn't "mutate" +template +void assertNotEmpty(LazyInit _lazyInit) { assertInitCalled(std::move(_lazyInit), false); } + +// Take ownership to ensure that it doesn't "mutate" +template +void assertEmpty(LazyInit _lazyInit) { assertInitCalled(std::move(_lazyInit), true); } + +template +T valueOf(LazyInit _lazyInit) +{ + return _lazyInit.init([&]{ + BOOST_REQUIRE(false); // this should never be called + return T(); + }); +} + +} + +BOOST_AUTO_TEST_SUITE(LazyInitTests) + +BOOST_AUTO_TEST_CASE(default_constructed_is_empty) +{ + assertEmpty(LazyInit()); + assertEmpty(LazyInit()); +} + +BOOST_AUTO_TEST_CASE(initialized_is_not_empty) +{ + LazyInit lazyInit; + lazyInit.init([]{ return 12; }); + + assertNotEmpty(std::move(lazyInit)); +} + +BOOST_AUTO_TEST_CASE(init_returns_init_value) +{ + LazyInit lazyInit; + + BOOST_CHECK_EQUAL(lazyInit.init([]{ return 12; }), 12); + + // A second call to init should not change the value + BOOST_CHECK_EQUAL(lazyInit.init([]{ return 42; }), 12); +} + +BOOST_AUTO_TEST_CASE(moved_from_is_empty) +{ + { + LazyInit lazyInit; + { [[maybe_unused]] auto pilfered = std::move(lazyInit); } + + assertEmpty(std::move(lazyInit)); + } + + { + LazyInit lazyInit; + lazyInit.init([]{ return 12; }); + + { [[maybe_unused]] auto pilfered = std::move(lazyInit); } + + assertEmpty(std::move(lazyInit)); + } +} + +BOOST_AUTO_TEST_CASE(move_constructed_has_same_value_as_original) +{ + LazyInit original; + original.init([]{ return 12; }); + + LazyInit moveConstructed = std::move(original); + BOOST_CHECK_EQUAL(valueOf(std::move(moveConstructed)), 12); +} BOOST_AUTO_TEST_SUITE_END()