diff --git a/core/meta/inc/TDictionary.h b/core/meta/inc/TDictionary.h index 42768707d54db..98ae6bf717727 100644 --- a/core/meta/inc/TDictionary.h +++ b/core/meta/inc/TDictionary.h @@ -163,10 +163,14 @@ class TDictionary : public TNamed { private: TDictAttributeMap *fAttributeMap; //pointer to a class attribute map + ULong64_t fUpdatingTransactionCount; //the Cling ID of the transaction that last updated the object + +protected: + Bool_t InterpreterStateHasChanged(); public: - TDictionary(): fAttributeMap(0) { } - TDictionary(const char* name): TNamed(name, ""), fAttributeMap(0) { } + TDictionary(): fAttributeMap(0), fUpdatingTransactionCount(0) { } + TDictionary(const char* name): TNamed(name, ""), fAttributeMap(0), fUpdatingTransactionCount(0) { } TDictionary(const TDictionary& dict); virtual ~TDictionary(); diff --git a/core/meta/inc/TEnum.h b/core/meta/inc/TEnum.h index 97b41cfe96ce9..5ffcda9ae03b5 100644 --- a/core/meta/inc/TEnum.h +++ b/core/meta/inc/TEnum.h @@ -38,9 +38,7 @@ class TClass; class TEnumConstant; -class TEnum : public TNamed { - -typedef TDictionary::DeclId_t DeclId_t; +class TEnum : public TDictionary { private: THashList fConstantList; //list of constants the enum type @@ -61,6 +59,7 @@ typedef TDictionary::DeclId_t DeclId_t; } DeclId_t GetDeclId() const { return (DeclId_t)fInfo; } Bool_t IsValid(); + Long_t Property() const; void Update(DeclId_t id); ClassDef(TEnum,2) //Enum type class diff --git a/core/meta/inc/TInterpreter.h b/core/meta/inc/TInterpreter.h index 95d5c6a3230e4..9d84c9a6b7d38 100644 --- a/core/meta/inc/TInterpreter.h +++ b/core/meta/inc/TInterpreter.h @@ -212,6 +212,7 @@ class TInterpreter : public TNamed { // core/meta helper functions. virtual TMethodCall::EReturnType MethodCallReturnType(TFunction *func) const = 0; + virtual ULong64_t GetInterpreterStateMarker() const = 0; typedef TDictionary::DeclId_t DeclId_t; virtual DeclId_t GetDeclId(CallFunc_t *info) const = 0; diff --git a/core/meta/src/TCling.cxx b/core/meta/src/TCling.cxx index 01512bf11906e..b2590f9fad6d5 100644 --- a/core/meta/src/TCling.cxx +++ b/core/meta/src/TCling.cxx @@ -399,150 +399,13 @@ extern "C" void TCling__UpdateListsOnCommitted(const cling::Transaction &T, cling::Interpreter* interp) { - std::set modifiedTClasses; // TClasses that require update after this transaction - - bool isTUTransaction = false; - if (T.decls_end()-T.decls_begin() == 1 && !T.hasNestedTransactions()) { - clang::Decl* FirstDecl = *(T.decls_begin()->m_DGR.begin()); - if (clang::TranslationUnitDecl* TU - = dyn_cast(FirstDecl)) { - // The is the first transaction, we have to expose to meta - // what's already in the AST. - isTUTransaction = true; - - // FIXME: don't load the world. Really, don't. Maybe - // instead smarten TROOT::GetListOfWhateveros() which - // currently is a THashList but could be a - // TInterpreterLookupCollection, one that reimplements - // TCollection::FindObject(name) and performs a lookup - // if not found in its T(Hash)List. - cling::Interpreter::PushTransactionRAII RAII(interp); - for (clang::DeclContext::decl_iterator TUI = TU->decls_begin(), - TUE = TU->decls_end(); TUI != TUE; ++TUI) - ((TCling*)gCling)->HandleNewDecl(*TUI, (*TUI)->isFromASTFile(),modifiedTClasses); - } - } - - std::set TransactionDeclSet; - if (!isTUTransaction && T.decls_end() - T.decls_begin()) { - const clang::Decl* WrapperFD = T.getWrapperFD(); - for (cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end(); - I != E; ++I) { - if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl) - continue; - - for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), - DE = I->m_DGR.end(); DI != DE; ++DI) { - if (*DI == WrapperFD) - continue; - TransactionDeclSet.insert(*DI); - ((TCling*)gCling)->HandleNewDecl(*DI, false, modifiedTClasses); - } - } - } - - // The above might trigger more decls to be deserialized. - // Thus the iteration over the deserialized decls must be last. - for (cling::Transaction::const_iterator I = T.deserialized_decls_begin(), - E = T.deserialized_decls_end(); I != E; ++I) { - for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), - DE = I->m_DGR.end(); DI != DE; ++DI) - if (TransactionDeclSet.find(*DI) == TransactionDeclSet.end()) { - //FIXME: HandleNewDecl should take DeclGroupRef - ((TCling*)gCling)->HandleNewDecl(*DI, /*isDeserialized*/true, - modifiedTClasses); - } - } - - - // When fully building the reflection info in TClass, a deserialization - // could be triggered, which may result in request for building the - // reflection info for the same TClass. This in turn will clear the caches - // for the TClass in-flight and cause null ptr derefs. - // FIXME: This is a quick fix, solving most of the issues. The actual - // question is: Shouldn't TClass provide a lock mechanism on update or lock - // itself until the update is done. - // - std::vector modifiedTClassesDiff(modifiedTClasses.size()); - std::vector::iterator it; - it = set_difference(modifiedTClasses.begin(), modifiedTClasses.end(), - ((TCling*)gCling)->GetModTClasses().begin(), - ((TCling*)gCling)->GetModTClasses().end(), - modifiedTClassesDiff.begin()); - modifiedTClassesDiff.resize(it - modifiedTClassesDiff.begin()); - - // Lock the TClass for updates - ((TCling*)gCling)->GetModTClasses().insert(modifiedTClassesDiff.begin(), - modifiedTClassesDiff.end()); - for (std::vector::const_iterator I = modifiedTClassesDiff.begin(), - E = modifiedTClassesDiff.end(); I != E; ++I) { - // Make sure the TClass has not been deleted. - if (!gROOT->GetListOfClasses()->FindObject(*I)) { - continue; - } - // Could trigger deserialization of decls. - cling::Interpreter::PushTransactionRAII RAII(interp); - // Unlock the TClass for updates - ((TCling*)gCling)->GetModTClasses().erase(*I); - - } + ((TCling*)gCling)->UpdateListsOnCommitted(T, interp); } extern "C" void TCling__UpdateListsOnUnloaded(const cling::Transaction &T) { - TGlobal *global = 0; - TFunction *function = 0; - TEnum* e = 0; - TListOfDataMembers* globals = (TListOfDataMembers*)gROOT->GetListOfGlobals(); - TListOfFunctions* functions = (TListOfFunctions*)gROOT->GetListOfGlobalFunctions(); - TListOfEnums* enums = (TListOfEnums*)gROOT->GetListOfEnums(); - for(cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end(); - I != E; ++I) - for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), - DE = I->m_DGR.end(); DI != DE; ++DI) { - if (isa(*DI) || isa(*DI)) { - clang::ValueDecl* VD = dyn_cast(*DI); - global = (TGlobal*)globals->FindObject(VD->getNameAsString().c_str()); - if (global && global->IsValid()) { - // Unload the global by setting the DataMemberInfo_t to 0 - globals->Unload(global); - global->Update(0); - } - } else if (isa(*DI) || isa(*DI)) { - const clang::NamedDecl* ND = dyn_cast(*DI); - if (ND) { - std::string buf = ND->getNameAsString(); - const char* name = buf.c_str(); - TClass* cl = TClass::GetClass(name); - if (cl) { - cl->ResetClassInfo(); - } - } - } else if (const FunctionDecl* FD = dyn_cast(*DI)) { - function = gROOT->GetGlobalFunction(FD->getNameAsString().c_str()); - if (function && function->IsValid()) { - functions->Unload(function); - function->Update(0); - } - } else if (const EnumDecl* ED = dyn_cast(*DI)) { - e = (TEnum*)enums->FindObject(ED->getNameAsString().c_str()); - if (e && e->IsValid()) { - TIter iEnumConst(e->GetConstants()); - while (TEnumConstant* enumConst = (TEnumConstant*)iEnumConst()) { - // Since the enum is already created and valid that ensures us that - // we have the enum constants created as well. - enumConst = (TEnumConstant*)globals->FindObject(enumConst->GetName()); - if (enumConst && enumConst->IsValid()) { - globals->Unload(enumConst); - enumConst->Update(0); - } - } - enums->Unload(e); - e->Update(0); - } - } - } + ((TCling*)gCling)->UpdateListsOnUnloaded(T); } extern "C" @@ -878,7 +741,8 @@ namespace{ TCling::TCling(const char *name, const char *title) : TInterpreter(name, title), fGlobalsListSerial(-1), fInterpreter(0), fMetaProcessor(0), fNormalizedCtxt(0), fPrevLoadedDynLibInfo(0), - fClingCallbacks(0), fHaveSinglePCM(kFALSE), fAutoLoadCallBack(0) + fClingCallbacks(0), fHaveSinglePCM(kFALSE), fAutoLoadCallBack(0), + fTransactionCount(0) { // Initialize the cling interpreter interface. @@ -2067,6 +1931,19 @@ void TCling::SetGetline(const char * (*getlineFunc)(const char* prompt), #endif } +//______________________________________________________________________________ +void TCling::HandleNewTransaction(const cling::Transaction &T) +{ + // Helper function to increase the internal Cling count of transactions + // that change the AST. + if ((std::distance(T.decls_begin(), T.decls_end()) != 1) + || T.deserialized_decls_begin() != T.deserialized_decls_end() + || T.macros_begin() != T.macros_end() + || ((!T.getFirstDecl().isNull()) && ((*T.getFirstDecl().begin()) != T.getWrapperFD()))) { + fTransactionCount++; + } +} + //______________________________________________________________________________ void TCling::RecursiveRemove(TObject* obj) { @@ -4483,6 +4360,162 @@ void TCling::UpdateAllCanvases() } } +//______________________________________________________________________________ +void TCling::UpdateListsOnCommitted(const cling::Transaction &T, + cling::Interpreter* interp) { + + std::set modifiedTClasses; // TClasses that require update after this transaction + + HandleNewTransaction(T); + + bool isTUTransaction = false; + if (T.decls_end()-T.decls_begin() == 1 && !T.hasNestedTransactions()) { + clang::Decl* FirstDecl = *(T.decls_begin()->m_DGR.begin()); + if (clang::TranslationUnitDecl* TU + = dyn_cast(FirstDecl)) { + // The is the first transaction, we have to expose to meta + // what's already in the AST. + isTUTransaction = true; + + // FIXME: don't load the world. Really, don't. Maybe + // instead smarten TROOT::GetListOfWhateveros() which + // currently is a THashList but could be a + // TInterpreterLookupCollection, one that reimplements + // TCollection::FindObject(name) and performs a lookup + // if not found in its T(Hash)List. + cling::Interpreter::PushTransactionRAII RAII(interp); + for (clang::DeclContext::decl_iterator TUI = TU->decls_begin(), + TUE = TU->decls_end(); TUI != TUE; ++TUI) + ((TCling*)gCling)->HandleNewDecl(*TUI, (*TUI)->isFromASTFile(),modifiedTClasses); + } + } + + std::set TransactionDeclSet; + if (!isTUTransaction && T.decls_end() - T.decls_begin()) { + const clang::Decl* WrapperFD = T.getWrapperFD(); + for (cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end(); + I != E; ++I) { + if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl) + continue; + + for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), + DE = I->m_DGR.end(); DI != DE; ++DI) { + if (*DI == WrapperFD) + continue; + TransactionDeclSet.insert(*DI); + ((TCling*)gCling)->HandleNewDecl(*DI, false, modifiedTClasses); + } + } + } + + // The above might trigger more decls to be deserialized. + // Thus the iteration over the deserialized decls must be last. + for (cling::Transaction::const_iterator I = T.deserialized_decls_begin(), + E = T.deserialized_decls_end(); I != E; ++I) { + for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), + DE = I->m_DGR.end(); DI != DE; ++DI) + if (TransactionDeclSet.find(*DI) == TransactionDeclSet.end()) { + //FIXME: HandleNewDecl should take DeclGroupRef + ((TCling*)gCling)->HandleNewDecl(*DI, /*isDeserialized*/true, + modifiedTClasses); + } + } + + + // When fully building the reflection info in TClass, a deserialization + // could be triggered, which may result in request for building the + // reflection info for the same TClass. This in turn will clear the caches + // for the TClass in-flight and cause null ptr derefs. + // FIXME: This is a quick fix, solving most of the issues. The actual + // question is: Shouldn't TClass provide a lock mechanism on update or lock + // itself until the update is done. + // + std::vector modifiedTClassesDiff(modifiedTClasses.size()); + std::vector::iterator it; + it = set_difference(modifiedTClasses.begin(), modifiedTClasses.end(), + ((TCling*)gCling)->GetModTClasses().begin(), + ((TCling*)gCling)->GetModTClasses().end(), + modifiedTClassesDiff.begin()); + modifiedTClassesDiff.resize(it - modifiedTClassesDiff.begin()); + + // Lock the TClass for updates + ((TCling*)gCling)->GetModTClasses().insert(modifiedTClassesDiff.begin(), + modifiedTClassesDiff.end()); + for (std::vector::const_iterator I = modifiedTClassesDiff.begin(), + E = modifiedTClassesDiff.end(); I != E; ++I) { + // Make sure the TClass has not been deleted. + if (!gROOT->GetListOfClasses()->FindObject(*I)) { + continue; + } + // Could trigger deserialization of decls. + cling::Interpreter::PushTransactionRAII RAII(interp); + // Unlock the TClass for updates + ((TCling*)gCling)->GetModTClasses().erase(*I); + + } +} + +//______________________________________________________________________________ +void TCling::UpdateListsOnUnloaded(const cling::Transaction &T) +{ + HandleNewTransaction(T); + + // Unload the objects from the lists and update the objects' state. + TGlobal *global = 0; + TFunction *function = 0; + TEnum* e = 0; + TListOfDataMembers* globals = (TListOfDataMembers*)gROOT->GetListOfGlobals(); + TListOfFunctions* functions = (TListOfFunctions*)gROOT->GetListOfGlobalFunctions(); + TListOfEnums* enums = (TListOfEnums*)gROOT->GetListOfEnums(); + for(cling::Transaction::const_iterator I = T.decls_begin(), E = T.decls_end(); + I != E; ++I) + for (DeclGroupRef::const_iterator DI = I->m_DGR.begin(), + DE = I->m_DGR.end(); DI != DE; ++DI) { + if (isa(*DI) || isa(*DI)) { + clang::ValueDecl* VD = dyn_cast(*DI); + global = (TGlobal*)globals->FindObject(VD->getNameAsString().c_str()); + if (global && global->IsValid()) { + // Unload the global by setting the DataMemberInfo_t to 0 + globals->Unload(global); + global->Update(0); + } + } else if (isa(*DI) || isa(*DI)) { + const clang::NamedDecl* ND = dyn_cast(*DI); + if (ND) { + std::string buf = ND->getNameAsString(); + const char* name = buf.c_str(); + TClass* cl = TClass::GetClass(name); + if (cl) { + cl->ResetClassInfo(); + } + } + } else if (const FunctionDecl* FD = dyn_cast(*DI)) { + function = gROOT->GetGlobalFunction(FD->getNameAsString().c_str()); + if (function && function->IsValid()) { + functions->Unload(function); + function->Update(0); + } + } else if (const EnumDecl* ED = dyn_cast(*DI)) { + e = (TEnum*)enums->FindObject(ED->getNameAsString().c_str()); + if (e && e->IsValid()) { + TIter iEnumConst(e->GetConstants()); + while (TEnumConstant* enumConst = (TEnumConstant*)iEnumConst()) { + // Since the enum is already created and valid that ensures us that + // we have the enum constants created as well. + enumConst = (TEnumConstant*)globals->FindObject(enumConst->GetName()); + if (enumConst) { + globals->Unload(enumConst); + enumConst->Update(0); + } + } + enums->Unload(e); + e->Update(0); + } + } + } +} + + //______________________________________________________________________________ const char* TCling::GetSharedLibs() { diff --git a/core/meta/src/TCling.h b/core/meta/src/TCling.h index 6dbfe460bf2de..9c22101c9a94b 100644 --- a/core/meta/src/TCling.h +++ b/core/meta/src/TCling.h @@ -124,6 +124,7 @@ class TCling : public TInterpreter { std::set fModTClasses; std::vector > fClassesToUpdate; void* fAutoLoadCallBack; + ULong64_t fTransactionCount; // Cling counter for commited or unloaded transactions which changed the AST. DeclId_t GetDeclId(const llvm::GlobalValue *gv) const; @@ -162,6 +163,7 @@ class TCling : public TInterpreter { TObjArray* GetRootMapFiles() const { return fRootmapFiles; } Bool_t HasDictionary(TClass* cl); void GetMissingDictionaries(TClass* cl, TObjArray& result, bool recurse); + unsigned long long GetInterpreterStateMarker() const { return fTransactionCount;} virtual void Initialize(); void InspectMembers(TMemberInspector&, const void* obj, const TClass* cl, Bool_t isTransient); Bool_t IsLoaded(const char* filename) const; @@ -471,6 +473,8 @@ class TCling : public TInterpreter { std::set& GetModTClasses() { return fModTClasses; } void HandleNewDecl(const void* DV, bool isDeserialized, std::set& modifiedClasses); + void UpdateListsOnCommitted(const cling::Transaction &T, cling::Interpreter* interp); + void UpdateListsOnUnloaded(const cling::Transaction &T); private: // Private Utility Functions TCling(); @@ -491,6 +495,8 @@ class TCling : public TInterpreter { bool InsertMissingDictionaryDecl(const clang::Decl* D, std::set &netD, clang::QualType qType, bool recurse); void InitRootmapFile(const char *name); int ReadRootmapFile(const char *rootmapfile); + void HandleNewTransaction(const cling::Transaction &T); + }; #endif diff --git a/core/meta/src/TDataMember.cxx b/core/meta/src/TDataMember.cxx index 5cbaa7e7dd904..bc719dcdab62d 100644 --- a/core/meta/src/TDataMember.cxx +++ b/core/meta/src/TDataMember.cxx @@ -717,7 +717,9 @@ Bool_t TDataMember::IsValid() // Return true if this data member object is pointing to a currently // loaded data member. If a function is unloaded after the TDataMember // is created, the TDataMember will be set to be invalid. - if (!fInfo) { + + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { DeclId_t newId = gInterpreter->GetDataMember(0, fName); if (newId) { DataMemberInfo_t *info = gInterpreter->DataMemberInfo_Factory(newId, 0); diff --git a/core/meta/src/TDictionary.cxx b/core/meta/src/TDictionary.cxx index 5f5196426c900..7b0b9ad544163 100644 --- a/core/meta/src/TDictionary.cxx +++ b/core/meta/src/TDictionary.cxx @@ -43,8 +43,10 @@ #include "TClassEdit.h" #include "TDataType.h" #include "TDictAttributeMap.h" +#include "TInterpreter.h" #include "TROOT.h" + ClassImp(TDictionary) TDictionary::TDictionary(const TDictionary& dict): @@ -92,3 +94,15 @@ TDictionary* TDictionary::GetDictionary(const type_info &typeinfo) return TClass::GetClass(typeinfo, true); } + +Bool_t TDictionary::InterpreterStateHasChanged() +{ + // Return true if there were any transactions that could have changed the + // state of the object. + ULong64_t currentTransaction = gInterpreter->GetInterpreterStateMarker(); + if (currentTransaction == fUpdatingTransactionCount) { + return false; + } + fUpdatingTransactionCount = currentTransaction; + return true; +} diff --git a/core/meta/src/TEnum.cxx b/core/meta/src/TEnum.cxx index de7f81e82eaf2..a353d5be9716d 100644 --- a/core/meta/src/TEnum.cxx +++ b/core/meta/src/TEnum.cxx @@ -19,18 +19,19 @@ #include "TEnumConstant.h" #include "TInterpreter.h" + ClassImp(TEnum) //______________________________________________________________________________ TEnum::TEnum(const char* name, void* info, TClass* cls) - : TNamed(name, "An enum type"), fInfo(info), fClass(cls) + :fInfo(info), fClass(cls) { //Constructor for TEnum class. //It take the name of the TEnum type, specification if it is global //and interpreter info. //Constant List is owner if enum not on global scope (thus constants not //in TROOT::GetListOfGlobals). - + SetNameTitle(name, "An enum type"); if (cls) { fConstantList.SetOwner(kTRUE); } @@ -57,10 +58,8 @@ Bool_t TEnum::IsValid() // loaded enum. If a enum is unloaded after the TEnum // is created, the TEnum will be set to be invalid. - if (!fInfo) { - //Check if the enum has not been declared again after unloading. - //FIXME: Slow lookup for the decl of the name. Check whether there has been - // a change in the AST. + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { DeclId_t newId = gInterpreter->GetEnum(fClass, fName); if (newId) { Update(newId); @@ -70,9 +69,17 @@ Bool_t TEnum::IsValid() return fInfo != 0; } +//______________________________________________________________________________ +Long_t TEnum::Property() const +{ + // Get property description word. For meaning of bits see EProperty. + + Long_t property = 0L; + return property |= kIsEnum; +} + //______________________________________________________________________________ void TEnum::Update(DeclId_t id) { fInfo = (void*)id; - } diff --git a/core/meta/src/TFunction.cxx b/core/meta/src/TFunction.cxx index eb98b87b736c5..3e39f2bc5486b 100644 --- a/core/meta/src/TFunction.cxx +++ b/core/meta/src/TFunction.cxx @@ -209,7 +209,9 @@ Bool_t TFunction::IsValid() // Return true if this function object is pointing to a currently // loaded function. If a function is unloaded after the TFunction // is created, the TFunction will be set to be invalid. - if(!fInfo) { + + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { // Only for global functions. For data member functions TMethod does it. DeclId_t newId = gInterpreter->GetFunction(0, fName); if (newId) { diff --git a/core/meta/src/TFunctionTemplate.cxx b/core/meta/src/TFunctionTemplate.cxx index 9d7be710491c3..ad1f402adf572 100644 --- a/core/meta/src/TFunctionTemplate.cxx +++ b/core/meta/src/TFunctionTemplate.cxx @@ -88,7 +88,9 @@ Bool_t TFunctionTemplate::IsValid() // Return true if this function template object is pointing to a currently // loaded function. If a function is unloaded after the TFunction // is created, the TFunction will be set to be invalid. - if(!fInfo) { + + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { // Only for global functions. For data member functions TMethod does it. DeclId_t newId = gInterpreter->GetFunction(0, fName); if (newId) { diff --git a/core/meta/src/TGlobal.cxx b/core/meta/src/TGlobal.cxx index 03ce55f505c51..350359fc6d412 100644 --- a/core/meta/src/TGlobal.cxx +++ b/core/meta/src/TGlobal.cxx @@ -129,7 +129,8 @@ Bool_t TGlobal::IsValid() // loaded global. If a global is unloaded after the TGlobal // is created, the TGlobal will be set to be invalid. - if (!fInfo) { + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { DeclId_t newId = gInterpreter->GetDataMember(0, fName); if (newId) { DataMemberInfo_t *info = gInterpreter->DataMemberInfo_Factory(newId, 0); diff --git a/core/meta/src/TMethod.cxx b/core/meta/src/TMethod.cxx index 0347cca2f054f..aa858a2a843cd 100644 --- a/core/meta/src/TMethod.cxx +++ b/core/meta/src/TMethod.cxx @@ -275,7 +275,9 @@ Bool_t TMethod::IsValid() // Return true if this function object is pointing to a currently // loaded function. If a function is unloaded after the TMethod // is created, the TMethod will be set to be invalid. - if(!fInfo) { + + // Register the transaction when checking the validity of the object. + if (!fInfo && InterpreterStateHasChanged()) { DeclId_t newId = gInterpreter->GetFunction(fClass->GetClassInfo(), fName); if (newId) { MethodInfo_t *info = gInterpreter->MethodInfo_Factory(newId); diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index d839c182e473b..15330eee83f8d 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -936,8 +936,7 @@ namespace cling { cling::Transaction* T = m_IncrParser->getLastTransaction(); if (InterpreterCallbacks* callbacks = getCallbacks()) callbacks->TransactionUnloaded(*T); - if (m_Executor) // we also might be in fsyntax-only mode. - m_Executor->runAndRemoveStaticDestructors(T); + m_Executor->runAndRemoveStaticDestructors(T); m_IncrParser->unloadTransaction(T); if (!--numberOfTransactions)