Index: include/lldb/DataFormatters/ValueObjectPrinter.h =================================================================== --- include/lldb/DataFormatters/ValueObjectPrinter.h +++ include/lldb/DataFormatters/ValueObjectPrinter.h @@ -20,6 +20,7 @@ #include "lldb/lldb-public.h" #include "lldb/Utility/Flags.h" +#include "lldb/Utility/Lazy.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/Symbol/CompilerType.h" @@ -65,21 +66,16 @@ const char *GetRootNameForDisplay(const char *if_fail = nullptr); - bool ShouldPrintValueObject(); + bool ShouldPrintValueObject() { return m_should_print.get(*this); } bool ShouldPrintValidation(); - bool IsNil(); - - bool IsUninitialized(); - - bool IsPtr(); - - bool IsRef(); - - bool IsInstancePointer(); - - bool IsAggregate(); + bool IsNil() { return m_is_nil.get(*this); } + bool IsUninitialized() { return m_is_uninit.get(*this); } + bool IsPtr() { return m_is_ptr.get(*this); } + bool IsRef() { return m_is_ref.get(*this); } + bool IsInstancePointer() { return m_is_instance_ptr.get(*this); } + bool IsAggregate() { return m_is_aggregate.get(*this); } bool PrintValidationMarkerIfNeeded(); @@ -138,13 +134,23 @@ CompilerType m_compiler_type; DumpValueObjectOptions::PointerDepth m_ptr_depth; uint32_t m_curr_depth; - LazyBool m_should_print; - LazyBool m_is_nil; - LazyBool m_is_uninit; - LazyBool m_is_ptr; - LazyBool m_is_ref; - LazyBool m_is_aggregate; - LazyBool m_is_instance_ptr; + + bool CalculateShouldPrint(); + bool CalculateIsNil(); + bool CalculateIsUnit(); + bool CalculateIsPtr(); + bool CalculateIsRef(); + bool CalculateIsAggregate(); + bool CalculateIsInstancePtr(); + + using Class = ValueObjectPrinter; + LazyBoolMember m_should_print; + LazyBoolMember m_is_nil; + LazyBoolMember m_is_uninit; + LazyBoolMember m_is_ptr; + LazyBoolMember m_is_ref; + LazyBoolMember m_is_aggregate; + LazyBoolMember m_is_instance_ptr; std::pair m_summary_formatter; std::string m_value; std::string m_summary; Index: include/lldb/Symbol/CompileUnit.h =================================================================== --- include/lldb/Symbol/CompileUnit.h +++ include/lldb/Symbol/CompileUnit.h @@ -14,6 +14,7 @@ #include "lldb/Core/ModuleChild.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/Function.h" +#include "lldb/Utility/Lazy.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-enumerations.h" @@ -407,7 +408,7 @@ /// optimization. 'false' indicates that either the optimization /// is unknown, or this compile unit was built without optimization. //------------------------------------------------------------------ - bool GetIsOptimized(); + bool GetIsOptimized() { return m_is_optimized.get(*this); } protected: void *m_user_data; ///< User data for the SymbolFile parser to store @@ -430,8 +431,9 @@ m_debug_macros_sp; ///< Debug macros that will get parsed on demand. lldb::VariableListSP m_variables; ///< Global and static variable list that ///will get parsed on demand. - lldb_private::LazyBool m_is_optimized; /// eLazyBoolYes if this compile unit - /// was compiled with optimization. + bool CalculateOptimized(); + /// true if this compile unit was compiled with optimization. + LazyBoolMember m_is_optimized; private: enum { Index: include/lldb/Target/ObjCLanguageRuntime.h =================================================================== --- include/lldb/Target/ObjCLanguageRuntime.h +++ include/lldb/Target/ObjCLanguageRuntime.h @@ -27,6 +27,7 @@ #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/LanguageRuntime.h" +#include "lldb/Utility/Lazy.h" #include "lldb/lldb-private.h" class CommandObjectObjC_ClassTable_Dump; @@ -53,9 +54,7 @@ // implementations of the runtime, and more might come class ClassDescriptor { public: - ClassDescriptor() - : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate), - m_type_wp() {} + ClassDescriptor() = default; virtual ~ClassDescriptor() = default; @@ -67,27 +66,11 @@ // virtual if any implementation has some other version-specific rules but // for the known v1/v2 this is all that needs to be done - virtual bool IsKVO() { - if (m_is_kvo == eLazyBoolCalculate) { - const char *class_name = GetClassName().AsCString(); - if (class_name && *class_name) - m_is_kvo = - (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name); - } - return (m_is_kvo == eLazyBoolYes); - } + virtual bool IsKVO() { return m_is_kvo.get(*this); } // virtual if any implementation has some other version-specific rules but // for the known v1/v2 this is all that needs to be done - virtual bool IsCFType() { - if (m_is_cf == eLazyBoolCalculate) { - const char *class_name = GetClassName().AsCString(); - if (class_name && *class_name) - m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 || - strcmp(class_name, "NSCFType") == 0); - } - return (m_is_cf == eLazyBoolYes); - } + virtual bool IsCFType() { return m_is_cf.get(*this); } virtual bool IsValid() = 0; @@ -139,8 +122,12 @@ bool check_version_specific = false) const; private: - LazyBool m_is_kvo; - LazyBool m_is_cf; + bool CalculateKVO(); + + bool CalculateCFType(); + + LazyBoolMember m_is_kvo; + LazyBoolMember m_is_cf; lldb::TypeWP m_type_wp; }; @@ -280,14 +267,7 @@ } bool HasNewLiteralsAndIndexing() { - if (m_has_new_literals_and_indexing == eLazyBoolCalculate) { - if (CalculateHasNewLiteralsAndIndexing()) - m_has_new_literals_and_indexing = eLazyBoolYes; - else - m_has_new_literals_and_indexing = eLazyBoolNo; - } - - return (m_has_new_literals_and_indexing == eLazyBoolYes); + return m_has_new_literals_and_indexing.get(*this); } virtual void SymbolsDidLoad(const ModuleList &module_list) { @@ -379,7 +359,9 @@ typedef ThreadSafeDenseMap TypeSizeCache; MsgImplMap m_impl_cache; - LazyBool m_has_new_literals_and_indexing; + LazyBoolMember + m_has_new_literals_and_indexing; ISAToDescriptorMap m_isa_to_descriptor; HashToISAMap m_hash_to_isa_map; TypeSizeCache m_type_size_cache; Index: include/lldb/Utility/Lazy.h =================================================================== --- /dev/null +++ include/lldb/Utility/Lazy.h @@ -0,0 +1,56 @@ +//===-- Lazy.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Lazy_h_ +#define liblldb_Lazy_h_ + +#include + +#include "llvm/ADT/Optional.h" + +#include "lldb/lldb-private-enumerations.h" + +namespace lldb_private { + +/// Contains a member variable value that will be lazily calculcated when it's +/// used for the first time. +/// +/// The class takes a value type T that should be stored, and a member function +/// that will be called to calculate the actual value on the first read. +// FIXME: The class/member pointer pair can be replaced by an auto template with +// C++17. +template +class LazyMember { + llvm::Optional Value; + +public: + LazyMember() { reset(); } + + /// Requests that the member should be recalculated on the next read. + void reset() { Value.reset(); } + + /// Reads the value of this LazyMember. Potentially recalculates the value + /// before returning. + const T &get(Class &instance) { + if (!Value) + set((instance.*Calculate)()); + return *Value; + } + + /// Explicitly sets the value of this instance to a certain value, which will + /// also prevent that any future reads recalculate it. + void set(const T &value) { Value = value; } +}; + +/// Convenience struct for LazyMember with a bool. +template +class LazyBoolMember : public LazyMember {}; +} // namespace lldb_private + +#endif // liblldb_Lazy_h_ Index: source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- source/DataFormatters/ValueObjectPrinter.cpp +++ source/DataFormatters/ValueObjectPrinter.cpp @@ -57,13 +57,6 @@ m_curr_depth = curr_depth; assert(m_orig_valobj && "cannot print a NULL ValueObject"); assert(m_stream && "cannot print to a NULL Stream"); - m_should_print = eLazyBoolCalculate; - m_is_nil = eLazyBoolCalculate; - m_is_uninit = eLazyBoolCalculate; - m_is_ptr = eLazyBoolCalculate; - m_is_ref = eLazyBoolCalculate; - m_is_aggregate = eLazyBoolCalculate; - m_is_instance_ptr = eLazyBoolCalculate; m_summary_formatter = {nullptr, false}; m_value.assign(""); m_summary.assign(""); @@ -167,57 +160,35 @@ return root_valobj_name ? root_valobj_name : if_fail; } -bool ValueObjectPrinter::ShouldPrintValueObject() { - if (m_should_print == eLazyBoolCalculate) - m_should_print = - (m_options.m_flat_output == false || m_type_flags.Test(eTypeHasValue)) - ? eLazyBoolYes - : eLazyBoolNo; - return m_should_print == eLazyBoolYes; +bool ValueObjectPrinter::CalculateShouldPrint() { + return m_options.m_flat_output == false || m_type_flags.Test(eTypeHasValue); } -bool ValueObjectPrinter::IsNil() { - if (m_is_nil == eLazyBoolCalculate) - m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo; - return m_is_nil == eLazyBoolYes; -} +bool ValueObjectPrinter::CalculateIsNil() { return m_valobj->IsNilReference(); } -bool ValueObjectPrinter::IsUninitialized() { - if (m_is_uninit == eLazyBoolCalculate) - m_is_uninit = - m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo; - return m_is_uninit == eLazyBoolYes; +bool ValueObjectPrinter::CalculateIsUnit() { + return m_valobj->IsUninitializedReference(); } -bool ValueObjectPrinter::IsPtr() { - if (m_is_ptr == eLazyBoolCalculate) - m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo; - return m_is_ptr == eLazyBoolYes; +bool ValueObjectPrinter::CalculateIsPtr() { + return m_type_flags.Test(eTypeIsPointer); } -bool ValueObjectPrinter::IsRef() { - if (m_is_ref == eLazyBoolCalculate) - m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo; - return m_is_ref == eLazyBoolYes; +bool ValueObjectPrinter::CalculateIsRef() { + return m_type_flags.Test(eTypeIsReference); } -bool ValueObjectPrinter::IsAggregate() { - if (m_is_aggregate == eLazyBoolCalculate) - m_is_aggregate = - m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo; - return m_is_aggregate == eLazyBoolYes; +bool ValueObjectPrinter::CalculateIsAggregate() { + return m_type_flags.Test(eTypeHasChildren); } -bool ValueObjectPrinter::IsInstancePointer() { +bool ValueObjectPrinter::CalculateIsInstancePtr() { // you need to do this check on the value's clang type - if (m_is_instance_ptr == eLazyBoolCalculate) - m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() & - eTypeInstanceIsPointer) != 0 - ? eLazyBoolYes - : eLazyBoolNo; - if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass()) - m_is_instance_ptr = eLazyBoolNo; - return m_is_instance_ptr == eLazyBoolYes; + bool result = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() & + eTypeInstanceIsPointer) != 0; + if (result && m_valobj->IsBaseClass()) + result = false; + return result; } bool ValueObjectPrinter::PrintLocationIfNeeded() { Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -12,6 +12,7 @@ #include "DWARFDIE.h" #include "DWARFDebugInfoEntry.h" +#include "lldb/Utility/Lazy.h" #include "lldb/lldb-enumerations.h" #include "llvm/Support/RWMutex.h" #include @@ -208,7 +209,6 @@ uint32_t m_producer_version_update = 0; lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown; bool m_is_dwarf64 = false; - lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate; dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base // If this is a dwo compile unit this is the offset of the base compile unit @@ -223,6 +223,10 @@ void ExtractDIEsRWLocked(); void ClearDIEsRWLocked(); + bool UpdateIsOptimized(); + lldb_private::LazyBoolMember + m_is_optimized; + // Get the DWARF unit DWARF debug informration entry. Parse the single DIE // if needed. const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() { Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -412,6 +412,14 @@ m_dwo_symbol_file->GetCompileUnit()->ClearDIEsRWLocked(); } +bool DWARFUnit::UpdateIsOptimized() { + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (die) + return die->GetAttributeValueAsUnsigned(m_dwarf, this, + DW_AT_APPLE_optimized, 0) == 1; + return false; +} + void DWARFUnit::BuildAddressRangeTable(SymbolFileDWARF *dwarf, DWARFDebugAranges *debug_aranges) { // This function is usually called if there in no .debug_aranges section in @@ -702,19 +710,7 @@ return m_language_type; } -bool DWARFUnit::GetIsOptimized() { - if (m_is_optimized == eLazyBoolCalculate) { - const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); - if (die) { - m_is_optimized = eLazyBoolNo; - if (die->GetAttributeValueAsUnsigned(m_dwarf, this, DW_AT_APPLE_optimized, - 0) == 1) { - m_is_optimized = eLazyBoolYes; - } - } - } - return m_is_optimized == eLazyBoolYes; -} +bool DWARFUnit::GetIsOptimized() { return m_is_optimized.get(*this); } SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const { return m_dwo_symbol_file.get(); Index: source/Symbol/CompileUnit.cpp =================================================================== --- source/Symbol/CompileUnit.cpp +++ source/Symbol/CompileUnit.cpp @@ -23,8 +23,9 @@ lldb_private::LazyBool is_optimized) : ModuleChild(module_sp), FileSpec(pathname, false), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), - m_support_files(), m_line_table_ap(), m_variables(), - m_is_optimized(is_optimized) { + m_support_files(), m_line_table_ap(), m_variables() { + if (is_optimized != eLazyBoolCalculate) + m_is_optimized.set(is_optimized == eLazyBoolYes); if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); @@ -36,8 +37,9 @@ lldb_private::LazyBool is_optimized) : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), - m_support_files(), m_line_table_ap(), m_variables(), - m_is_optimized(is_optimized) { + m_support_files(), m_line_table_ap(), m_variables() { + if (is_optimized != eLazyBoolCalculate) + m_is_optimized.set(is_optimized == eLazyBoolYes); if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); @@ -382,17 +384,14 @@ return sc_list.GetSize() - prev_size; } -bool CompileUnit::GetIsOptimized() { - if (m_is_optimized == eLazyBoolCalculate) { - m_is_optimized = eLazyBoolNo; - if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) { - SymbolContext sc; - CalculateSymbolContext(&sc); - if (symbol_vendor->ParseCompileUnitIsOptimized(sc)) - m_is_optimized = eLazyBoolYes; - } +bool CompileUnit::CalculateOptimized() { + if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) { + SymbolContext sc; + CalculateSymbolContext(&sc); + if (symbol_vendor->ParseCompileUnitIsOptimized(sc)) + return true; } - return m_is_optimized; + return false; } void CompileUnit::SetVariableList(VariableListSP &variables) { Index: source/Target/ObjCLanguageRuntime.cpp =================================================================== --- source/Target/ObjCLanguageRuntime.cpp +++ source/Target/ObjCLanguageRuntime.cpp @@ -35,7 +35,6 @@ ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process) : LanguageRuntime(process), m_impl_cache(), - m_has_new_literals_and_indexing(eLazyBoolCalculate), m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(), m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(), m_negative_complete_class_cache() {} @@ -154,6 +153,21 @@ return false; } +bool ObjCLanguageRuntime::ClassDescriptor::CalculateKVO() { + const char *class_name = GetClassName().AsCString(); + if (class_name && *class_name) + return strstr(class_name, "NSKVONotifying_") == class_name; + return false; +} + +bool ObjCLanguageRuntime::ClassDescriptor::CalculateCFType() { + const char *class_name = GetClassName().AsCString(); + if (class_name && *class_name) + return strcmp(class_name, "__NSCFType") == 0 || + strcmp(class_name, "NSCFType") == 0; + return false; +} + ObjCLanguageRuntime::ObjCISA ObjCLanguageRuntime::GetISA(const ConstString &name) { ISAToDescriptorIterator pos = GetDescriptorIterator(name); Index: unittests/Utility/CMakeLists.txt =================================================================== --- unittests/Utility/CMakeLists.txt +++ unittests/Utility/CMakeLists.txt @@ -10,6 +10,7 @@ FileSpecTest.cpp FlagsTest.cpp JSONTest.cpp + LazyTest.cpp LogTest.cpp NameMatchesTest.cpp PredicateTest.cpp Index: unittests/Utility/LazyTest.cpp =================================================================== --- /dev/null +++ unittests/Utility/LazyTest.cpp @@ -0,0 +1,72 @@ +//===-- LazyTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Lazy.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +namespace { +class LazyClass { + bool UpdateFoo() { + ++m_updates_called; + return m_foo_value; + } + +public: + LazyBoolMember m_foo; + bool getFoo() { return m_foo.get(*this); } + bool m_foo_value = true; + int m_updates_called = 0; +}; +} // namespace + +TEST(LazyTest, UpdateCount) { + LazyClass l; + EXPECT_EQ(0, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); +} + +TEST(LazyTest, Value) { + { + LazyClass l1; + EXPECT_EQ(l1.m_foo_value, l1.getFoo()); + } + + { + LazyClass l2; + bool old_value = l2.m_foo_value; + l2.m_foo_value = !l2.m_foo_value; + EXPECT_NE(old_value, l2.getFoo()); + } +} + +TEST(LazyTest, Reset) { + LazyClass l; + EXPECT_EQ(0, l.m_updates_called); + l.m_foo.reset(); + EXPECT_EQ(0, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + + l.m_foo.reset(); + EXPECT_EQ(1, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(2, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(2, l.m_updates_called); +}