Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/foundation/inc/TClassEdit.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ namespace TClassEdit {
void Init(TClassEdit::TInterpreterLookupHelper *helper);

std::string CleanType (const char *typeDesc,int mode = 0,const char **tail=0);
inline bool IsArtificial(std::string_view name) { return name.find('@') != name.npos; }
inline bool IsArtificial(ROOT::Internal::TStringView name) {return IsArtificial(std::string_view(name)); }
bool IsDefAlloc(const char *alloc, const char *classname);
bool IsDefAlloc(const char *alloc, const char *keyclassname, const char *valueclassname);
bool IsDefComp (const char *comp , const char *classname);
Expand Down Expand Up @@ -185,6 +187,16 @@ namespace TClassEdit {
inline bool IsUniquePtr(ROOT::Internal::TStringView name) {return IsUniquePtr(std::string_view(name)); }
inline bool IsStdArray(std::string_view name) {return 0 == name.compare(0, 6, "array<");}
inline bool IsStdArray(ROOT::Internal::TStringView name) {return IsStdArray(std::string_view(name)); }
inline bool IsStdPair(std::string_view name)
{
return 0 == name.compare(0, 10, "std::pair<") || 0 == name.compare(0, 5, "pair<");
}
inline bool IsStdPair(ROOT::Internal::TStringView name) {return IsStdPair(std::string_view(name)); }
inline bool IsStdPairBase(std::string_view name)
{
return 0 == name.compare(0, 17, "std::__pair_base<") || 0 == name.compare(0, 12, "__pair_base<");
}
inline bool IsStdPairBase(ROOT::Internal::TStringView name) {return IsStdPair(std::string_view(name)); }
inline std::string GetUniquePtrType(std::string_view name)
{
// Find the first template parameter
Expand Down
14 changes: 11 additions & 3 deletions core/foundation/src/TClassEdit.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -838,16 +838,24 @@ void TClassEdit::GetNormalizedName(std::string &norm_name, std::string_view name
}

norm_name = std::string(name); // NOTE: Is that the shortest version?

if (TClassEdit::IsArtificial(name)) {
// If there is a @ symbol (followed by a version number) then this is a synthetic class name created
// from an already normalized name for the purpose of supporting schema evolution.
return;
}

// Remove the std:: and default template argument and insert the Long64_t and change basic_string to string.
TClassEdit::TSplitType splitname(norm_name.c_str(),(TClassEdit::EModType)(TClassEdit::kLong64 | TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst));
splitname.ShortType(norm_name, TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kResolveTypedef | TClassEdit::kKeepOuterConst);

if (splitname.fElements.size() == 3 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not a bit fan of == 3 and == 4 without any comment. See also the change in tree/treeplayer/src/TTreeGeneratorBase.cxx

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TTreeGeneratorBase.cxx, I left 3 or 4 because the likely wrong one was there with no (obvious negative consequence) and (I should add a comment) thus it might actually be the right one .... i.e. to remove it I would have to try to produce a test and the code is used little enough to not be a priority (to spend the time to write the test).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment would be great! :-)

Copy link
Member Author

@pcanal pcanal Sep 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without any comment.

Well that's part of the documented contract of TClassEdit::TSplitType :)

But nevertheless I will add a comment.

// 4 elements expected: "pair", "first type name", "second type name", "trailing stars"
if (splitname.fElements.size() == 4 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair" || splitname.fElements[0] == "__pair_base")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not use IsStdPair here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The test is also on the base class name (__pair_base) and technically the name can have wild spelling. I.e. the input name (unsplit and unnormalized of course) could be "std :: pair <" and the split name lacks the < that IsStdPair expects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here I mean that I need to do the test after the call to Split to remove the problem with spelling (and then the name does not have the "<" and thus IsStdPair is not applicable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can splitname be std::pair when splitname is the result of kDropStd? Or am I misremembering input/output of ShortType?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually ... that's right .. the test for std::pair is here superfluous ...

// We don't want to lookup the std::pair itself.
std::string first, second;
GetNormalizedName(first, splitname.fElements[1]);
GetNormalizedName(second, splitname.fElements[2]);
norm_name = "pair<" + first + "," + second;
norm_name = splitname.fElements[0] + "<" + first + "," + second;
if (!second.empty() && second.back() == '>')
norm_name += " >";
else
Expand Down Expand Up @@ -1391,7 +1399,7 @@ bool TClassEdit::IsStdClass(const char *classname)
classname += StdLen( classname );
if ( strcmp(classname,"string")==0 ) return true;
if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true;
if ( strncmp(classname,"pair<",strlen("pair<"))==0) return true;
if ( IsStdPair(classname) ) return true;
if ( strcmp(classname,"allocator")==0) return true;
if ( strncmp(classname,"allocator<",strlen("allocator<"))==0) return true;
if ( strncmp(classname,"greater<",strlen("greater<"))==0) return true;
Expand Down
10 changes: 9 additions & 1 deletion core/meta/inc/TVirtualStreamerInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class TVirtualStreamerInfo : public TNamed {
TVirtualStreamerInfo(TClass * /*cl*/);
virtual ~TVirtualStreamerInfo();
virtual void Build() = 0;
virtual void BuildCheck(TFile *file = 0) = 0;
virtual void BuildCheck(TFile *file = 0, Bool_t load = kTRUE) = 0;
virtual void BuildEmulated(TFile *file) = 0;
virtual void BuildOld() = 0;
virtual Bool_t BuildFor( const TClass *cl ) = 0;
Expand Down Expand Up @@ -180,6 +180,14 @@ class TVirtualStreamerInfo : public TNamed {
static void SetCanDelete(Bool_t opt=kTRUE);
static void SetFactory(TVirtualStreamerInfo *factory);

// \brief Generate the TClass and TStreamerInfo for the requested pair.
// This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to
// provokes the creation of the corresponding TClass. This relies on the dictionary for
// std::pair<const int, int> to already exist (or the interpreter information being available)
// as it is used as a template.
Comment on lines +185 to +187
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// provokes the creation of the corresponding TClass. This relies on the dictionary for
// std::pair<const int, int> to already exist (or the interpreter information being available)
// as it is used as a template.
// provokes the creation of the corresponding TClass.

Implementation detail; this should be asserted by GenerateInfoForPair, not be part of the doc.

Comment on lines +183 to +187
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// \brief Generate the TClass and TStreamerInfo for the requested pair.
// This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to
// provokes the creation of the corresponding TClass. This relies on the dictionary for
// std::pair<const int, int> to already exist (or the interpreter information being available)
// as it is used as a template.
// \brief Generate the TClass and TStreamerInfo for the requested pair.
/// This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to
/// provokes the creation of the corresponding TClass. This relies on the dictionary for
/// std::pair<const int, int> to already exist (or the interpreter information being available)
/// as it is used as a template.

virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &pairclassname) = 0;
virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &firstname, const std::string &secondname) = 0;

virtual TVirtualCollectionProxy *GenEmulatedProxy(const char* class_name, Bool_t silent) = 0;
virtual TClassStreamer *GenEmulatedClassStreamer(const char* class_name, Bool_t silent) = 0;
virtual TVirtualCollectionProxy *GenExplicitProxy( const ::ROOT::Detail::TCollectionProxyInfo &info, TClass *cl ) = 0;
Expand Down
70 changes: 48 additions & 22 deletions core/meta/src/TClass.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ In order to access the name of a class within the ROOT type system, the method T
#include "TDataMember.h"
#include "TDataType.h"
#include "TDatime.h"
#include "TEnum.h"
#include "TError.h"
#include "TExMap.h"
#include "TFunctionTemplate.h"
Expand Down Expand Up @@ -170,6 +171,12 @@ namespace {

std::atomic<Int_t> TClass::fgClassCount;

static bool IsFromRootCling() {
// rootcling also uses TCling for generating the dictionary ROOT files.
const static bool foundSymbol = dlsym(RTLD_DEFAULT, "usedToIdentifyRootClingByDlSym");
return foundSymbol;
}

// Implementation of the TDeclNameRegistry

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1108,7 +1115,7 @@ TClass::TClass(const char *name, Bool_t silent) :
::Fatal("TClass::TClass", "gInterpreter not initialized");

gInterpreter->SetClassInfo(this); // sets fClassInfo pointer
if (!silent && !fClassInfo && fName.First('@')==kNPOS)
if (!silent && !fClassInfo && !TClassEdit::IsArtificial(name))
::Warning("TClass::TClass", "no dictionary for class %s is available", name);
ResetBit(kLoading);

Expand Down Expand Up @@ -1339,7 +1346,11 @@ void TClass::Init(const char *name, Version_t cversion,
return;
}
// Always strip the default STL template arguments (from any template argument or the class name)
fName = TClassEdit::ShortType(name, TClassEdit::kDropStlDefault).c_str();
if (TClassEdit::IsArtificial(name))
fName = name; // We can assume that the artificial class name is already normalized.
else
fName = TClassEdit::ShortType(name, TClassEdit::kDropStlDefault).c_str();

fClassVersion = cversion;
fDeclFileName = dfil ? dfil : "";
fImplFileName = ifil ? ifil : "";
Expand Down Expand Up @@ -1444,7 +1455,7 @@ void TClass::Init(const char *name, Version_t cversion,
// We need to check if the class it is not fwd declared for the cases where we
// created a TClass directly in the kForwardDeclared state. Indeed in those cases
// fClassInfo will always be nullptr.
if (fState!=kForwardDeclared && !fClassInfo) {
if (fState!=kForwardDeclared && !fClassInfo && !TClassEdit::IsArtificial(fName)) {

if (fState == kHasTClassInit) {
// If the TClass is being generated from a ROOT dictionary,
Expand Down Expand Up @@ -1482,7 +1493,7 @@ void TClass::Init(const char *name, Version_t cversion,
}
}
}
if (!silent && (!fClassInfo && !fCanLoadClassInfo) && !isStl && fName.First('@')==kNPOS &&
if (!silent && (!fClassInfo && !fCanLoadClassInfo) && !isStl && !TClassEdit::IsArtificial(fName) &&
!TClassEdit::IsInterpreterDetail(fName.Data()) ) {
if (fState == kHasTClassInit) {
if (fImplFileLine == -1 && fClassVersion == 0) {
Expand Down Expand Up @@ -1599,7 +1610,7 @@ void TClass::Init(const char *name, Version_t cversion,
fStreamer = TVirtualStreamerInfo::Factory()->GenEmulatedClassStreamer( GetName(), silent );
}
}
} else if (!strncmp(GetName(),"std::pair<",10) || !strncmp(GetName(),"pair<",5) ) {
} else if (TClassEdit::IsStdPair(GetName())) {
// std::pairs have implicit conversions
GetSchemaRules(kTRUE);
}
Expand Down Expand Up @@ -2014,7 +2025,7 @@ void TClass::BuildRealData(void* pointer, Bool_t isTransient)
// and those for which the user explicitly requested a dictionary.
if (!isTransient && GetState() != kHasTClassInit
&& TClassEdit::IsStdClass(GetName())
&& strncmp(GetName(), "pair<", 5) != 0) {
&& !TClassEdit::IsStdPair(GetName())) {
Error("BuildRealData", "Inspection for %s not supported!", GetName());
}

Expand Down Expand Up @@ -2304,14 +2315,6 @@ Bool_t TClass::CanSplit() const
if (!valueClass->CanSplit()) { This->fCanSplit = 0; return kFALSE; }
if (valueClass->GetCollectionProxy() != 0) { This->fCanSplit = 0; return kFALSE; }

Int_t stl = -TClassEdit::IsSTLCont(GetName(), 0);
if ((stl==ROOT::kSTLmap || stl==ROOT::kSTLmultimap)
&& !valueClass->HasDataMemberInfo())
{
This->fCanSplit = 0;
return kFALSE;
}

This->fCanSplit = 1;
return kTRUE;

Expand Down Expand Up @@ -2970,6 +2973,15 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent)
load = kTRUE;
}

if (TClassEdit::IsArtificial(name)) {
// If there is a @ symbol (followed by a version number) then this is a synthetic class name created
// from an already normalized name for the purpose of supporting schema evolution.
// There is no dictionary or interpreter information about this kind of class, the only
// (undesirable) side-effect of doing the search would be a waste of CPU time and potential
// auto-loading or auto-parsing based on the scope of the name.
return cl;
}

// To avoid spurious auto parsing, let's check if the name as-is is
// known in the TClassTable.
DictFuncPtr_t dict = TClassTable::GetDictNorm(name);
Expand Down Expand Up @@ -3030,15 +3042,27 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent)
// altcl->GetName(), name, normalizedName.c_str());
// }

// We want to avoid auto-parsing due to intentionally missing dictionary for std::pair.
// However, we don't need this special treatement in rootcling (there is no auto-parsing)
// and we want to make that the TClass for the pair goes through the regular creation
// mechanism (i.e. in rootcling they should be in kInterpreted state and never in
// kEmulated state) so that they have proper interpreter (ClassInfo) information which
// will be used to create the TProtoClass (if one is requested for the pair).
const bool ispair = TClassEdit::IsStdPair(normalizedName) && !IsFromRootCling();
const bool ispairbase = TClassEdit::IsStdPairBase(normalizedName) && !IsFromRootCling();

TClass *loadedcl = 0;
if (checkTable) {
loadedcl = LoadClassDefault(normalizedName.c_str(),silent);
} else {
if (gInterpreter->AutoLoad(normalizedName.c_str(),kTRUE)) {
loadedcl = LoadClassDefault(normalizedName.c_str(),silent);
}
auto e = TEnum::GetEnum(normalizedName.c_str(), TEnum::kNone);
if (e)
return nullptr;
// Maybe this was a typedef: let's try to see if this is the case
if (!loadedcl){
if (!loadedcl && !ispair && !ispairbase) {
if (TDataType* theDataType = gROOT->GetType(normalizedName.c_str())){
// We have a typedef: we get the name of the underlying type
auto underlyingTypeName = theDataType->GetTypeName();
Expand All @@ -3061,13 +3085,17 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent)
// TClass if we have one.
if (cl) return cl;

if (TClassEdit::IsSTLCont( normalizedName.c_str() )) {
if (ispair) {
auto pairinfo = TVirtualStreamerInfo::Factory()->GenerateInfoForPair(normalizedName);
return pairinfo ? pairinfo->GetClass() : nullptr;

} else if (TClassEdit::IsSTLCont( normalizedName.c_str() )) {

return gInterpreter->GenerateTClass(normalizedName.c_str(), kTRUE, silent);
}

// Check the interpreter only after autoparsing the template if any.
{
if (!ispairbase) {
std::string::size_type posLess = normalizedName.find('<');
if (posLess != std::string::npos) {
gCling->AutoParse(normalizedName.substr(0, posLess).c_str());
Expand Down Expand Up @@ -3632,9 +3660,7 @@ TList *TClass::GetListOfEnums(Bool_t requestListLoading /* = kTRUE */)
return fEnums.load();
}

static bool fromRootCling = dlsym(RTLD_DEFAULT, "usedToIdentifyRootClingByDlSym");

if (fromRootCling) // rootcling is single thread (this save some space in the rootpcm).
if (IsFromRootCling()) // rootcling is single thread (this save some space in the rootpcm).
fEnums = new TListOfEnums(this);
else
fEnums = new TListOfEnumsWithLock(this);
Expand Down Expand Up @@ -3997,7 +4023,7 @@ void TClass::GetMissingDictionaries(THashTable& result, bool recurse)

THashTable visited;

if (strncmp(fName, "pair<", 5) == 0) {
if (TClassEdit::IsStdPair(fName)) {
GetMissingDictionariesForPairElements(result, visited, recurse);
return;
}
Expand Down Expand Up @@ -6339,7 +6365,7 @@ UInt_t TClass::GetCheckSum(ECheckSum code, Bool_t &isvalid) const
// otherwise, on some STL implementations, it can happen that pair has
// base classes which are an internal implementation detail.
TList *tlb = ((TClass*)this)->GetListOfBases();
if (tlb && !GetCollectionProxy() && strncmp(GetName(), "pair<", 5)) {
if (tlb && !GetCollectionProxy() && !TClassEdit::IsStdPair(GetName())) {
// Loop over bases if not a proxied collection or a pair

TIter nextBase(tlb);
Expand Down
2 changes: 1 addition & 1 deletion core/meta/src/TListOfEnums.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ TEnum *TListOfEnums::GetObject(const char *name) const
void TListOfEnums::UnmapObject(TObject *obj)
{
TEnum *e = dynamic_cast<TEnum *>(obj);
if (e) {
if (e && e->GetDeclId()) {
fIds->Remove((Long64_t)e->GetDeclId());
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/meta/src/TSchemaRuleSet.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ Bool_t TSchemaRuleSet::HasRuleWithSourceClass( const TString &source ) const
}
}
}
} else if (!strncmp(fClass->GetName(),"std::pair<",10) || !strncmp(fClass->GetName(),"pair<",5)) {
if (!strncmp(source,"std::pair<",10) || !strncmp(source,"pair<",5)) {
} else if (TClassEdit::IsStdPair(fClass->GetName())) {
if (TClassEdit::IsStdPair(source)) {
// std::pair can be converted into each other if both its parameter can be converted into
// each other.
TClass *src = TClass::GetClass(source);
Expand Down
34 changes: 26 additions & 8 deletions core/metacling/src/TCling.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4031,7 +4031,31 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp

const char *classname = name;

int storeAutoload = SetClassAutoLoading(autoload);
// RAII to suspend and restore auto-loading and auto-parsing based on some external conditions.
class MaybeSuspendAutoLoadParse {
int fStoreAutoLoad = 0;
int fStoreAutoParse = 0;
bool fSuspendedAutoParse = false;
public:
MaybeSuspendAutoLoadParse(int autoload) {
fStoreAutoLoad = ((TCling*)gCling)->SetClassAutoLoading(autoload);
}

void SuspendAutoParsing() {
fSuspendedAutoParse = true;
fStoreAutoParse = ((TCling*)gCling)->SetSuspendAutoParsing(true);
}

~MaybeSuspendAutoLoadParse() {
if (fSuspendedAutoParse)
((TCling*)gCling)->SetSuspendAutoParsing(fStoreAutoParse);
((TCling*)gCling)->SetClassAutoLoading(fStoreAutoLoad);
}
};

MaybeSuspendAutoLoadParse autoLoadParseRAII( autoload );
if (TClassEdit::IsStdPair(classname) || TClassEdit::IsStdPairBase(classname))
autoLoadParseRAII.SuspendAutoParsing();

// First we want to check whether the decl exist, but _without_
// generating any template instantiation. However, the lookup
Expand Down Expand Up @@ -4077,14 +4101,12 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp
// findscope.
if (ROOT::TMetaUtils::IsSTLCont(*tmpltDecl)) {
// For STL Collection we return kUnknown.
SetClassAutoLoading(storeAutoload);
return kUnknown;
}
}
}
TClingClassInfo tci(GetInterpreterImpl(), *type);
if (!tci.IsValid()) {
SetClassAutoLoading(storeAutoload);
return kUnknown;
}
auto propertiesMask = isClassOrNamespaceOnly ? kIsClass | kIsStruct | kIsNamespace :
Expand All @@ -4111,19 +4133,16 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp
// , hasClassDefInline);

// We are now sure that the entry is not in fact an autoload entry.
SetClassAutoLoading(storeAutoload);
if (hasClassDefInline)
return kWithClassDefInline;
else
return kKnown;
} else {
// We are now sure that the entry is not in fact an autoload entry.
SetClassAutoLoading(storeAutoload);
return kUnknown;
}
}

SetClassAutoLoading(storeAutoload);
if (decl)
return kKnown;
else
Expand All @@ -4135,7 +4154,6 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp
TClingTypedefInfo t(fInterpreter, name);
if (t.IsValid() && !(t.Property() & kIsFundamental)) {
delete[] classname;
SetClassAutoLoading(storeAutoload);
return kTRUE;
}
*/
Expand All @@ -4146,7 +4164,6 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp
// decl = lh.findScope(buf);
// }

// SetClassAutoLoading(storeAutoload);
// return (decl);
}

Expand Down Expand Up @@ -4364,6 +4381,7 @@ TClass *TCling::GenerateTClass(const char *classname, Bool_t emulation, Bool_t s
if (TClassEdit::IsSTLCont(classname)) {
version = TClass::GetClass("TVirtualStreamerInfo")->GetClassVersion();
}
R__LOCKGUARD(gInterpreterMutex);
TClass *cl = new TClass(classname, version, silent);
if (emulation) {
cl->SetBit(TClass::kIsEmulation);
Expand Down
Loading