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