Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix spurrious auto parsing when looking up a TClass #17994

Merged
merged 5 commits into from
Mar 22, 2025
Merged
Changes from all commits
Commits
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
137 changes: 75 additions & 62 deletions core/meta/src/TClass.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3089,7 +3089,8 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi

// Early return to release the lock without having to execute the
// long-ish normalization.
if (cl && (cl->IsLoaded() || cl->TestBit(kUnloading))) return cl;
if (cl && (cl->IsLoaded() || cl->TestBit(kUnloading)))
return cl;

R__WRITE_LOCKGUARD(ROOT::gCoreMutex);

Expand All @@ -3098,7 +3099,8 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi

cl = (TClass*)gROOT->GetListOfClasses()->FindObject(name);
if (cl) {
if (cl->IsLoaded() || cl->TestBit(kUnloading)) return cl;
if (cl->IsLoaded() || cl->TestBit(kUnloading))
return cl;

// We could speed-up some of the search by adding (the equivalent of)
//
Expand Down Expand Up @@ -3131,11 +3133,11 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi

// To avoid spurious auto parsing, let's check if the name as-is is
// known in the TClassTable.
DictFuncPtr_t dict = TClassTable::GetDictNorm(name);
if (dict) {
if (DictFuncPtr_t dict = TClassTable::GetDictNorm(name)) {
// The name is normalized, so the result of the first search is
// authoritative.
if (!cl && !load) return nullptr;
if (!cl && !load)
return nullptr;

TClass *loadedcl = (dict)();
if (loadedcl) {
Expand All @@ -3147,8 +3149,11 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi
// continue as before ...
}

// Note: this variable does not always holds the fully normalized name
// as there is information from a not yet loaded library or from header
// not yet parsed that may be needed to fully normalize the name.
std::string normalizedName;
Bool_t checkTable = kFALSE;
Bool_t nameChanged = kFALSE;

if (!cl) {
{
Expand All @@ -3160,34 +3165,20 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi
cl = (TClass*)gROOT->GetListOfClasses()->FindObject(normalizedName.c_str());

if (cl) {
if (cl->IsLoaded() || cl->TestBit(kUnloading)) return cl;
if (cl->IsLoaded() || cl->TestBit(kUnloading))
return cl;

//we may pass here in case of a dummy class created by TVirtualStreamerInfo
load = kTRUE;
}
checkTable = kTRUE;
nameChanged = kTRUE;
}
} else {
normalizedName = cl->GetName(); // Use the fact that all TClass names are normalized.
checkTable = load && (normalizedName != name);
}

if (!load) return nullptr;

// This assertion currently fails because of
// TClass *c1 = TClass::GetClass("basic_iostream<char,char_traits<char> >");
// TClass *c2 = TClass::GetClass("std::iostream");
// where the TClassEdit normalized name of iostream is basic_iostream<char>
// i.e missing the addition of the default parameter. This is because TClingLookupHelper
// uses only 'part' of TMetaUtils::GetNormalizedName.

// if (!cl) {
// TDataType* dataType = (TDataType*)gROOT->GetListOfTypes()->FindObject(name);
// TClass *altcl = dataType ? (TClass*)gROOT->GetListOfClasses()->FindObject(dataType->GetFullTypeName()) : 0;
// if (altcl && normalizedName != altcl->GetName())
// ::Fatal("TClass::GetClass","The existing name (%s) for %s is different from the normalized name: %s\n",
// altcl->GetName(), name, normalizedName.c_str());
// }
if (!load)
return nullptr;

// 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)
Expand All @@ -3198,50 +3189,72 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi
const bool ispair = TClassEdit::IsStdPair(normalizedName) && !IsFromRootCling();
const bool ispairbase = TClassEdit::IsStdPairBase(normalizedName) && !IsFromRootCling();

TClass *loadedcl = nullptr;
if (checkTable) {
loadedcl = LoadClassDefault(normalizedName.c_str(),silent);
} else {
if (gInterpreter->AutoLoad(normalizedName.c_str(),kTRUE)) {
// At this point more information has been loaded. This
// information might be pertinent to the normalization of the name.
// For example it might contain or be a typedef for which we don't
// have a forward declaration (eg. typedef to instance of class
// template with default parameters). So let's redo the normalization
// as the new information (eg. typedef in TROOT::GetListOfTypes) might
// lead to a different value.
{
TInterpreter::SuspendAutoLoadingRAII autoloadOff(gInterpreter);
TClassEdit::GetNormalizedName(normalizedName, name);
auto loadClass = [](const char *requestedname) -> TClass* {
DictFuncPtr_t dict = TClassTable::GetDictNorm(requestedname);
if (dict) {
TClass *loadedcl = (dict)();
if (loadedcl) {
loadedcl->PostLoadCheck();
return loadedcl;
}
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 && !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();
// We see if we can bootstrap a class with it
auto underlyingTypeDict = TClassTable::GetDictNorm(underlyingTypeName.Data());
if (underlyingTypeDict){
loadedcl = underlyingTypeDict();
}
return nullptr;
};

}
// Check with the changed name first.
if (nameChanged) {
if(TClass *loadedcl = loadClass(normalizedName.c_str()))
return loadedcl;
}
if (gInterpreter->AutoLoad(normalizedName.c_str(),kTRUE)) {
// Check if we just loaded the necessary dictionary.
if (TClass *loadedcl = loadClass(normalizedName.c_str()))
return loadedcl;

// At this point more information has been loaded. This
// information might be pertinent to the normalization of the name.
// For example it might contain or be a typedef for which we don't
// have a forward declaration (eg. typedef to instance of class
// template with default parameters). So let's redo the normalization
// as the new information (eg. typedef in TROOT::GetListOfTypes) might
// lead to a different value.
{
TInterpreter::SuspendAutoLoadingRAII autoloadOff(gInterpreter);
std::string normalizedNameAfterAutoLoad;
TClassEdit::GetNormalizedName(normalizedNameAfterAutoLoad, name);
nameChanged = normalizedNameAfterAutoLoad != normalizedName;
normalizedName = normalizedNameAfterAutoLoad;
}
if (nameChanged) {
// Try to load with an attempt to autoload with the new name
if (TClass *loadedcl = LoadClassDefault(normalizedName.c_str(), silent))
return loadedcl;
}
}

// If name is known to be an enum, we don't need to try to load it.
if (TEnum::GetEnum(normalizedName.c_str(), TEnum::kNone))
return nullptr;

// Maybe this was a typedef: let's try to see if this is the case
if (!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();
// We see if we can bootstrap a class with it
if (TClass *loadedcl = LoadClassDefault(underlyingTypeName, silent))
return loadedcl;
}
}
if (loadedcl) return loadedcl;

// See if the TClassGenerator can produce the TClass we need.
loadedcl = LoadClassCustom(normalizedName.c_str(),silent);
if (loadedcl) return loadedcl;
if (TClass *loadedcl = LoadClassCustom(normalizedName.c_str(), silent))
return loadedcl;

// We have not been able to find a loaded TClass, return the Emulated
// TClass if we have one.
if (cl) return cl;
if (cl)
return cl;

if (ispair) {
if (hint_pair_offset && hint_pair_size) {
Expand Down Expand Up @@ -3270,8 +3283,8 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi
}
}
}
} else if (TClassEdit::IsSTLCont( normalizedName.c_str() )) {

} else if (TClassEdit::IsSTLCont( normalizedName.c_str() ))
{
return gInterpreter->GenerateTClass(normalizedName.c_str(), kTRUE, silent);
}

Expand Down Expand Up @@ -5885,7 +5898,7 @@ TClass *TClass::LoadClassDefault(const char *requestedname, Bool_t /* silent */)
DictFuncPtr_t dict = TClassTable::GetDictNorm(requestedname);

if (!dict) {
if (gInterpreter->AutoLoad(requestedname,kTRUE)) {
if (gInterpreter->AutoLoad(requestedname, kTRUE)) {
dict = TClassTable::GetDictNorm(requestedname);
}
}
Expand Down
Loading