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