Revision 8feeafb4a9c7b2ad065ff7cb00db52255e287025 authored by dpiparo on 28 November 2023, 00:27:58 UTC, committed by Danilo Piparo on 28 November 2023, 00:59:47 UTC
1 parent 92a35a8
TClingMethodInfoTests.cxx
#include "TClass.h"
#include "TClassRef.h"
#include "TGlobal.h"
#include "TInterpreter.h"
#include "TList.h"
#include "TListOfFunctions.h"
#include "TMethod.h"
#include "TROOT.h"
#include "gtest/gtest.h"
TEST(TClingMethodInfo, Prototype)
{
TClass *cl = TClass::GetClass("TObject");
ASSERT_NE(cl, nullptr);
TMethod *meth = (TMethod *)cl->GetListOfMethods()->FindObject("SysError");
ASSERT_NE(meth, nullptr);
EXPECT_STREQ(meth->GetPrototype(), "void TObject::SysError(const char* method, const char* msgfmt,...) const");
}
TEST(TClingMethodInfo, ROOT10789)
{
gInterpreter->Declare(R"CODE(
namespace ROOT10789 {
struct EmptyStruct {
/*
EmptyStruct();
EmptyStruct(const EmptyStruct &);
EmptyStruct(EmptyStruct &&);
EmptyStruct & operator=(const EmptyStruct &);
EmptyStruct & operator=(EmptyStruct &&);
~EmptyStruct();
*/
};
class Base {
protected:
int protectedVar;
void protectedFunc();
void protectedFunc(float);
public:
int func();
int publicVar;
void publicFunc();
void publicFunc(int);
Base(int);
/*
Base(const Base &);
Base(Base &&);
Base &operator=(const Base &);
Base &operator=(Base &&);
~Base();
*/
};
class Derived: public Base {
private:
using Base::publicFunc; // Base::publicFunc(), Base::publicFunc(int)
protected:
using Base::func; // Base::func()
public:
Derived() = delete;
Derived(float);
using Base::Base; // Base::Base(int); SKIPPED: Base::Base(const Base &); Base::Base(Base &&);
using Base::protectedVar;
using Base::publicVar;
using Base::protectedFunc; // Base::protectedFunc(); Base::protectedFunc(float);
/*
Derived(const Derived &);
Derived(Derived &&);
Derived &operator=(const Derived &);
Derived &operator=(Derived &&);
~Derived();
*/
};
class Rederived: public Derived {
using Derived::protectedFunc;
};
struct BagOfThings {
union {
int a;
float b;
};
struct {
int var;
};
enum {
kEnumConst = 12
};
struct Nested {
void doNotFindMe();
};
protected:
int Deleted() = delete;
};
namespace EmptyNamespace {}
namespace SingleFuncNamespace {
int SingleFunc();
}
namespace SingleUsingDeclNamespace {
using SingleFuncNamespace::SingleFunc;
}
namespace WithInlineNamespace {
inline namespace InlineNamespace {
void InlineNamespaceFunc();
inline namespace AnotherInline {
void InlineInlineNamespaceFunc();
}
namespace Named {
void InlineNamedNamespaceFunc();
}
namespace {
int InAnon();
}
}
namespace {
int InAnon(int);
}
}
namespace Split {
}
namespace Split {
int One();
}
namespace Split {
int Two();
}
namespace Split {
}
namespace Split {
int Three();
}
namespace Split {
int Two(int);
}
}
struct BUG6359 {
template <class T>
BUG6359(int, T);
};
template <class T>
inline BUG6359::BUG6359(int ,T) {}
)CODE");
struct FuncInfo_t {
const char *fName;
int fNOverloads;
long fPropertyToTest;
};
auto checkClass = [](const char *className, int nMethods, std::vector<FuncInfo_t> funcInfos) {
TClass *cl = TClass::GetClass(className);
ASSERT_NE(cl, nullptr);
TListOfFunctions *methods = (TListOfFunctions *)cl->GetListOfMethods();
EXPECT_EQ(methods->GetSize(), nMethods) << "for scope " << className << (methods->ls(), "") << '\n';
for (auto &&funcInfo : funcInfos) {
TList *overloads = methods->GetListForObject(funcInfo.fName);
ASSERT_NE(overloads, nullptr);
EXPECT_EQ(overloads->GetSize(), funcInfo.fNOverloads)
<< "for " << className << "::" << funcInfo.fName << (overloads->ls(), "") << '\n';
for (auto meth : TRangeDynCast<TMethod>(overloads))
EXPECT_EQ(meth->Property() & funcInfo.fPropertyToTest, funcInfo.fPropertyToTest)
<< "for method " << meth->GetPrototype() << " in class " << className;
}
};
checkClass("ROOT10789::EmptyStruct", 6, {{"EmptyStruct", 3, kIsPublic}, {"~EmptyStruct", 1, kIsPublic}});
checkClass("ROOT10789::Base", 11,
{{"func", 1, kIsPublic}, {"protectedFunc", 2, kIsProtected}, {"Base", 3, kIsPublic}});
checkClass("ROOT10789::Derived", 12,
{{"publicFunc", 2, kIsPrivate},
{"protectedFunc", 2, kIsPublic},
{"publicFunc", 2, kIsPrivate},
{"Derived", 7 /*includes *all* ctors, even base-class-used*/, kIsPublic}});
checkClass("ROOT10789::Rederived", 7, {{"protectedFunc", 2, kIsPrivate}, {"Rederived", 3, kIsPublic}});
checkClass("ROOT10789::BagOfThings", 6, {});
checkClass("ROOT10789::EmptyNamespace", 0, {});
checkClass("ROOT10789::SingleFuncNamespace", 1, {{"SingleFunc", 1, 0}});
checkClass("ROOT10789::SingleUsingDeclNamespace", 1, {{"SingleFunc", 1, 0}});
checkClass("ROOT10789::WithInlineNamespace", 4,
{{"InlineNamespaceFunc", 1, 0}, {"InlineInlineNamespaceFunc", 1, 0}, {"InAnon", 2, 0}});
checkClass("ROOT10789::Split", 4, {{"One", 1, 0}, {"Two", 2, 0}, {"Three", 1, 0}});
checkClass("BUG6359", 5, {{"BUG6359", 2, 0}});
}
TEST(TClingMethodInfo, DerivedCtorROOT11010)
{
gInterpreter->Declare(R"CODE(
int ROOT11010baseMemCtorCalled = 0;
int ROOT11010baseMemDtorCalled = 0;
int ROOT11010baseCtorCalled = 0;
int ROOT11010baseDtorCalled = 0;
int ROOT11010derivMemCtorCalled = 0;
int ROOT11010derivMemDtorCalled = 0;
int ROOT11010derivDtorCalled = 0;
namespace ROOT11010 {
struct BaseMem {
int mem[4] = {17, 17, 17, 17};
BaseMem() { ++ROOT11010baseMemCtorCalled; }
~BaseMem() { ++ROOT11010baseMemDtorCalled; }
};
class Base {
BaseMem memB;
public:
Base(std::string s) {
++ROOT11010baseCtorCalled;
if (s != "TheStringCtorArg") {
std::cerr << "THE CTOR ARG IS WRONG!" << s << std::endl;
throw 8714659826;
}
}
virtual ~Base() {
++ROOT11010baseDtorCalled;
}
};
struct DerivMem {
double mem[3] = {42., 42., 42. };
DerivMem() { ++ROOT11010derivMemCtorCalled; }
~DerivMem() { ++ROOT11010derivMemDtorCalled; }
};
class Derived: public Base {
DerivMem memD;
public:
using Base::Base;
~Derived() override {
++ROOT11010derivDtorCalled;
}
};
}
)CODE");
auto GetInt = [](const char *name) {
auto *globals = gROOT->GetListOfGlobals();
std::string fullname("ROOT11010");
fullname += name;
return *(int *)((TGlobal *)globals->FindObject(fullname.c_str()))->GetAddress();
};
{
// ROOT-10789 take 2:
TClassRef c("ROOT11010::Base");
ASSERT_TRUE(c) << "Cannot find TClass for ROOT11010::Base";
TFunction *f = (TFunction *)c->GetListOfMethods()->FindObject("Base");
ASSERT_TRUE(f) << "Cannot find constructor for ROOT11010::Base";
CallFunc_t *callf = gInterpreter->CallFunc_Factory();
MethodInfo_t *meth = gInterpreter->MethodInfo_Factory(f->GetDeclId());
gInterpreter->CallFunc_SetFunc(callf, meth);
gInterpreter->MethodInfo_Delete(meth);
const TInterpreter::CallFuncIFacePtr_t &faceptr = gInterpreter->CallFunc_IFacePtr(callf);
gInterpreter->CallFunc_Delete(callf); // does not touch IFacePtr
EXPECT_EQ(GetInt("baseMemCtorCalled"), 0);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 0);
EXPECT_EQ(GetInt("baseCtorCalled"), 0);
EXPECT_EQ(GetInt("baseDtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 0);
EXPECT_EQ(GetInt("derivDtorCalled"), 0);
void *argbuf[8];
void *objresult = nullptr;
std::string s("TheStringCtorArg");
argbuf[0] = &s;
faceptr.fGeneric(0, 1, argbuf, &objresult);
EXPECT_NE(objresult, nullptr);
EXPECT_EQ(GetInt("baseMemCtorCalled"), 1);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 0);
EXPECT_EQ(GetInt("baseCtorCalled"), 1);
EXPECT_EQ(GetInt("baseDtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 0);
EXPECT_EQ(GetInt("derivDtorCalled"), 0);
c->Destructor(objresult);
EXPECT_EQ(GetInt("baseMemCtorCalled"), 1);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 1);
EXPECT_EQ(GetInt("baseCtorCalled"), 1);
EXPECT_EQ(GetInt("baseDtorCalled"), 1);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 0);
EXPECT_EQ(GetInt("derivDtorCalled"), 0);
}
{
TClassRef c("ROOT11010::Derived");
ASSERT_TRUE(c) << "Cannot find TClass for ROOT11010::Derived";
TFunction *f = (TFunction *)c->GetListOfMethods()->FindObject("Derived");
ASSERT_TRUE(f) << "Cannot find constructor for ROOT11010::Derived";
CallFunc_t *callf = gInterpreter->CallFunc_Factory();
MethodInfo_t *meth = gInterpreter->MethodInfo_Factory(f->GetDeclId());
gInterpreter->CallFunc_SetFunc(callf, meth);
gInterpreter->MethodInfo_Delete(meth);
// On Windows we get an assert, CodeGen-ing the callfunc for the construction:
// CGClass.cpp:488's CallBaseDtor::Emit fails to cast CFG.CurCodeDecl (the callfunc
// __cf2 function) into the expected destructor (i.e. CXXMethodDecl). I.e. something
// is wrong in the exception emission stack.
#if !defined(_MSC_VER) || defined(R__ENABLE_BROKEN_WIN_TESTS)
const TInterpreter::CallFuncIFacePtr_t &faceptr = gInterpreter->CallFunc_IFacePtr(callf);
gInterpreter->CallFunc_Delete(callf); // does not touch IFacePtr
EXPECT_EQ(GetInt("baseMemCtorCalled"), 1);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 1);
EXPECT_EQ(GetInt("baseCtorCalled"), 1);
EXPECT_EQ(GetInt("baseDtorCalled"), 1);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 0);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 0);
EXPECT_EQ(GetInt("derivDtorCalled"), 0);
void *argbuf[8];
void *objresult = nullptr;
std::string s("TheStringCtorArg");
argbuf[0] = &s;
faceptr.fGeneric(0, 1, argbuf, &objresult);
EXPECT_NE(objresult, nullptr);
EXPECT_EQ(GetInt("baseMemCtorCalled"), 2);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 1);
EXPECT_EQ(GetInt("baseCtorCalled"), 2);
EXPECT_EQ(GetInt("baseDtorCalled"), 1);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 1);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 0);
EXPECT_EQ(GetInt("derivDtorCalled"), 0);
c->Destructor(objresult);
EXPECT_EQ(GetInt("baseMemCtorCalled"), 2);
EXPECT_EQ(GetInt("baseMemDtorCalled"), 2);
EXPECT_EQ(GetInt("baseCtorCalled"), 2);
EXPECT_EQ(GetInt("baseDtorCalled"), 2);
EXPECT_EQ(GetInt("derivMemCtorCalled"), 1);
EXPECT_EQ(GetInt("derivMemDtorCalled"), 1);
EXPECT_EQ(GetInt("derivDtorCalled"), 1);
#endif
}
}
TEST(TClingMethodInfo, TemplateFun)
{
// PyROOT needs to know whether a ctor is a template, see clingwrapper.cxx:
// > // don't give in just yet, but rather get the full name through the symbol name,
// > // as eg. constructors do not receive their proper/full name from GetName().
// Also test "can we instantiate this function without extra type info"
// (see `Templates::MyMethods`).
gInterpreter->Declare(R"CODE(
struct TemplateFun {
template <class T>
TemplateFun(int, T);
template <class T>
int NonCtor(float, T);
};
class InheritTemplateFun: TemplateFun {
public:
using TemplateFun::TemplateFun;
};
namespace Templates {
template <class T> class TTArg;
template <class OUTER>
struct MyMethods {
template <class X> int Na();
template <class X> int Nb(X); // can be deduced but not enough for MethodInfo.
template <class A, class B = int> int Nc();
template <class X = OUTER, class... ARGS, class Y> int Nd();
template <class X = int> int Ya();
template <class...> int Yb();
template <int X = 12> int Yc();
template <template <class T> class X = TTArg> int Yd();
template <class X = OUTER> int Ye();
template <class X = OUTER> int Yf();
template <class X = OUTER, class... ARGS> int Yg();
template <class X = OUTER, class... ARGS, class Y = int> int Yh();
};
}
)CODE");
TClass *clTemplateFun = TClass::GetClass("TemplateFun");
ASSERT_NE(clTemplateFun, nullptr);
TFunction *funCtor = clTemplateFun->GetMethodWithPrototype("TemplateFun", "int, int");
ASSERT_NE(funCtor, nullptr);
EXPECT_EQ(funCtor->ExtraProperty() & kIsTemplateSpec, kIsTemplateSpec);
TFunction *funNonCtor = clTemplateFun->GetMethodWithPrototype("NonCtor", "float, int");
ASSERT_NE(funNonCtor, nullptr);
EXPECT_EQ(funNonCtor->ExtraProperty() & kIsTemplateSpec, kIsTemplateSpec);
TClass *clInhTemplateFun = TClass::GetClass("InheritTemplateFun");
ASSERT_NE(clInhTemplateFun, nullptr);
// Fails because LookupHelper doesn't know how to instantiate function templates,
// even though at least the function template is made available to the derived
// class as per using decl. This is issue #6481.
//TFunction *funInhCtor = clInhTemplateFun->GetMethodWithPrototype("InheritTemplateFun", "int, int");
//ASSERT_NE(funInhCtor, nullptr);
//EXPECT_EQ(funInhCtor->ExtraProperty() & kIsTemplateSpec, kIsTemplateSpec);
//EXPECT_EQ(funInhCtor->Property() & kIsPrivate, kIsPrivate);
// Doesn't work either, as GetListOfFunctionTemplates() ignores using decls.
// Issue #6482
// clInhTemplateFun->GetListOfFunctionTemplates(true)->ls(); // FindObject("InheritTemplateFun")-
TClass *clMyMethods = TClass::GetClass("Templates::MyMethods<int>");
ASSERT_NE(clMyMethods, nullptr);
TListOfFunctions *methods = (TListOfFunctions *)clMyMethods->GetListOfMethods();
ASSERT_NE(methods, nullptr);
std::set<std::string> names;
for (auto *obj: *methods)
names.insert(obj->GetName());
EXPECT_EQ(names.count("Na"), 0);
EXPECT_EQ(names.count("Nb"), 0);
EXPECT_EQ(names.count("Nc"), 0);
EXPECT_EQ(names.count("Nd"), 0);
//[1]: See comment in TClingCXXRecMethIter::InstantiateTemplateWithDefaults()
// handling parameter packs.
EXPECT_EQ(names.count("Ya<int>"), 1);
//EXPECT_EQ(names.count("Yb"), 1); //[1]
EXPECT_EQ(names.count("Yc<12>"), 1);
EXPECT_EQ(names.count("Yd<TTArg>"), 1);
EXPECT_EQ(names.count("Ye<int>"), 1);
EXPECT_EQ(names.count("Yf<int>"), 1);
//EXPECT_EQ(names.count("Yg<int>"), 1); //[1]
//EXPECT_EQ(names.count("Yh<int, int>"), 1); //[1]
// These do a lookup / overload set, and can materialize functions (like Yb)
// that the enumeration above failed to catch; see overriding
// `TListOfFunctions::FindObject()`.
EXPECT_EQ(methods->FindObject("Na"), nullptr);
EXPECT_EQ(methods->FindObject("Nb"), nullptr);
EXPECT_EQ(methods->FindObject("Nc"), nullptr);
EXPECT_EQ(methods->FindObject("Nd"), nullptr);
EXPECT_NE(methods->FindObject("Ya"), nullptr);
EXPECT_NE(methods->FindObject("Yb"), nullptr);
EXPECT_NE(methods->FindObject("Yc"), nullptr);
EXPECT_NE(methods->FindObject("Yd"), nullptr);
EXPECT_NE(methods->FindObject("Ye"), nullptr);
EXPECT_NE(methods->FindObject("Yf"), nullptr);
EXPECT_NE(methods->FindObject("Yg"), nullptr);
EXPECT_NE(methods->FindObject("Yh"), nullptr);
}
TEST(TClingMethodInfo, Ctors)
{
gInterpreter->Declare(R"CODE(
namespace BUG6578 {
struct Base1 {
Base1(int = 42) {}
protected:
Base1(const Base1&, std::string = "abc") {}
};
struct Base2 {
Base2() {}
Base2(const Base2&) {}
Base2(int, int) {}
};
class Derived: public Base1, protected Base2 {
private: // This is irrelevant - access of used ctors is defined by access of base class!
using Base1::Base1; // Base1(int), Base1(Base1&, string)
public: // This is irrelevant - access of used ctors is defined by access of base class!
using Base2::Base2; // Base2(int, int)
Derived(const Derived&) = delete;
// Derived()
};
}
)CODE");
TClassRef c("BUG6578::Derived");
ASSERT_TRUE(c) << "Cannot find TClass for BUG6578::Derived";
TListOfFunctions *methods = (TListOfFunctions*)c->GetListOfMethods();
ASSERT_TRUE(methods) << "Cannot find methods for BUG6578::Derived";
std::map<std::string, int /*properties*/> ctorsExpected {
{"(int = 42)", kIsPublic},
{"(const BUG6578::Base1&, string = \"abc\")", kIsProtected},
{"(int, int)", kIsProtected}
};
for (TMethod *meth: TRangeDynCast<TMethod>(*methods)) {
if (!(meth->ExtraProperty() & kIsConstructor))
continue;
auto iCtorExpected = ctorsExpected.find(meth->GetSignature());
EXPECT_FALSE(iCtorExpected == ctorsExpected.end())
<< "Unexpected constructor overload " << meth->GetPrototype();
if (iCtorExpected != ctorsExpected.end()) {
EXPECT_EQ(meth->Property() & iCtorExpected->second, iCtorExpected->second)
<< "Unexpected properties for " << meth->GetPrototype();
ctorsExpected.erase(iCtorExpected);
if (meth->Property() & kIsPublic) {
CallFunc_t *callf = gInterpreter->CallFunc_Factory();
MethodInfo_t *methInfo = gInterpreter->MethodInfo_Factory(meth->GetDeclId());
gInterpreter->CallFunc_SetFunc(callf, methInfo);
gInterpreter->MethodInfo_Delete(methInfo);
const TInterpreter::CallFuncIFacePtr_t &faceptr = gInterpreter->CallFunc_IFacePtr(callf);
gInterpreter->CallFunc_Delete(callf); // does not touch IFacePtr
EXPECT_NE(faceptr.fGeneric, nullptr);
}
}
}
EXPECT_EQ(ctorsExpected.size(), 0);
if (ctorsExpected.size()) {
std::cerr << "Expected constructors that were not found:\n";
for (auto &&iCtorExpected: ctorsExpected)
std::cerr << " " << iCtorExpected.first << '\n';
}
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...