ROOT  6.06/09
Reference Guide
TClingBaseClassInfo.cxx
Go to the documentation of this file.
1 // @(#)root/core/meta:$Id$
2 // Author: Paul Russo 30/07/2012
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TClingBaseClassInfo
13 
14 Emulation of the CINT BaseClassInfo class.
15 
16 The CINT C++ interpreter provides an interface to metadata about
17 the base classes of a class through the BaseClassInfo class. This
18 class provides the same functionality, using an interface as close
19 as possible to BaseClassInfo but the base class metadata comes from
20 the Clang C++ compiler, not CINT.
21 */
22 
23 #include "TClingBaseClassInfo.h"
24 
25 #include "TClingClassInfo.h"
26 #include "TDictionary.h"
27 #include "TMetaUtils.h"
28 
29 #include "TError.h"
30 
31 #include "cling/Interpreter/Interpreter.h"
32 #include "cling/Interpreter/Transaction.h"
33 
34 
35 #include "clang/AST/ASTContext.h"
36 #include "clang/AST/Decl.h"
37 #include "clang/AST/DeclCXX.h"
38 #include "clang/AST/PrettyPrinter.h"
39 #include "clang/AST/RecordLayout.h"
40 #include "clang/AST/Type.h"
41 #include "clang/AST/CXXInheritance.h"
42 
43 
44 #include "llvm/Support/Casting.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include "llvm/ExecutionEngine/ExecutionEngine.h"
47 #include "llvm/IR/Module.h"
48 
49 #include <string>
50 #include <sstream>
51 #include <iostream>
52 
53 using namespace llvm;
54 using namespace clang;
55 using namespace std;
56 
57 TClingBaseClassInfo::TClingBaseClassInfo(cling::Interpreter* interp,
58  TClingClassInfo* ci)
59  : fInterp(interp), fClassInfo(0), fFirstTime(true), fDescend(false),
60  fDecl(0), fIter(0), fBaseInfo(0), fOffset(0L), fClassInfoOwnership(true)
61 {
62  // Constructs a base class iterator on ci; ci == 0 means global scope (which
63  // is meaningless). The derived class info passed in as ci is copied.
64  if (!ci) {
65  fClassInfo = new TClingClassInfo(interp);
66  return;
67  }
68  fClassInfo = new TClingClassInfo(*ci);
69  if (!fClassInfo->GetDecl()) {
70  return;
71  }
72  const clang::CXXRecordDecl* CRD =
73  llvm::dyn_cast<clang::CXXRecordDecl>(fClassInfo->GetDecl());
74  if (!CRD) {
75  // We were initialized with something that is not a class.
76  // FIXME: We should prevent this from happening!
77  return;
78  }
79  fDecl = CRD;
80  fIter = CRD->bases_begin();
81 }
82 
83 TClingBaseClassInfo::TClingBaseClassInfo(cling::Interpreter* interp,
84  TClingClassInfo* derived,
85  TClingClassInfo* base)
86  : fInterp(interp), fClassInfo(0), fFirstTime(true), fDescend(false),
87  fDecl(0), fIter(0), fBaseInfo(0), fOffset(0L), fClassInfoOwnership(false)
88 {
89  // Constructs a single base class base (no iterator) of derived; derived must be != 0.
90  // The derived class info is referenced during the lifetime of the TClingBaseClassInfo.
91  if (!derived->GetDecl()) {
92  return;
93  }
94  const clang::CXXRecordDecl* CRD =
95  llvm::dyn_cast<clang::CXXRecordDecl>(derived->GetDecl());
96  const clang::CXXRecordDecl* BaseCRD =
97  llvm::dyn_cast<clang::CXXRecordDecl>(base->GetDecl());
98  if (!CRD || !BaseCRD) {
99  // We were initialized with something that is not a class.
100  // FIXME: We should prevent this from happening!
101  return;
102  }
103 
104  fClassInfo = derived;
105  fDecl = CRD;
106  //CRD->isDerivedFrom(BaseCRD, Paths);
107  // Check that base derives from derived.
108  clang::CXXBasePaths Paths;
109  if (!CRD->isDerivedFrom(BaseCRD, Paths)) {
110  //Not valid fBaseInfo = 0.
111  return;
112  }
113 
114  fBaseInfo = new TClingClassInfo(*base);
115  fIter = CRD->bases_end();
116 }
117 
119  : fInterp(rhs.fInterp), fClassInfo(0), fFirstTime(rhs.fFirstTime),
120  fDescend(rhs.fDescend), fDecl(rhs.fDecl), fIter(rhs.fIter), fBaseInfo(0),
121  fIterStack(rhs.fIterStack), fOffset(rhs.fOffset), fClassInfoOwnership(true)
122 {
123  // Copies a base class info including the base and derived class infos.
125  fBaseInfo = new TClingClassInfo(*rhs.fBaseInfo);
126 }
127 
129  const TClingBaseClassInfo& rhs)
130 {
131  if (this != &rhs) {
132  fInterp = rhs.fInterp;
134  delete fClassInfo;
136  fFirstTime = rhs.fFirstTime;
137  fDescend = rhs.fDescend;
138  fDecl = rhs.fDecl;
139  fIter = rhs.fIter;
140  delete fBaseInfo;
141  fBaseInfo = new TClingClassInfo(*rhs.fBaseInfo);
142  fIterStack = rhs.fIterStack;
143  fOffset = rhs.fOffset;
144  fClassInfoOwnership = true;
145  }
146  return *this;
147 }
148 
150 {
151  if (!IsValid()) {
152  return 0;
153  }
154  return fBaseInfo;
155 }
156 
159  TClingClassInfo* toBaseClass,
160  void* address, bool isDerivedObject) const
161 {
162  // Generate a function at run-time that would calculate the offset
163  // from the parameter derived class to the parameter toBase class for the
164  // address.
165 
166  // rootcling can trigger this, too, and without CodeGen we cannot use any
167  // offset calculation function.
168  if (fInterp->isInSyntaxOnlyMode())
169  return 0;
170 
171  // Get the dedcls for the two classes.
172  const clang::RecordDecl* fromDerivedDecl
173  = dyn_cast<clang::RecordDecl>(fromDerivedClass->GetDecl());
174  if (!fromDerivedDecl) {
175  Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
176  "Offset of non-class %s is ill-defined!", fromDerivedClass->Name());
177  return 0;
178  }
179  const clang::RecordDecl* toBaseDecl
180  = dyn_cast<clang::RecordDecl>(toBaseClass->GetDecl());
181  if (!toBaseDecl) {
182  Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
183  "Offset of non-class %s is ill-defined!", toBaseClass->Name());
184  return 0;
185  }
186 
187  // Make the wrapper name.
188  string wrapper_name;
189  {
190  ostringstream buf;
191  buf << "h" << fromDerivedDecl;
192  buf << '_';
193  buf << "h" << toBaseDecl;
194  wrapper_name = buf.str();
195  }
196  string code;
197  // Check whether the function was already generated.
198  if (!fInterp->getAddressOfGlobal(wrapper_name)) {
199  // Get the class or namespace name.
200  string fromDerivedClassName;
201  clang::QualType QTDerived(fromDerivedClass->GetType(), 0);
202  ROOT::TMetaUtils::GetFullyQualifiedTypeName(fromDerivedClassName,
203  QTDerived, *fInterp);
204  string toBase_class_name;
205  clang::QualType QTtoBase(toBaseClass->GetType(), 0);
207  QTtoBase, *fInterp);
208  // Write the wrapper code.
209  llvm::raw_string_ostream buf(code);
210  buf << "extern \"C\" long " + wrapper_name + "(void* address, bool isDerivedObject) {\n"
211  // If the object is not derived, will downcast to toBase first.
212  << " " << fromDerivedClassName << " *fromDerived;"
213  << " if (isDerivedObject) {"
214  << " fromDerived = (" << fromDerivedClassName << "*)address;\n"
215  << " } else {\n"
216  << " fromDerived = dynamic_cast<" << fromDerivedClassName << "*>((" << toBase_class_name << "*)address);\n"
217  << " }\n"
218  << " if (!fromDerived) {\n"
219  << " return -1; \n"
220  << " }\n"
221  << " " << toBase_class_name << " *toBase = fromDerived;\n"
222  << " return ((long)toBase - (long)fromDerived);\n}\n";
223  }
224 
225  // If we have a GV then compileFunction will use it; empty code is enough.
226  void* f = fInterp->compileFunction(wrapper_name, code, true /*ifUnique*/,
227  false /*withAccessControl*/);
228  if (!f) {
229  Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
230  "Compilation failed!");
231  return 0;
232  }
233 
234  return (OffsetPtrFunc_t) f;
235 }
236 
238 {
239  return
240  // inited with a valid class, and
241  fClassInfo->IsValid() &&
242  // the base class we are iterating over is valid, and
243  fDecl &&
244  // our current base has a TClingClassInfo, and
245  fBaseInfo &&
246  // our current base is a valid class
247  fBaseInfo->IsValid();
248 }
249 
251 {
252  // Exit early if the iterator is already invalid.
253  if (!fDecl || !fIter ||
254  (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end())) {
255  return 0;
256  }
257  // Advance to the next valid base.
258  while (1) {
259  // Advance the iterator.
260  if (fFirstTime) {
261  // The cint semantics are strange.
262  fFirstTime = false;
263  }
264  else if (!onlyDirect && fDescend) {
265  // We previously processed a base class which itself has bases,
266  // now we process the bases of that base class.
267 
268  // At least getASTRecordLayout() might deserialize.
269  cling::Interpreter::PushTransactionRAII RAII(fInterp);
270  fDescend = false;
271  const clang::RecordType *Ty = fIter->getType()->
272  getAs<clang::RecordType>();
273  // Note: We made sure this would work when we selected the
274  // base for processing.
275  clang::CXXRecordDecl *Base = llvm::cast<clang::CXXRecordDecl>(
276  Ty->getDecl()->getDefinition());
277  clang::ASTContext &Context = Base->getASTContext();
278  const clang::RecordDecl *RD = llvm::dyn_cast<clang::RecordDecl>(fDecl);
279  const clang::ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
280  int64_t offset = Layout.getBaseClassOffset(Base).getQuantity();
281  fOffset += static_cast<long>(offset);
282  fIterStack.push_back(std::make_pair(std::make_pair(fDecl, fIter),
283  static_cast<long>(offset)));
284  fDecl = Base;
285  fIter = Base->bases_begin();
286  }
287  else {
288  // Simple case, move on to the next base class specifier.
289  ++fIter;
290  }
291  // Fix it if we went past the end.
292  while (
293  (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end()) &&
294  fIterStack.size()
295  ) {
296  // All done with this base class.
297  fDecl = fIterStack.back().first.first;
298  fIter = fIterStack.back().first.second;
299  fOffset -= fIterStack.back().second;
300  fIterStack.pop_back();
301  ++fIter;
302  }
303  // Check for final termination.
304  if (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end()) {
305  // We have reached the end of the direct bases, all done.
306  delete fBaseInfo;
307  fBaseInfo = 0;
308  // Iterator is now invalid.
309  return 0;
310  }
311  // Check if current base class is a dependent type, that is, an
312  // uninstantiated template class.
313  const clang::TagType *Ty = fIter->getType()->getAs<clang::TagType>();
314  if (!Ty) {
315  // A dependent type (uninstantiated template), skip it.
316  continue;
317  }
318  // Check if current base class has a definition.
319  const clang::CXXRecordDecl *Base =
320  llvm::cast_or_null<clang::CXXRecordDecl>(Ty->getDecl()->
321  getDefinition());
322  if (!Base) {
323  // No definition yet (just forward declared), skip it.
324  continue;
325  }
326  // Now that we are going to return this base, check to see if
327  // we need to examine its bases next call.
328  if (!onlyDirect && Base->getNumBases()) {
329  fDescend = true;
330  }
331  // Update info for this base class.
332  delete fBaseInfo;
333  clang::QualType bType = ROOT::TMetaUtils::ReSubstTemplateArg(fIter->getType(),fClassInfo->GetType());
334  fBaseInfo = new TClingClassInfo(fInterp, *bType);
335  // Iterator is now valid.
336  return 1;
337  }
338 }
339 
340 int TClingBaseClassInfo::Next(int onlyDirect)
341 {
342  return InternalNext(onlyDirect);
343 }
344 
346 {
347  return Next(1);
348 }
349 
350 // This function is updating original one on http://clang.llvm.org/doxygen/CGExprCXX_8cpp_source.html#l01647
351 // To fit the needs.
352 static clang::CharUnits computeOffsetHint(clang::ASTContext &Context,
353  const clang::CXXRecordDecl *Src,
354  const clang::CXXRecordDecl *Dst,
355  cling::Interpreter* interp)
356 {
357  clang::CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
358  /*DetectVirtual=*/false);
359 
360  // If Dst is not derived from Src we can skip the whole computation below and
361  // return that Src is not a public base of Dst. Record all inheritance paths.
362  if (!Dst->isDerivedFrom(Src, Paths))
363  return clang::CharUnits::fromQuantity(-2ULL);
364 
365  unsigned NumPublicPaths = 0;
366  clang::CharUnits Offset;
367 
368  // Now walk all possible inheritance paths.
369  for (clang::CXXBasePaths::paths_iterator I = Paths.begin(), E = Paths.end();
370  I != E; ++I) {
371 
372  ++NumPublicPaths;
373 
374  for (clang::CXXBasePath::iterator J = I->begin(), JE = I->end(); J != JE; ++J) {
375  // If the path contains a virtual base class we can't give any hint.
376  // -1: no hint.
377  if (J->Base->isVirtual())
378  return clang::CharUnits::fromQuantity(-1ULL);
379 
380  if (NumPublicPaths > 1) // Won't use offsets, skip computation.
381  continue;
382 
383  // Accumulate the base class offsets.
384  cling::Interpreter::PushTransactionRAII RAII(interp);
385  const clang::ASTRecordLayout &L = Context.getASTRecordLayout(J->Class);
386  Offset += L.getBaseClassOffset(J->Base->getType()->getAsCXXRecordDecl());
387  }
388  }
389 
390  // -2: Src is not a public base of Dst.
391  if (NumPublicPaths == 0)
392  return clang::CharUnits::fromQuantity(-2ULL);
393 
394  // -3: Src is a multiple public base type but never a virtual base type.
395  if (NumPublicPaths > 1)
396  return clang::CharUnits::fromQuantity(-3ULL);
397 
398  // Otherwise, the Src type is a unique public nonvirtual base type of Dst.
399  // Return the offset of Src from the origin of Dst.
400  return Offset;
401  }
402 
403 ptrdiff_t TClingBaseClassInfo::Offset(void * address, bool isDerivedObject) const
404 {
405  // Compute the offset of the derived class to the base class.
406 
407  if (!IsValid()) {
408  return -1;
409  }
410  // Check if current base class has a definition.
411  const clang::CXXRecordDecl* Base =
412  llvm::cast_or_null<clang::CXXRecordDecl>(fBaseInfo->GetDecl());
413  if (!Base) {
414  // No definition yet (just forward declared), invalid.
415  return -1;
416  }
417  // If the base class has no virtual inheritance.
418  if (!(Property() & kIsVirtualBase)) {
419  clang::ASTContext& Context = Base->getASTContext();
420  const clang::CXXRecordDecl* RD = llvm::dyn_cast<clang::CXXRecordDecl>(fDecl);
421  if (!RD) {
422  // No RecordDecl for the class.
423  return -1;
424  }
425  long clang_val = computeOffsetHint(Context, Base, RD, fInterp).getQuantity();
426  if (clang_val == -2 || clang_val == -3) {
427  TString baseName;
428  TString derivedName;
429  {
430  // Need TNormalizedCtxt otherwise...
431  // Note: should we really be issuing a message here? Shouldn't
432  // the caller check and issue the message?
433  std::string buf;
434  PrintingPolicy Policy(fBaseInfo->GetDecl()->getASTContext().
435  getPrintingPolicy());
436  llvm::raw_string_ostream stream(buf);
437  ((const clang::NamedDecl*)fBaseInfo->GetDecl())
438  ->getNameForDiagnostic(stream, Policy, /*Qualified=*/true);
439  stream.flush();
440  baseName = buf;
441 
442  buf.clear();
443  ((const clang::NamedDecl*)fClassInfo->GetDecl())
444  ->getNameForDiagnostic(stream, Policy, /*Qualified=*/true);
445  stream.flush();
446  derivedName = buf;
447  }
448  if (clang_val == -2) {
449  Error("TClingBaseClassInfo::Offset",
450  "The class %s does not derive from the base %s.",
451  derivedName.Data(), baseName.Data());
452  } else {
453  // clang_val == -3
454  Error("TClingBaseClassInfo::Offset",
455  "There are multiple paths from derived class %s to base class %s.",
456  derivedName.Data(), baseName.Data());
457  }
458  clang_val = -1;
459  }
461  return clang_val;
462  }
463  // Verify the address of the instantiated object
464  if (!address) {
465  Error("TClingBaseClassInfo::Offset", "The address of the object for virtual base offset calculation is not valid.");
466  return -1;
467  }
468 
469  // Virtual inheritance case
470  OffsetPtrFunc_t executableFunc = GenerateBaseOffsetFunction(fClassInfo, fBaseInfo, address, isDerivedObject);
471  if (executableFunc) {
472  fClassInfo->AddBaseOffsetFunction(fBaseInfo->GetDecl(), executableFunc);
473  return (*executableFunc)(address, isDerivedObject);
474  }
475 
476  return -1;
477 }
478 
479 
481 {
482  if (!IsValid()) {
483  return 0L;
484  }
485  long property = 0L;
486 
487  if (fDecl == fClassInfo->GetDecl()) {
488  property |= kIsDirectInherit;
489  }
490 
491  const clang::CXXRecordDecl* CRD
492  = llvm::dyn_cast<CXXRecordDecl>(fDecl);
493  const clang::CXXRecordDecl* BaseCRD
494  = llvm::dyn_cast<CXXRecordDecl>(fBaseInfo->GetDecl());
495  if (!CRD || !BaseCRD) {
496  Error("TClingBaseClassInfo::Property",
497  "The derived class or the base class do not have a CXXRecordDecl.");
498  return property;
499  }
500 
501  clang::CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
502  /*DetectVirtual=*/true);
503  if (!CRD->isDerivedFrom(BaseCRD, Paths)) {
504  // Error really unexpected here, because construction / iteration guarantees
505  //inheritance;
506  Error("TClingBaseClassInfo", "Class not derived from given base.");
507  }
508  if (Paths.getDetectedVirtual()) {
509  property |= kIsVirtualBase;
510  }
511 
512  clang::AccessSpecifier AS = clang::AS_public;
513  // Derived: public Mid; Mid : protected Base: Derived inherits protected Base?
514  for (clang::CXXBasePaths::const_paths_iterator IB = Paths.begin(), EB = Paths.end();
515  AS != clang::AS_private && IB != EB; ++IB) {
516  switch (IB->Access) {
517  // keep AS unchanged?
518  case clang::AS_public: break;
519  case clang::AS_protected: AS = clang::AS_protected; break;
520  case clang::AS_private: AS = clang::AS_private; break;
521  case clang::AS_none: break;
522  }
523  }
524  switch (AS) {
525  case clang::AS_public:
526  property |= kIsPublic;
527  break;
528  case clang::AS_protected:
529  property |= kIsProtected;
530  break;
531  case clang::AS_private:
532  property |= kIsPrivate;
533  break;
534  case clang::AS_none:
535  // IMPOSSIBLE
536  break;
537  }
538  return property;
539 }
540 
542 {
543  if (!IsValid()) {
544  return -1L;
545  }
546  return fBaseInfo->Tagnum();
547 }
548 
550 {
551  if (!IsValid()) {
552  output.clear();
553  return;
554  }
555  fBaseInfo->FullName(output,normCtxt);
556 }
557 
558 const char* TClingBaseClassInfo::Name() const
559 {
560  if (!IsValid()) {
561  return 0;
562  }
563  return fBaseInfo->Name();
564 }
565 
567 {
568  if (!IsValid()) {
569  return 0;
570  }
571  return fBaseInfo->TmpltName();
572 }
573 
OffsetPtrFunc_t GenerateBaseOffsetFunction(const TClingClassInfo *derivedClass, TClingClassInfo *targetClass, void *address, bool isDerivedObject) const
RooCmdArg Offset(Bool_t flag=kTRUE)
void AddBaseOffsetValue(const clang::Decl *decl, ptrdiff_t offset)
Definition: TString.h:780
bool IsValid() const
const char * Name() const
RooArgList L(const RooAbsArg &v1)
std::vector< std::pair< std::pair< const clang::Decl *, clang::CXXRecordDecl::base_class_const_iterator >, long > > fIterStack
Small helper to keep current directory context.
static clang::CharUnits computeOffsetHint(clang::ASTContext &Context, const clang::CXXRecordDecl *Src, const clang::CXXRecordDecl *Dst, cling::Interpreter *interp)
const char * Name() const
clang::QualType ReSubstTemplateArg(clang::QualType input, const clang::Type *instance)
Check if 'input' or any of its template parameter was substituted when instantiating the class templa...
void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
Basic string class.
Definition: TString.h:137
TClingBaseClassInfo & operator=(const TClingBaseClassInfo &)
long Tagnum() const
STL namespace.
void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter)
ClassImp(TIterator) Bool_t TIterator return false
Compare two iterator objects.
Definition: TIterator.cxx:20
const clang::Type * GetType() const
TClingBaseClassInfo(cling::Interpreter *, TClingClassInfo *)
const char * Data() const
Definition: TString.h:349
ptrdiff_t(* OffsetPtrFunc_t)(void *, bool)
TClingClassInfo * fBaseInfo
int InternalNext(int onlyDirect)
void Error(const char *location, const char *msgfmt,...)
const char * TmpltName() const
const char * TmpltName() const
TClingClassInfo * GetBase() const
Emulation of the CINT BaseClassInfo class.
ptrdiff_t Offset(void *address=0, bool isDerivedObject=true) const
Double_t E()
Definition: TMath.h:54
const clang::Decl * GetDecl() const
double f(double x)
Definition: TCling.h:48
const clang::Decl * fDecl
void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
Emulation of the CINT ClassInfo class.
cling::Interpreter * fInterp
RooCmdArg Layout(Double_t xmin, Double_t xmax=0.99, Double_t ymin=0.95)
clang::CXXRecordDecl::base_class_const_iterator fIter
#define I(x, y, z)
static void output(int code)
Definition: gifencode.c:226
TClingClassInfo * fClassInfo
void AddBaseOffsetFunction(const clang::Decl *decl, OffsetPtrFunc_t func)