From 1a1fb79c09a9e13b6409a0d0dffde72cc5f586e0 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 15 Mar 2022 22:54:28 -0500 Subject: [PATCH 01/19] rootpcm: Read enums before class to get the correct underlying type. --- core/metacling/src/TCling.cxx | 90 +++++++++++++++++------------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 79bce08995332..f75efddc3224d 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -1712,51 +1712,6 @@ void TCling::LoadPCMImpl(TFile &pcmFile) if (gDebug > 1) ::Info("TCling::LoadPCMImpl", "reading protoclasses for %s \n", pcmFile.GetName()); - pcmFile.GetObject("__ProtoClasses", protoClasses); - - if (protoClasses) { - for (auto obj : *protoClasses) { - TProtoClass *proto = (TProtoClass *)obj; - TClassTable::Add(proto); - } - // Now that all TClass-es know how to set them up we can update - // existing TClasses, which might cause the creation of e.g. TBaseClass - // objects which in turn requires the creation of TClasses, that could - // come from the PCH, but maybe later in the loop. Instead of resolving - // a dependency graph the addition to the TClassTable above allows us - // to create these dependent TClasses as needed below. - for (auto proto : *protoClasses) { - if (TClass *existingCl = (TClass *)gROOT->GetListOfClasses()->FindObject(proto->GetName())) { - // We have an existing TClass object. It might be emulated - // or interpreted; we now have more information available. - // Make that available. - if (existingCl->GetState() != TClass::kHasTClassInit) { - DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName()); - if (!dict) { - ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s", proto->GetName()); - } else { - // This will replace the existing TClass. - TClass *ncl = (*dict)(); - if (ncl) - ncl->PostLoadCheck(); - } - } - } - } - - protoClasses->Clear(); // Ownership was transfered to TClassTable. - delete protoClasses; - } - - TObjArray *dataTypes; - pcmFile.GetObject("__Typedefs", dataTypes); - if (dataTypes) { - for (auto typedf : *dataTypes) - gROOT->GetListOfTypes()->Add(typedf); - dataTypes->Clear(); // Ownership was transfered to TListOfTypes. - delete dataTypes; - } - TObjArray *enums; pcmFile.GetObject("__Enums", enums); if (enums) { @@ -1806,6 +1761,51 @@ void TCling::LoadPCMImpl(TFile &pcmFile) enums->Clear(); delete enums; } + + pcmFile.GetObject("__ProtoClasses", protoClasses); + + if (protoClasses) { + for (auto obj : *protoClasses) { + TProtoClass *proto = (TProtoClass *)obj; + TClassTable::Add(proto); + } + // Now that all TClass-es know how to set them up we can update + // existing TClasses, which might cause the creation of e.g. TBaseClass + // objects which in turn requires the creation of TClasses, that could + // come from the PCH, but maybe later in the loop. Instead of resolving + // a dependency graph the addition to the TClassTable above allows us + // to create these dependent TClasses as needed below. + for (auto proto : *protoClasses) { + if (TClass *existingCl = (TClass *)gROOT->GetListOfClasses()->FindObject(proto->GetName())) { + // We have an existing TClass object. It might be emulated + // or interpreted; we now have more information available. + // Make that available. + if (existingCl->GetState() != TClass::kHasTClassInit) { + DictFuncPtr_t dict = gClassTable->GetDict(proto->GetName()); + if (!dict) { + ::Error("TCling::LoadPCM", "Inconsistent TClassTable for %s", proto->GetName()); + } else { + // This will replace the existing TClass. + TClass *ncl = (*dict)(); + if (ncl) + ncl->PostLoadCheck(); + } + } + } + } + + protoClasses->Clear(); // Ownership was transfered to TClassTable. + delete protoClasses; + } + + TObjArray *dataTypes; + pcmFile.GetObject("__Typedefs", dataTypes); + if (dataTypes) { + for (auto typedf : *dataTypes) + gROOT->GetListOfTypes()->Add(typedf); + dataTypes->Clear(); // Ownership was transfered to TListOfTypes. + delete dataTypes; + } } //////////////////////////////////////////////////////////////////////////////// From fe0b4ce350a3716f25718c4721172ba15547b7cd Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 23 Mar 2022 20:03:50 -0500 Subject: [PATCH 02/19] When loading a TClass for map, also update the pair's TClass. This is done also for multimap. --- core/meta/src/TClass.cxx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 9680f9947c1de..aa13490241b75 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -5995,6 +5995,26 @@ void TClass::PostLoadCheck() } } } + if (fCollectionProxy) { + // Update the related pair's TClass if it has already been created. + size_t noffset = 0; + if (strncmp(GetName(), "map<", 4) == 0) + noffset = 3; + else if (strncmp(GetName(), "multimap<", 9) == 0) + noffset = 8; + if (noffset) { + std::string pairname("pair"); + pairname.append(GetName() + noffset); + if ( auto pcl = TClass::GetClass(pairname.c_str(), false, false) ) + { + TIter next(pcl->GetStreamerInfos()); + while (auto info = (TVirtualStreamerInfo*)next()) { + info->Clear("build"); + } + fCollectionProxy->GetValueClass(); + } + } + } } //////////////////////////////////////////////////////////////////////////////// From bed6557e80cd99161c6b27b421ea436f1bb4cc20 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 23 Mar 2022 20:04:19 -0500 Subject: [PATCH 03/19] TStreamerInfo::Clear, also reset TStreamerElement's offset --- io/io/src/TStreamerInfo.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 4cbfbf61b0722..60351bb9140e8 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -2615,6 +2615,11 @@ void TStreamerInfo::Clear(Option_t *option) ResetIsCompiled(); ResetBit(kBuildOldUsed); + TIter next(fElements); + while (auto element = (TStreamerElement*)next()) { + element->SetOffset(0); + } + if (fReadObjectWise) fReadObjectWise->fActions.clear(); if (fReadMemberWise) fReadMemberWise->fActions.clear(); if (fReadMemberWiseVecPtr) fReadMemberWiseVecPtr->fActions.clear(); From 8a79a4a2cecd6710b32c98a8c0df652398c2e3ea Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 23 Mar 2022 20:06:02 -0500 Subject: [PATCH 04/19] rootcling: record Enum used by classes and collection. When encountering a data member of a class that is a enum or if it is use in a map or multimap, record the enum in the rootpcm. --- io/rootpcm/src/rootclingIO.cxx | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/io/rootpcm/src/rootclingIO.cxx b/io/rootpcm/src/rootclingIO.cxx index f9cf38a829c83..4be9805fec7d1 100644 --- a/io/rootpcm/src/rootclingIO.cxx +++ b/io/rootpcm/src/rootclingIO.cxx @@ -169,6 +169,15 @@ bool CloseStreamerInfoROOTFile(bool writeEmptyRootPCM) auto dm = (TDataMember *) dmObj; if (!dm->IsPersistent() || cl->GetClassVersion()==0) continue; if (IsUnsupportedUniquePointer(normName.c_str(), dm)) return false; + if (dm->IsEnum()) { + const char *enumTypeName = dm->GetTypeName(); + auto enumType = TEnum::GetEnum(enumTypeName); + if (enumType && (!enumType->GetClass() || !enumType->GetClass()->IsLoaded()) && + ((strstr(enumTypeName, "(anonymous)") == nullptr) && + std::find(gEnumsToStore.begin(), gEnumsToStore.end(), enumTypeName) == gEnumsToStore.end())) + gEnumsToStore.emplace_back(enumTypeName); + } + } // Never store a proto class for a class which rootcling already has @@ -189,6 +198,26 @@ bool CloseStreamerInfoROOTFile(bool writeEmptyRootPCM) // continue; cl->Property(); // Force initialization of the bits and property fields. + if (auto pr = cl->GetCollectionProxy()) { + auto colltype = pr->GetCollectionType(); + if (colltype == ROOT::kSTLmap || colltype == ROOT::kSTLmultimap) { + if (auto pcl = pr->GetValueClass()) { + if (auto pcl_dms = pcl->GetListOfDataMembers()) { + for (auto dmObj : *pcl_dms) { + auto dm = (TDataMember *) dmObj; + if (dm->IsEnum()) { + const char *enumTypeName = dm->GetTypeName(); + auto enumType = TEnum::GetEnum(enumTypeName); + if (enumType && (!enumType->GetClass() || !enumType->GetClass()->IsLoaded()) && + (std::find(gEnumsToStore.begin(), gEnumsToStore.end(), enumTypeName) == gEnumsToStore.end())) + gEnumsToStore.emplace_back(enumTypeName); + } + } + } + } + } + } + protoClasses.AddLast(new TProtoClass(cl)); } @@ -242,10 +271,10 @@ bool CloseStreamerInfoROOTFile(bool writeEmptyRootPCM) if (dictFile.IsZombie()) return false; // Instead of plugins: - protoClasses.Write("__ProtoClasses", TObject::kSingleKey); - protoClasses.Delete(); typedefs.Write("__Typedefs", TObject::kSingleKey); enums.Write("__Enums", TObject::kSingleKey); + protoClasses.Write("__ProtoClasses", TObject::kSingleKey); + protoClasses.Delete(); return true; } From 3c5fa3d3b6186553d4e14b1c21f2d6ae029cad33 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 24 Mar 2022 16:46:52 -0500 Subject: [PATCH 05/19] io: Correct offset passed from SyntheticPair to StreamerInfo. This was a fatal typo in 96784051cc6 pair: properly update StreamerInfo. --- io/io/src/TStreamerInfo.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 60351bb9140e8..b91e34196f026 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -2136,8 +2136,9 @@ void TStreamerInfo::BuildOld() if (pattern && pattern != this && pattern->IsBuilt()) { int pair_element_offset = kMissing; pattern->GetStreamerElement(element->GetName(), pair_element_offset); - if (offset != kMissing) - element->SetOffset(offset); + if (pair_element_offset != kMissing) { + element->SetOffset(pair_element_offset); + } } } } From 01e58c7a99e0f06117a0dd69f897c23f20cadc45 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 24 Mar 2022 21:28:08 -0500 Subject: [PATCH 06/19] Add missing TListOfEnums::FindObject. We already had TListOfEnumsWithLock but TListOfEnums::FindObject is used by rootcling --- core/meta/inc/TListOfEnums.h | 3 +++ core/meta/src/TListOfEnums.cxx | 16 ++++++++++++++++ core/meta/src/TListOfEnumsWithLock.cxx | 11 +---------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/core/meta/inc/TListOfEnums.h b/core/meta/inc/TListOfEnums.h index 63c688ce4d48d..17ebeba860448 100644 --- a/core/meta/inc/TListOfEnums.h +++ b/core/meta/inc/TListOfEnums.h @@ -71,6 +71,9 @@ class TListOfEnums : public THashList TEnum *Find(DeclId_t id) const; virtual TEnum *GetObject(const char*) const; + TObject *FindObject(const char*) const override; + using THashList::FindObject; + void Clear(Option_t *option) override; void Delete(Option_t *option="") override; diff --git a/core/meta/src/TListOfEnums.cxx b/core/meta/src/TListOfEnums.cxx index d06ad74e28e05..51aeabca6ec4a 100644 --- a/core/meta/src/TListOfEnums.cxx +++ b/core/meta/src/TListOfEnums.cxx @@ -183,6 +183,22 @@ TEnum *TListOfEnums::Find(DeclId_t id) const return (TEnum *)fIds->GetValue((Long64_t)id); } +//////////////////////////////////////////////////////////////////////////////// +/// Specialize FindObject to do search for the +/// a enum just by name or create it if its not already in the list + +TObject *TListOfEnums::FindObject(const char *name) const +{ + TObject *result = THashList::FindObject(name); + if (!result) { + TInterpreter::DeclId_t decl; + if (GetClass()) decl = gInterpreter->GetEnum(GetClass(), name); + else decl = gInterpreter->GetEnum(nullptr, name); + if (decl) result = const_cast(this)->Get(decl, name); + } + return result; +} + //////////////////////////////////////////////////////////////////////////////// /// Return (after creating it if necessary) the TEnum /// describing the enum corresponding to the Decl 'id'. diff --git a/core/meta/src/TListOfEnumsWithLock.cxx b/core/meta/src/TListOfEnumsWithLock.cxx index dadefa98c947b..761481fc73c80 100644 --- a/core/meta/src/TListOfEnumsWithLock.cxx +++ b/core/meta/src/TListOfEnumsWithLock.cxx @@ -156,16 +156,7 @@ void TListOfEnumsWithLock::Delete(Option_t *option /* ="" */) TObject *TListOfEnumsWithLock::FindObject(const char *name) const { R__LOCKGUARD(gInterpreterMutex); - TObject *result = TListOfEnums::FindObject(name); - if (!result) { - - - TInterpreter::DeclId_t decl; - if (GetClass()) decl = gInterpreter->GetEnum(GetClass(), name); - else decl = gInterpreter->GetEnum(0, name); - if (decl) result = const_cast(this)->Get(decl, name); - } - return result; + return TListOfEnums::FindObject(name); } From f97246334d1b9978d0c393df1add35e5a02f569d Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 24 Mar 2022 21:29:56 -0500 Subject: [PATCH 07/19] TGenCollectionProxy: add missing nullptr protection --- io/io/src/TGenCollectionProxy.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index 31e00a5cc626c..daec43a9ed4c8 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -896,7 +896,7 @@ TGenCollectionProxy *TGenCollectionProxy::InitializeEx(Bool_t silent) else { gROOT->GetListOfClasses()->Remove(paircl); TClass *newpaircl = TClass::GetClass(nam.c_str(), true, false, fValOffset, fValDiff); - if (newpaircl == paircl || newpaircl->GetClassSize() != fValDiff) + if (!newpaircl || newpaircl == paircl || newpaircl->GetClassSize() != fValDiff) Fatal("InitializeEx", "The TClass creation for %s did not get the right size: %d instead of%d\n", nam.c_str(), (int)paircl->GetClassSize(), (int)fValDiff); From dd8ccca7251a9f8932823163083e07e7930452f3 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 25 Mar 2022 12:46:39 -0500 Subject: [PATCH 08/19] When resetting a pair's StreamerInfo inform all collection proxies --- core/cont/inc/TVirtualCollectionProxy.h | 3 +++ core/meta/src/TClass.cxx | 16 +++++++++++++++- io/io/inc/TGenCollectionProxy.h | 3 +++ io/io/src/TGenCollectionProxy.cxx | 13 +++++++++++++ io/io/src/TStreamerInfo.cxx | 2 ++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/cont/inc/TVirtualCollectionProxy.h b/core/cont/inc/TVirtualCollectionProxy.h index d191ef94b429b..49b8dd20efe31 100644 --- a/core/cont/inc/TVirtualCollectionProxy.h +++ b/core/cont/inc/TVirtualCollectionProxy.h @@ -74,6 +74,9 @@ class TVirtualCollectionProxy { virtual TVirtualCollectionProxy* Generate() const = 0; // Returns an object of the actual CollectionProxy class virtual ~TVirtualCollectionProxy() {}; + // Reset the info gathered from StreamerInfos and value's TClass. + virtual Bool_t Reset() { return kTRUE; }; + virtual TClass *GetCollectionClass() const { return fClass; } // Return a pointer to the TClass representing the container diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index aa13490241b75..47f6e137875db 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -6007,9 +6007,23 @@ void TClass::PostLoadCheck() pairname.append(GetName() + noffset); if ( auto pcl = TClass::GetClass(pairname.c_str(), false, false) ) { + TInterpreter::SuspendAutoLoadingRAII autoloadOff(gInterpreter); + + fCollectionProxy->Reset(); + TIter nextClass(gROOT->GetListOfClasses()); + while (auto acl = (TClass*)nextClass()) { + if (acl == this) continue; + if (acl->fCollectionProxy && acl->fCollectionProxy->GetValueClass() == pcl) { + acl->fCollectionProxy->Reset(); + } + } + TIter next(pcl->GetStreamerInfos()); while (auto info = (TVirtualStreamerInfo*)next()) { - info->Clear("build"); + if (info->IsBuilt()) { + info->Clear("build"); + info->BuildOld(); + } } fCollectionProxy->GetValueClass(); } diff --git a/io/io/inc/TGenCollectionProxy.h b/io/io/inc/TGenCollectionProxy.h index 4c3f390f36beb..13e5e3f95611d 100644 --- a/io/io/inc/TGenCollectionProxy.h +++ b/io/io/inc/TGenCollectionProxy.h @@ -359,6 +359,9 @@ class TGenCollectionProxy // Standard destructor. virtual ~TGenCollectionProxy(); + // Reset the info gathered from StreamerInfos and value's TClass. + virtual Bool_t Reset(); + // Return a pointer to the TClass representing the container. virtual TClass *GetCollectionClass() const; diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index daec43a9ed4c8..450c65a64d076 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -775,6 +775,19 @@ TGenCollectionProxy *TGenCollectionProxy::Initialize(Bool_t silent) const return p->InitializeEx(silent); } +//////////////////////////////////////////////////////////////////////////////// +/// Reset the info gathered from StreamerInfos and value's TClass. +Bool_t TGenCollectionProxy::Reset() +{ + if (fReadMemberWise) + fReadMemberWise->Clear(); + delete fWriteMemberWise; + fWriteMemberWise = nullptr; + if (fConversionReadMemberWise) + fConversionReadMemberWise->clear(); + return kTRUE; +} + //////////////////////////////////////////////////////////////////////////////// /// Check existence of function pointers diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index b91e34196f026..9eaf81127ab82 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -2609,10 +2609,12 @@ void TStreamerInfo::Clear(Option_t *option) delete [] fComp; fComp = 0; delete [] fCompFull; fCompFull= 0; delete [] fCompOpt; fCompOpt = 0; + fNdata = 0; fNfulldata = 0; fNslots= 0; fSize = 0; + ResetIsCompiled(); ResetBit(kBuildOldUsed); From 7e37d73545c72358e6407ac53c91aa8ec24eb6c5 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 30 Mar 2022 12:50:20 -0500 Subject: [PATCH 09/19] TProtoClass::FillTClass: Create a ListEnumsWithLock when needed --- core/meta/src/TProtoClass.cxx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/core/meta/src/TProtoClass.cxx b/core/meta/src/TProtoClass.cxx index 5fb2dc8356760..f82665543bc2a 100644 --- a/core/meta/src/TProtoClass.cxx +++ b/core/meta/src/TProtoClass.cxx @@ -24,6 +24,7 @@ Persistent version of a TClass. #include "TList.h" #include "TListOfDataMembers.h" #include "TListOfEnums.h" +#include "TListOfEnumsWithLock.h" #include "TRealData.h" #include "TError.h" #include "TVirtualCollectionProxy.h" @@ -31,6 +32,22 @@ Persistent version of a TClass. #include #include +#ifdef WIN32 +#include +#include "Windows4Root.h" +#include +#define RTLD_DEFAULT ((void *)::GetModuleHandle(NULL)) +#define dlsym(library, function_name) ::GetProcAddress((HMODULE)library, function_name) +#else +#include +#endif + +static bool IsFromRootCling() { + // rootcling also uses TCling for generating the dictionary ROOT files. + const static bool foundSymbol = dlsym(RTLD_DEFAULT, "usedToIdentifyRootClingByDlSym"); + return foundSymbol; +} + //////////////////////////////////////////////////////////////////////////////// /// Initialize a TProtoClass from a TClass. @@ -262,7 +279,8 @@ Bool_t TProtoClass::FillTClass(TClass* cl) { // We need to fill enums one by one to initialise the internal map which is // transient { - auto temp = new TListOfEnums(); + auto temp = cl->fEnums.load() ? cl->fEnums.load() : + IsFromRootCling() ? new TListOfEnums() : new TListOfEnumsWithLock(); if (fEnums) { for (TObject* enumAsTObj : *fEnums){ temp->Add((TEnum*) enumAsTObj); From 9c6129cb29e809c4297b22e200f37a3e2c33cc3f Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 30 Mar 2022 12:51:06 -0500 Subject: [PATCH 10/19] Don't put class' enum in rootpcm global list of enums --- io/rootpcm/src/rootclingIO.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io/rootpcm/src/rootclingIO.cxx b/io/rootpcm/src/rootclingIO.cxx index 4be9805fec7d1..0debb592b796a 100644 --- a/io/rootpcm/src/rootclingIO.cxx +++ b/io/rootpcm/src/rootclingIO.cxx @@ -248,6 +248,8 @@ bool CloseStreamerInfoROOTFile(bool writeEmptyRootPCM) Error("CloseStreamerInfoROOTFile", "Cannot find TClass instance for namespace %s.", nsName.c_str()); return false; } + if (!(tclassInstance->Property() & kIsNamespace)) + continue; // Enum will be part of the TClass. auto enumListPtr = tclassInstance->GetListOfEnums(); if (!enumListPtr) { Error("CloseStreamerInfoROOTFile", "TClass instance for namespace %s does not have any enum associated. This is an inconsistency.", nsName.c_str()); From a0c9807331fa672206396d91282036ce6318fb14 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 29 Mar 2022 14:31:20 -0500 Subject: [PATCH 11/19] pair: Also treat unordered map --- core/meta/src/TClass.cxx | 4 ++++ io/rootpcm/src/rootclingIO.cxx | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 47f6e137875db..7225f0b5c8347 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -6002,6 +6002,10 @@ void TClass::PostLoadCheck() noffset = 3; else if (strncmp(GetName(), "multimap<", 9) == 0) noffset = 8; + else if (strncmp(GetName(), "unordered_map<", 14) == 0) + noffset = 13; + else if (strncmp(GetName(), "unordered_multimap<", 19) == 0) + noffset = 18; if (noffset) { std::string pairname("pair"); pairname.append(GetName() + noffset); diff --git a/io/rootpcm/src/rootclingIO.cxx b/io/rootpcm/src/rootclingIO.cxx index 0debb592b796a..7d319ba6f84fe 100644 --- a/io/rootpcm/src/rootclingIO.cxx +++ b/io/rootpcm/src/rootclingIO.cxx @@ -200,7 +200,8 @@ bool CloseStreamerInfoROOTFile(bool writeEmptyRootPCM) if (auto pr = cl->GetCollectionProxy()) { auto colltype = pr->GetCollectionType(); - if (colltype == ROOT::kSTLmap || colltype == ROOT::kSTLmultimap) { + if (colltype == ROOT::kSTLmap || colltype == ROOT::kSTLmultimap || + colltype == ROOT::kSTLunorderedmap || colltype == ROOT::kSTLunorderedmultimap) { if (auto pcl = pr->GetValueClass()) { if (auto pcl_dms = pcl->GetListOfDataMembers()) { for (auto dmObj : *pcl_dms) { From c982d6f7a19563987ec02fb2bd322941eac509d1 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 29 Mar 2022 14:31:51 -0500 Subject: [PATCH 12/19] TClass: clear fLastReadInfo, fCurrentInfo when deleting TStreamerInfos --- core/meta/src/TClass.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 7225f0b5c8347..24c5162dc1dbc 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -7296,6 +7296,10 @@ void TClass::RemoveStreamerInfo(Int_t slot) R__LOCKGUARD(gInterpreterMutex); TVirtualStreamerInfo *info = (TVirtualStreamerInfo*)fStreamerInfo->At(slot); fStreamerInfo->RemoveAt(fClassVersion); + if (fLastReadInfo.load() == info) + fLastReadInfo = nullptr; + if (fCurrentInfo.load() == info) + fCurrentInfo = nullptr; delete info; if (fState == kEmulated && fStreamerInfo->GetEntries() == 0) { fState = kForwardDeclared; From 84a00aa4cae50aa554006d59fb448bb140f17307 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 29 Mar 2022 15:11:13 -0500 Subject: [PATCH 13/19] Do the pair TClass StreamerInfo refresh only when needed --- core/meta/src/TClass.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 24c5162dc1dbc..7db4e313ec3f1 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -6009,7 +6009,8 @@ void TClass::PostLoadCheck() if (noffset) { std::string pairname("pair"); pairname.append(GetName() + noffset); - if ( auto pcl = TClass::GetClass(pairname.c_str(), false, false) ) + auto pcl = TClass::GetClass(pairname.c_str(), false, false); + if ( pcl && !pcl->IsLoaded() && !pcl->IsSyntheticPair() ) { TInterpreter::SuspendAutoLoadingRAII autoloadOff(gInterpreter); From 942aa1b539f4e3f00b1f3c61d4e9af6317353cb5 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 31 Mar 2022 17:04:48 -0500 Subject: [PATCH 14/19] TBufferFile: create StreamerInfo only if needed. In the 'root_serialization' use case, we ended in TBufferFile::WriteClassBuffer with no StreamerInfo created yet for the TClass for `std::vector` and thus go into the branch that creates the StreamerInfo ... However, at least in that case, the call to `BuildRealData` induced the creation of the StreamerInfo but the code in `TBufferFile::WriteClassBuffer` did not notice and thus create a second one and tried to register it in the same slot. (The actual negative side effect was only an error message and a memory leak). --- io/io/src/TBufferFile.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index c5b759fa1aeee..979b514efb451 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -3516,6 +3516,8 @@ Int_t TBufferFile::WriteClassBuffer(const TClass *cl, void *pointer) //Have to be sure between the check and the taking of the lock if the current streamer has changed R__LOCKGUARD(gInterpreterMutex); sinfo = (TStreamerInfo*)const_cast(cl)->GetCurrentStreamerInfo(); + if (sinfo == nullptr) + sinfo = (TStreamerInfo*)const_cast(cl)->GetStreamerInfo(); if (sinfo == nullptr) { const_cast(cl)->BuildRealData(pointer); sinfo = new TStreamerInfo(const_cast(cl)); From 260355c879c641107a0832b7a32667c3d62b83a1 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 31 Mar 2022 17:09:08 -0500 Subject: [PATCH 15/19] TGenCollectionProxy init: fail if the value's StreamerInfo is not there. This happened in a user case where the container (map) for a pair of an enum, which had not dictionary and something else that did have a dictionary. --- io/io/src/TGenCollectionProxy.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index 450c65a64d076..7c229ac7d2722 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -894,11 +894,16 @@ TGenCollectionProxy *TGenCollectionProxy::InitializeEx(Bool_t silent) { TInterpreter::SuspendAutoParsing autoParseRaii(gCling); - if (0==TClass::GetClass(nam.c_str(), true, false, fValOffset, fValDiff)) { + TClass *paircl = TClass::GetClass(nam.c_str(), true, false, fValOffset, fValDiff); + if (paircl == nullptr) { // We need to emulate the pair - TVirtualStreamerInfo::Factory()->GenerateInfoForPair(inside[1], inside[2], silent, fValOffset, fValDiff); + auto info = TVirtualStreamerInfo::Factory()->GenerateInfoForPair(inside[1], inside[2], silent, fValOffset, fValDiff); + if (!info) { + Fatal("InitializeEx", + "Could not load nor generate the dictionary for \"%s\", some element might be missing their dictionary (eg. enums)", + nam.c_str()); + } } else { - TClass *paircl = TClass::GetClass(nam.c_str()); if (paircl->GetClassSize() != fValDiff) { if (paircl->GetState() >= TClass::kInterpreted) Fatal("InitializeEx", From 44bef955ec9a62a03c57ac27206f5b7a8bf015c2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 31 Mar 2022 17:10:19 -0500 Subject: [PATCH 16/19] Avoid reading pass the end of the StreamerInfo container. This was happening when, inadvertently, StreamerInfo were created multiple times and then immeditately deleted (for map) --- io/io/src/TStreamerInfo.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 9eaf81127ab82..7d4782b624cb0 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -214,7 +214,8 @@ TStreamerInfo::~TStreamerInfo() // Note: If a StreamerInfo is loaded from a file and is the same information // as an existing one, it is assigned the same "unique id" and we need to double // check before removing it from the global list. - if (fNumber >=0 && gROOT->GetListOfStreamerInfo()->At(fNumber) == this) + if (fNumber >=0 && gROOT->GetListOfStreamerInfo()->GetSize() > fNumber + && gROOT->GetListOfStreamerInfo()->At(fNumber) == this) gROOT->GetListOfStreamerInfo()->RemoveAt(fNumber); delete [] fComp; fComp = 0; From d36536d029c53ac909e9969060d3af9f647b4809 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 31 Mar 2022 18:48:12 -0500 Subject: [PATCH 17/19] CollectionProxy: pass pair hint to R__CreateValue --- io/io/inc/TGenCollectionProxy.h | 2 +- io/io/src/TGenCollectionProxy.cxx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/io/io/inc/TGenCollectionProxy.h b/io/io/inc/TGenCollectionProxy.h index 13e5e3f95611d..0cfe5ab5edaa6 100644 --- a/io/io/inc/TGenCollectionProxy.h +++ b/io/io/inc/TGenCollectionProxy.h @@ -70,7 +70,7 @@ class TGenCollectionProxy // Default copy constructor has the correct implementation. // Initializing constructor - Value(const std::string& info, Bool_t silent); + Value(const std::string& info, Bool_t silent, size_t hint_pair_offset = 0, size_t hint_pair_size = 0); // Delete individual item from STL container void DeleteItem(void* ptr); diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index 7c229ac7d2722..df36c7ff8095a 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -313,7 +313,7 @@ class TGenMapProxy : public TGenSetProxy { //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TGenCollectionProxy::Value::Value(const std::string& inside_type, Bool_t silent) +TGenCollectionProxy::Value::Value(const std::string& inside_type, Bool_t silent, size_t hint_pair_offset, size_t hint_pair_size) { std::string inside = (inside_type.find("const ")==0) ? inside_type.substr(6) : inside_type; fCase = 0; @@ -364,7 +364,7 @@ TGenCollectionProxy::Value::Value(const std::string& inside_type, Bool_t silent) // might fail because CINT does not known the nesting // scope, so let's first look for an emulated class: - fType = TClass::GetClass(intype.c_str(),kTRUE,silent); + fType = TClass::GetClass(intype.c_str(),kTRUE,silent, hint_pair_offset, hint_pair_size); if (fType) { if (isPointer) { @@ -828,9 +828,10 @@ void TGenCollectionProxy::CheckFunctions() const //////////////////////////////////////////////////////////////////////////////// /// Utility routine to issue a Fatal error is the Value object is not valid -static TGenCollectionProxy::Value *R__CreateValue(const std::string &name, Bool_t silent) +static TGenCollectionProxy::Value *R__CreateValue(const std::string &name, Bool_t silent, + size_t hint_pair_offset = 0, size_t hint_pair_size = 0) { - TGenCollectionProxy::Value *val = new TGenCollectionProxy::Value( name, silent ); + TGenCollectionProxy::Value *val = new TGenCollectionProxy::Value( name, silent, hint_pair_offset, hint_pair_size ); if ( !val->IsValid() ) { Fatal("TGenCollectionProxy","Could not find %s!",name.c_str()); } @@ -924,7 +925,7 @@ TGenCollectionProxy *TGenCollectionProxy::InitializeEx(Bool_t silent) } } } - newfValue = R__CreateValue(nam, silent); + newfValue = R__CreateValue(nam, silent, fValOffset, fValDiff); fPointers = (0 != (fKey->fCase&kIsPointer)); if (fPointers || (0 != (fKey->fProperties&kNeedDelete))) { From a5a3b0517e6ffe08c6f8ca8a5f9b5dba2a550dd8 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 6 Apr 2022 06:54:59 -0500 Subject: [PATCH 18/19] Remove unnecessary trailing semi-colon --- core/cont/inc/TVirtualCollectionProxy.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/cont/inc/TVirtualCollectionProxy.h b/core/cont/inc/TVirtualCollectionProxy.h index 49b8dd20efe31..9e76ee64e04b2 100644 --- a/core/cont/inc/TVirtualCollectionProxy.h +++ b/core/cont/inc/TVirtualCollectionProxy.h @@ -68,14 +68,14 @@ class TVirtualCollectionProxy { TPushPop& operator=(const TPushPop&) = delete; }; - TVirtualCollectionProxy() : fClass(), fProperties(0) {}; - TVirtualCollectionProxy(TClass *cl) : fClass(cl), fProperties(0) {}; + TVirtualCollectionProxy() : fClass(), fProperties(0) {} + TVirtualCollectionProxy(TClass *cl) : fClass(cl), fProperties(0) {} virtual TVirtualCollectionProxy* Generate() const = 0; // Returns an object of the actual CollectionProxy class - virtual ~TVirtualCollectionProxy() {}; + virtual ~TVirtualCollectionProxy() {} // Reset the info gathered from StreamerInfos and value's TClass. - virtual Bool_t Reset() { return kTRUE; }; + virtual Bool_t Reset() { return kTRUE; } virtual TClass *GetCollectionClass() const { return fClass; } // Return a pointer to the TClass representing the container From c8a128375d7a507a00238d988ce85645252a3ca2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 6 Apr 2022 06:58:30 -0500 Subject: [PATCH 19/19] [NFC] Simplify code in TListOfEnums --- core/meta/src/TListOfEnums.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/meta/src/TListOfEnums.cxx b/core/meta/src/TListOfEnums.cxx index 51aeabca6ec4a..8a35a135355a1 100644 --- a/core/meta/src/TListOfEnums.cxx +++ b/core/meta/src/TListOfEnums.cxx @@ -192,8 +192,7 @@ TObject *TListOfEnums::FindObject(const char *name) const TObject *result = THashList::FindObject(name); if (!result) { TInterpreter::DeclId_t decl; - if (GetClass()) decl = gInterpreter->GetEnum(GetClass(), name); - else decl = gInterpreter->GetEnum(nullptr, name); + decl = gInterpreter->GetEnum(GetClass(), name); if (decl) result = const_cast(this)->Get(decl, name); } return result;