Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
Cppyy.cxx
Go to the documentation of this file.
1 // Bindings
2 #include "PyROOT.h"
3 #include "Cppyy.h"
4 #include "TCallContext.h"
5 
6 // ROOT
7 #include "TBaseClass.h"
8 #include "TClass.h"
9 #include "TClassRef.h"
10 #include "TClassTable.h"
11 #include "TClassEdit.h"
12 #include "TCollection.h"
13 #include "TDataMember.h"
14 #include "TDataType.h"
15 #include "TEnumConstant.h"
16 #include "TError.h"
17 #include "TFunction.h"
18 #include "TGlobal.h"
19 #include "TInterpreter.h"
20 #include "TList.h"
21 #include "TMethod.h"
22 #include "TMethodArg.h"
23 #include "TROOT.h"
24 
25 // Standard
26 #include <assert.h>
27 #include <map>
28 #include <set>
29 #include <sstream>
30 
31 // temp
32 #include <iostream>
33 // FIXME: Should refer to PyROOT::TParameter in the code.
34 #ifdef R__CXXMODULES
35  #define TParameter PyROOT::TParameter
36 #else
37  typedef PyROOT::TParameter TParameter;
38 #endif
39 // --temp
40 
41 
42 // small number that allows use of stack for argument passing
43 const int SMALL_ARGS_N = 8;
44 
45 
46 // data for life time management ---------------------------------------------
47 typedef std::vector< TClassRef > ClassRefs_t;
48 static ClassRefs_t g_classrefs( 1 );
49 static const ClassRefs_t::size_type GLOBAL_HANDLE = 1;
50 
51 typedef std::map< std::string, ClassRefs_t::size_type > Name2ClassRefIndex_t;
52 static Name2ClassRefIndex_t g_name2classrefidx;
53 
54 typedef std::map< Cppyy::TCppMethod_t, CallFunc_t* > Method2CallFunc_t;
55 static Method2CallFunc_t g_method2callfunc;
56 
57 typedef std::vector< TFunction > GlobalFuncs_t;
58 static GlobalFuncs_t g_globalfuncs;
59 
60 typedef std::vector< TGlobal* > GlobalVars_t;
61 static GlobalVars_t g_globalvars;
62 
63 // data ----------------------------------------------------------------------
64 Cppyy::TCppScope_t Cppyy::gGlobalScope = GLOBAL_HANDLE;
65 
66 // smart pointer types
67 static std::set< std::string > gSmartPtrTypes =
68  { "auto_ptr", "shared_ptr", "weak_ptr", "unique_ptr" };
69 
70 
71 // global initialization -----------------------------------------------------
72 namespace {
73 
74 class ApplicationStarter {
75 public:
76  ApplicationStarter() {
77  // Insure ROOT's atexit is executed *after* the atexit that calls
78  // ApplicationStarter's destructor, by forcing the ROOT's atexit
79  // registration now.
80  TROOT::Initialize();
81  // setup dummy holders for global and std namespaces
82  assert( g_classrefs.size() == GLOBAL_HANDLE );
83  g_name2classrefidx[ "" ] = GLOBAL_HANDLE;
84  g_classrefs.push_back(TClassRef(""));
85  // ROOT ignores std/::std, so point them to the global namespace
86  g_name2classrefidx[ "std" ] = GLOBAL_HANDLE;
87  g_name2classrefidx[ "::std" ] = GLOBAL_HANDLE;
88  // add a dummy global to refer to as null at index 0
89  g_globalvars.push_back( nullptr );
90  }
91 
92  ~ApplicationStarter() {
93  for ( auto ifunc : g_method2callfunc )
94  gInterpreter->CallFunc_Delete( ifunc.second );
95  }
96 } _applicationStarter;
97 
98 } // unnamed namespace
99 
100 
101 // local helpers -------------------------------------------------------------
102 static inline
103 TClassRef& type_from_handle( Cppyy::TCppScope_t scope )
104 {
105  assert( (ClassRefs_t::size_type) scope < g_classrefs.size() );
106  return g_classrefs[ (ClassRefs_t::size_type)scope ];
107 }
108 
109 // type_from_handle to go here
110 static inline
111 TFunction* type_get_method( Cppyy::TCppType_t klass, Cppyy::TCppIndex_t idx )
112 {
113  TClassRef& cr = type_from_handle( klass );
114  if ( cr.GetClass() )
115  return (TFunction*)cr->GetListOfMethods()->At( idx );
116  assert( klass == (Cppyy::TCppType_t)GLOBAL_HANDLE );
117  return (TFunction*)idx;
118 }
119 
120 static inline
121 Cppyy::TCppScope_t declaring_scope( Cppyy::TCppMethod_t method )
122 {
123  TMethod* m = dynamic_cast<TMethod*>( (TFunction*)method );
124  if ( m ) return Cppyy::GetScope( m->GetClass()->GetName() );
125  return (Cppyy::TCppScope_t)GLOBAL_HANDLE;
126 }
127 
128 
129 // name to opaque C++ scope representation -----------------------------------
130 Cppyy::TCppIndex_t Cppyy::GetNumScopes( TCppScope_t scope )
131 {
132  TClassRef& cr = type_from_handle( scope );
133  if ( cr.GetClass() ) return 0; // not supported if not at global scope
134  assert( scope == (TCppScope_t)GLOBAL_HANDLE );
135  return gClassTable->Classes();
136 }
137 
138 std::string Cppyy::GetScopeName( TCppScope_t parent, TCppIndex_t iscope )
139 {
140 // Retrieve the scope name of the scope indexed with iscope in parent.
141  TClassRef& cr = type_from_handle( parent );
142  if ( cr.GetClass() ) return 0; // not supported if not at global scope
143  assert( parent == (TCppScope_t)GLOBAL_HANDLE );
144  std::string name = gClassTable->At( iscope );
145  if ( name.find("::") == std::string::npos )
146  return name;
147  return "";
148 }
149 
150 std::string Cppyy::GetName( const std::string& name )
151 {
152  if( name.size() == 0) return name;
153  // need to deal with template paremeters that can have scopes themselves
154  Int_t tpl_open = 0;
155  for ( std::string::size_type pos = name.size() - 1; pos > 0; pos-- ) {
156  std::string::value_type c = name[pos];
157  // count '<' and '>' to be able to skip template contents
158  if ( c == '>' )
159  ++tpl_open;
160  else if ( c == '<' )
161  --tpl_open;
162  // by only checking for "::" the last part (class name) is dropped
163  else if ( tpl_open == 0 && c == ':'&& name[ pos - 1 ] == ':' ) {
164  // found a new scope part
165  return name.substr( pos+1 );
166  }
167  }
168  return name;
169 }
170 
171 std::string Cppyy::ResolveName( const std::string& cppitem_name )
172 {
173 // Fully resolve the given name to the final type name.
174  std::string tclean = TClassEdit::CleanType( cppitem_name.c_str() );
175 
176  TDataType* dt = gROOT->GetType( tclean.c_str() );
177  if ( dt ) return dt->GetFullTypeName();
178  return TClassEdit::ResolveTypedef( tclean.c_str(), true );
179 }
180 
181 std::string Cppyy::ResolveEnum(const TEnum* en)
182 {
183  if (en) {
184  auto ut = en->GetUnderlyingType();
185  if (ut != EDataType::kNumDataTypes)
186  return TDataType::GetTypeName(ut);
187  }
188  // Can't get type of enum, use int as default
189  return "int";
190 }
191 
192 std::string Cppyy::ResolveEnum(const std::string& enum_type)
193 {
194  return ResolveEnum(TEnum::GetEnum(enum_type.c_str()));
195 }
196 
197 Cppyy::TCppScope_t Cppyy::GetScope( const std::string& sname )
198 {
199  std::string scope_name;
200  if ( sname.find( "std::", 0, 5 ) == 0 )
201  scope_name = sname.substr( 5, std::string::npos );
202  else
203  scope_name = sname;
204 
205  scope_name = ResolveName( scope_name );
206  auto icr = g_name2classrefidx.find( scope_name );
207  if ( icr != g_name2classrefidx.end() )
208  return (TCppType_t)icr->second;
209 
210  // use TClass directly, to enable auto-loading
211  TClassRef cr( TClass::GetClass( scope_name.c_str(), kTRUE, kTRUE ) );
212  if ( !cr.GetClass() )
213  return (TCppScope_t)NULL;
214 
215  // no check for ClassInfo as forward declared classes are okay (fragile)
216 
217  ClassRefs_t::size_type sz = g_classrefs.size();
218  g_name2classrefidx[ scope_name ] = sz;
219  g_classrefs.push_back( TClassRef( scope_name.c_str() ) );
220  return (TCppScope_t)sz;
221 }
222 
223 Cppyy::TCppType_t Cppyy::GetTemplate( const std::string& /* template_name */ )
224 {
225  return (TCppType_t)0;
226 }
227 
228 Cppyy::TCppType_t Cppyy::GetActualClass( TCppType_t klass, TCppObject_t obj )
229 {
230  TClassRef& cr = type_from_handle( klass );
231  TClass* clActual = cr->GetActualClass( (void*)obj );
232  if ( clActual && clActual != cr.GetClass() ) {
233  // TODO: lookup through name should not be needed
234  return (TCppType_t)GetScope( clActual->GetName() );
235  }
236  return klass;
237 }
238 
239 size_t Cppyy::SizeOf( TCppType_t klass )
240 {
241  TClassRef& cr = type_from_handle( klass );
242  if ( cr.GetClass() ) return (size_t)cr->Size();
243  return (size_t)0;
244 }
245 
246 Bool_t Cppyy::IsBuiltin( const std::string& type_name )
247 {
248  TDataType* dt = gROOT->GetType( TClassEdit::CleanType( type_name.c_str(), 1 ).c_str() );
249  if ( dt ) return dt->GetType() != kOther_t;
250  return kFALSE;
251 }
252 
253 Bool_t Cppyy::IsComplete( const std::string& type_name )
254 {
255 // verify whether the dictionary of this class is fully available
256  Bool_t b = kFALSE;
257 
258  Int_t oldEIL = gErrorIgnoreLevel;
259  gErrorIgnoreLevel = 3000;
260  TClass* klass = TClass::GetClass( TClassEdit::ShortType( type_name.c_str(), 1 ).c_str() );
261  if ( klass && klass->GetClassInfo() ) // works for normal case w/ dict
262  b = gInterpreter->ClassInfo_IsLoaded( klass->GetClassInfo() );
263  else { // special case for forward declared classes
264  ClassInfo_t* ci = gInterpreter->ClassInfo_Factory( type_name.c_str() );
265  if ( ci ) {
266  b = gInterpreter->ClassInfo_IsLoaded( ci );
267  gInterpreter->ClassInfo_Delete( ci ); // we own the fresh class info
268  }
269  }
270  gErrorIgnoreLevel = oldEIL;
271  return b;
272 }
273 
274 // memory management ---------------------------------------------------------
275 Cppyy::TCppObject_t Cppyy::Allocate( TCppType_t type )
276 {
277  TClassRef& cr = type_from_handle( type );
278  return (TCppObject_t)malloc( cr->Size() );
279 }
280 
281 void Cppyy::Deallocate( TCppType_t /* type */, TCppObject_t instance )
282 {
283  free( instance );
284 }
285 
286 Cppyy::TCppObject_t Cppyy::Construct( TCppType_t type )
287 {
288  TClassRef& cr = type_from_handle( type );
289  return (TCppObject_t)cr->New();
290 }
291 
292 void Cppyy::Destruct( TCppType_t type, TCppObject_t instance )
293 {
294  TClassRef& cr = type_from_handle( type );
295  cr->Destructor( (void*)instance );
296 }
297 
298 
299 // method/function dispatching -----------------------------------------------
300 static inline ClassInfo_t* GetGlobalNamespaceInfo()
301 {
302  static ClassInfo_t* gcl = gInterpreter->ClassInfo_Factory();
303  return gcl;
304 }
305 
306 static CallFunc_t* GetCallFunc( Cppyy::TCppMethod_t method )
307 {
308  auto icf = g_method2callfunc.find( method );
309  if ( icf != g_method2callfunc.end() )
310  return icf->second;
311 
312  CallFunc_t* callf = nullptr;
313  TFunction* func = (TFunction*)method;
314  std::string callString = "";
315 
316 // create, if not cached
317  Cppyy::TCppScope_t scope = declaring_scope( method );
318  const TClassRef& klass = type_from_handle( scope );
319  if ( klass.GetClass() || (func && scope == GLOBAL_HANDLE) ) {
320  ClassInfo_t* gcl = klass.GetClass() ? klass->GetClassInfo() : nullptr;
321  if ( ! gcl )
322  gcl = GetGlobalNamespaceInfo();
323 
324  TCollection* method_args = func->GetListOfMethodArgs();
325  TIter iarg( method_args );
326 
327  TMethodArg* method_arg = 0;
328  while ((method_arg = (TMethodArg*)iarg.Next())) {
329  std::string fullType = method_arg->GetTypeNormalizedName();
330  if ( callString.empty() )
331  callString = fullType;
332  else
333  callString += ", " + fullType;
334  }
335 
336  Long_t offset = 0;
337  callf = gInterpreter->CallFunc_Factory();
338 
339  gInterpreter->CallFunc_SetFuncProto(
340  callf,
341  gcl,
342  func ? func->GetName() : klass->GetName(),
343  callString.c_str(),
344  func ? (func->Property() & kIsConstMethod) : kFALSE,
345  &offset,
346  ROOT::kExactMatch );
347 
348 // CLING WORKAROUND -- The number of arguments is not always correct (e.g. when there
349 // are default parameters, causing the callString to be wrong and
350 // the exact match to fail); or the method may have been inline or
351 // be compiler generated. In all those cases the exact match fails,
352 // whereas the conversion match sometimes works.
353  if ( ! gInterpreter->CallFunc_IsValid( callf ) ) {
354  gInterpreter->CallFunc_SetFuncProto(
355  callf,
356  gcl,
357  func ? func->GetName() : klass->GetName(),
358  callString.c_str(),
359  func ? (func->Property() & kIsConstMethod) : kFALSE,
360  &offset ); // <- no kExactMatch as that will fail
361  }
362 // -- CLING WORKAROUND
363 
364  }
365 
366  if ( !( callf && gInterpreter->CallFunc_IsValid( callf ) ) ) {
367  PyErr_Format( PyExc_RuntimeError, "could not resolve %s::%s(%s)",
368  const_cast<TClassRef&>(klass).GetClassName(),
369  func ? func->GetName() : const_cast<TClassRef&>(klass).GetClassName(),
370  callString.c_str() );
371  if ( callf ) gInterpreter->CallFunc_Delete( callf );
372  return nullptr;
373  }
374 
375  g_method2callfunc[ method ] = callf;
376  return callf;
377 }
378 
379 static inline void copy_args( void* args_, void** vargs ) {
380  std::vector<TParameter>& args = *(std::vector<TParameter>*)args_;
381  for ( std::vector<TParameter>::size_type i = 0; i < args.size(); ++i ) {
382  switch ( args[i].fTypeCode ) {
383  case 'l': /* long */
384  vargs[i] = (void*)&args[i].fValue.fLong;
385  break;
386  case 'f': /* double */
387  vargs[i] = (void*)&args[i].fValue.fFloat;
388  break;
389  case 'd': /* double */
390  vargs[i] = (void*)&args[i].fValue.fDouble;
391  break;
392  case 'D': /* long double */
393  vargs[i] = (void*)&args[i].fValue.fLongDouble;
394  break;
395  case 'k': /* long long */
396  case 'K': /* unsigned long long */
397  case 'U': /* unsigned long */
398  case 'p': /* void* */
399  vargs[i] = (void*)&args[i].fValue.fVoidp;
400  break;
401  case 'V': /* (void*)type& */
402  vargs[i] = args[i].fValue.fVoidp;
403  break;
404  case 'r': /* const type& */
405  vargs[i] = args[i].fRef;
406  break;
407  default:
408  std::cerr << "unknown type code: " << args[i].fTypeCode << std::endl;
409  break;
410  }
411  }
412 }
413 
414 Bool_t FastCall(
415  Cppyy::TCppMethod_t method, void* args_, void* self, void* result )
416 {
417  const std::vector<TParameter>& args = *(std::vector<TParameter>*)args_;
418 
419  CallFunc_t* callf = GetCallFunc( method );
420  if ( ! callf )
421  return kFALSE;
422 
423  TInterpreter::CallFuncIFacePtr_t faceptr = gCling->CallFunc_IFacePtr( callf );
424  if ( faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kGeneric ) {
425  if ( args.size() <= SMALL_ARGS_N ) {
426  void* smallbuf[SMALL_ARGS_N];
427  copy_args( args_, smallbuf );
428  faceptr.fGeneric( self, args.size(), smallbuf, result );
429  } else {
430  std::vector<void*> buf( args.size() );
431  copy_args( args_, buf.data() );
432  faceptr.fGeneric( self, args.size(), buf.data(), result );
433  }
434  return kTRUE;
435  }
436 
437  if ( faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kCtor ) {
438  if ( args.size() <= SMALL_ARGS_N ) {
439  void* smallbuf[SMALL_ARGS_N];
440  copy_args( args_, (void**)smallbuf );
441  faceptr.fCtor( (void**)smallbuf, result, args.size() );
442  } else {
443  std::vector<void*> buf( args.size() );
444  copy_args( args_, buf.data() );
445  faceptr.fCtor( buf.data(), result, args.size() );
446  }
447  return kTRUE;
448  }
449 
450  if ( faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kDtor ) {
451  std::cerr << " DESTRUCTOR NOT IMPLEMENTED YET! " << std::endl;
452  return kFALSE;
453  }
454 
455  return kFALSE;
456 }
457 
458 template< typename T >
459 static inline T CallT( Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, void* args )
460 {
461  T t{};
462  if ( FastCall( method, args, (void*)self, &t ) )
463  return t;
464  return (T)-1;
465 }
466 
467 #define CPPYY_IMP_CALL( typecode, rtype ) \
468 rtype Cppyy::Call##typecode( TCppMethod_t method, TCppObject_t self, void* args )\
469 { \
470  return CallT< rtype >( method, self, args ); \
471 }
472 
473 void Cppyy::CallV( TCppMethod_t method, TCppObject_t self, void* args )
474 {
475  if ( ! FastCall( method, args, (void*)self, nullptr ) )
476  return /* TODO ... report error */;
477 }
478 
479 CPPYY_IMP_CALL( B, UChar_t )
480 CPPYY_IMP_CALL( C, Char_t )
481 CPPYY_IMP_CALL( H, Short_t )
482 CPPYY_IMP_CALL( I, Int_t )
483 CPPYY_IMP_CALL( L, Long_t )
484 CPPYY_IMP_CALL( LL, Long64_t )
485 CPPYY_IMP_CALL( F, Float_t )
486 CPPYY_IMP_CALL( D, Double_t )
487 CPPYY_IMP_CALL( LD, LongDouble_t )
488 
489 void* Cppyy::CallR( TCppMethod_t method, TCppObject_t self, void* args )
490 {
491  void* r = nullptr;
492  if ( FastCall( method, args, (void*)self, &r ) )
493  return r;
494  return nullptr;
495 }
496 
497 Char_t* Cppyy::CallS( TCppMethod_t method, TCppObject_t self, void* args )
498 {
499  Char_t* s = nullptr;
500  if ( FastCall( method, args, (void*)self, &s ) )
501  return s;
502  return nullptr;
503 }
504 
505 Cppyy::TCppObject_t Cppyy::CallConstructor(
506  TCppMethod_t method, TCppType_t /* klass */, void* args ) {
507  void* obj = nullptr;
508  if ( FastCall( method, args, nullptr, &obj ) )
509  return (TCppObject_t)obj;
510  return (TCppObject_t)0;
511 }
512 
513 void Cppyy::CallDestructor( TCppType_t type, TCppObject_t self )
514 {
515  TClassRef& cr = type_from_handle( type );
516  cr->Destructor( (void*)self, kTRUE );
517 }
518 
519 Cppyy::TCppObject_t Cppyy::CallO( TCppMethod_t method,
520  TCppObject_t self, void* args, TCppType_t result_type )
521 {
522  TClassRef& cr = type_from_handle( result_type );
523  size_t s = gInterpreter->ClassInfo_Size(cr->GetClassInfo());
524  void* obj = malloc( s );
525  if ( FastCall( method, args, self, obj ) )
526  return (TCppObject_t)obj;
527  return (TCppObject_t)0;
528 }
529 
530 Cppyy::TCppMethPtrGetter_t Cppyy::GetMethPtrGetter(
531  TCppScope_t /* scope */, TCppIndex_t /* imeth */ )
532 {
533  return (TCppMethPtrGetter_t)0;
534 }
535 
536 
537 // handling of function argument buffer --------------------------------------
538 void* Cppyy::AllocateFunctionArgs( size_t nargs )
539 {
540  return new TParameter[nargs];
541 }
542 
543 void Cppyy::DeallocateFunctionArgs( void* args )
544 {
545  delete [] (TParameter*)args;
546 }
547 
548 size_t Cppyy::GetFunctionArgSizeof()
549 {
550  return sizeof( TParameter );
551 }
552 
553 size_t Cppyy::GetFunctionArgTypeoffset()\
554 {
555  return offsetof( TParameter, fTypeCode );
556 }
557 
558 
559 // scope reflection information ----------------------------------------------
560 Bool_t Cppyy::IsNamespace( TCppScope_t scope ) {
561 // Test if this scope represents a namespace.
562  if (scope == GLOBAL_HANDLE)
563  return kTRUE;
564 
565  TClassRef& cr = type_from_handle( scope );
566  if ( cr.GetClass() )
567  return cr->Property() & kIsNamespace;
568  return kFALSE;
569 }
570 
571 Bool_t Cppyy::IsAbstract( TCppType_t klass ) {
572 // Test if this type may not be instantiated.
573  TClassRef& cr = type_from_handle( klass );
574  if ( cr.GetClass() )
575  return cr->Property() & kIsAbstract;
576  return kFALSE;
577 }
578 
579 Bool_t Cppyy::IsEnum( const std::string& type_name ) {
580  return gInterpreter->ClassInfo_IsEnum( type_name.c_str() );
581 }
582 
583 
584 // class reflection information ----------------------------------------------
585 std::string Cppyy::GetFinalName( TCppType_t klass )
586 {
587  if ( klass == GLOBAL_HANDLE ) // due to CLING WORKAROUND in InitConverters_
588  return "";
589  // TODO: either this or GetScopedFinalName is wrong
590  TClassRef& cr = type_from_handle( klass );
591  return cr->GetName();
592 }
593 
594 std::string Cppyy::GetScopedFinalName( TCppType_t klass )
595 {
596  // TODO: either this or GetFinalName is wrong
597  TClassRef& cr = type_from_handle( klass );
598  return cr->GetName();
599 }
600 
601 Bool_t Cppyy::HasComplexHierarchy( TCppType_t /* handle */ )
602 {
603 // Always TRUE for now (pre-empts certain optimizations).
604  return kTRUE;
605 }
606 
607 Cppyy::TCppIndex_t Cppyy::GetNumBases( TCppType_t klass )
608 {
609 // Get the total number of base classes that this class has.
610  TClassRef& cr = type_from_handle( klass );
611  if ( cr.GetClass() && cr->GetListOfBases() != 0 )
612  return cr->GetListOfBases()->GetSize();
613  return 0;
614 }
615 
616 std::string Cppyy::GetBaseName( TCppType_t klass, TCppIndex_t ibase )
617 {
618  TClassRef& cr = type_from_handle( klass );
619  return ((TBaseClass*)cr->GetListOfBases()->At( ibase ))->GetName();
620 }
621 
622 Bool_t Cppyy::IsSubtype( TCppType_t derived, TCppType_t base )
623 {
624  if ( derived == base )
625  return kTRUE;
626  TClassRef& derived_type = type_from_handle( derived );
627  TClassRef& base_type = type_from_handle( base );
628  return derived_type->GetBaseClass( base_type ) != 0;
629 }
630 
631 void Cppyy::AddSmartPtrType( const std::string& type_name ) {
632  gSmartPtrTypes.insert( ResolveName( type_name ) );
633 }
634 
635 Bool_t Cppyy::IsSmartPtr( const std::string& type_name ) {
636 // checks if typename denotes a smart pointer
637 // TODO: perhaps make this stricter?
638  const std::string& real_name = ResolveName( type_name );
639  return gSmartPtrTypes.find(
640  real_name.substr( 0,real_name.find( "<" ) ) ) != gSmartPtrTypes.end();
641 }
642 
643 // type offsets --------------------------------------------------------------
644 ptrdiff_t Cppyy::GetBaseOffset( TCppType_t derived, TCppType_t base,
645  TCppObject_t address, int direction, bool rerror )
646 {
647 // calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0
648  if ( derived == base || !(base && derived) )
649  return (ptrdiff_t)0;
650 
651  TClassRef& cd = type_from_handle( derived );
652  TClassRef& cb = type_from_handle( base );
653 
654  if ( !cd.GetClass() || !cb.GetClass() )
655  return (ptrdiff_t)0;
656 
657  Long_t offset = -1;
658  if ( ! (cd->GetClassInfo() && cb->GetClassInfo()) ) { // gInterpreter requirement
659  // would like to warn, but can't quite determine error from intentional
660  // hiding by developers, so only cover the case where we really should have
661  // had a class info, but apparently don't:
662  if ( cd->IsLoaded() ) {
663  // warn to allow diagnostics
664  std::ostringstream msg;
665  msg << "failed offset calculation between " << cb->GetName() << " and " << cd->GetName();
666  PyErr_Warn( PyExc_RuntimeWarning, const_cast<char*>( msg.str().c_str() ) );
667  }
668 
669  // return -1 to signal caller NOT to apply offset
670  return rerror ? (ptrdiff_t)offset : 0;
671  }
672 
673  offset = gInterpreter->ClassInfo_GetBaseOffset(
674  cd->GetClassInfo(), cb->GetClassInfo(), (void*)address, direction > 0 );
675  if ( offset == -1 ) // Cling error, treat silently
676  return rerror ? (ptrdiff_t)offset : 0;
677 
678  return (ptrdiff_t)(direction < 0 ? -offset : offset);
679 }
680 
681 
682 // method/function reflection information ------------------------------------
683 Cppyy::TCppIndex_t Cppyy::GetNumMethods( TCppScope_t scope )
684 {
685  TClassRef& cr = type_from_handle( scope );
686  if ( cr.GetClass() && cr->GetListOfMethods() ) {
687  Cppyy::TCppIndex_t nMethods = (TCppIndex_t)cr->GetListOfMethods()->GetSize();
688  if ( nMethods == (TCppIndex_t)0 ) {
689  std::string clName = GetScopedFinalName( scope );
690  if ( clName.find( '<' ) != std::string::npos ) {
691  // chicken-and-egg problem: TClass does not know about methods until instantiation: force it
692  if ( TClass::GetClass( ("std::" + clName).c_str() ) )
693  clName = "std::" + clName;
694  std::ostringstream stmt;
695  stmt << "template class " << clName << ";";
696  gInterpreter->Declare( stmt.str().c_str() );
697  // now reload the methods
698  return (TCppIndex_t)cr->GetListOfMethods( kTRUE )->GetSize();
699  }
700  }
701  return nMethods;
702  } else if ( scope == (TCppScope_t)GLOBAL_HANDLE ) {
703  // enforce lazines by denying the existence of methods
704  return (TCppIndex_t)0;
705  }
706  return (TCppIndex_t)0;
707 }
708 
709 Cppyy::TCppIndex_t Cppyy::GetMethodIndexAt( TCppScope_t /* scope */, TCppIndex_t /* imeth */ )
710 {
711  return (TCppIndex_t)0;
712 }
713 
714 std::vector< Cppyy::TCppMethod_t > Cppyy::GetMethodsFromName(
715  TCppScope_t scope, const std::string& name, bool alsoInBases )
716 {
717 // TODO: this method assumes that the call for this name is made only
718 // once, and thus there is no need to store the results of the search
719 // in g_globalfuncs ... probably true, but needs verification
720  std::vector< TCppMethod_t > methods;
721  if ( scope == GLOBAL_HANDLE ) {
722  TCollection* funcs = gROOT->GetListOfGlobalFunctions( kTRUE );
723  g_globalfuncs.reserve(funcs->GetSize());
724 
725  TIter ifunc(funcs);
726 
727  TFunction* func = 0;
728  while ( (func = (TFunction*)ifunc.Next()) ) {
729  // cover not only direct matches, but also template matches
730  std::string fn = func->GetName();
731  if ( fn.rfind( name, 0 ) == 0 ) {
732  // either match exactly, or match the name as template
733  if ( (name.size() == fn.size()) ||
734  (name.size() < fn.size() && fn[name.size()] == '<') ) {
735  methods.push_back( (TCppMethod_t)func );
736  }
737  }
738  }
739  } else {
740  TClassRef& cr = type_from_handle( scope );
741  if ( cr.GetClass() ) {
742  // todo: handle overloads
743  TMethod* m = alsoInBases ? cr->GetMethodAllAny( name.c_str() ) : cr->GetMethodAny( name.c_str() );
744  if ( m ) methods.push_back( (TCppMethod_t)m );
745  }
746  }
747 
748  return methods;
749 }
750 
751 Cppyy::TCppMethod_t Cppyy::GetMethod( TCppScope_t scope, TCppIndex_t imeth )
752 {
753  TFunction* f = type_get_method( scope, imeth );
754  return (Cppyy::TCppMethod_t)f;
755 }
756 
757 std::string Cppyy::GetMethodName( TCppMethod_t method )
758 {
759  if ( method ) {
760  std::string name = ((TFunction*)method)->GetName();
761  //if ( IsMethodTemplate( method ) )
762  // return name.substr( 0, name.find('<') );
763  return name;
764  }
765  return "<unknown>";
766 }
767 
768 std::string Cppyy::GetMethodResultType( TCppMethod_t method )
769 {
770  if ( method ) {
771  TFunction* f = (TFunction*)method;
772  if ( f->ExtraProperty() & kIsConstructor )
773  return "constructor";
774  return f->GetReturnTypeNormalizedName();
775  }
776  return "<unknown>";
777 }
778 
779 Cppyy::TCppIndex_t Cppyy::GetMethodNumArgs( TCppMethod_t method )
780 {
781  if ( method )
782  return ((TFunction*)method)->GetNargs();
783  return 0;
784 }
785 
786 Cppyy::TCppIndex_t Cppyy::GetMethodReqArgs( TCppMethod_t method )
787 {
788  if ( method ) {
789  TFunction* f = (TFunction*)method;
790  return (TCppIndex_t)(f->GetNargs() - f->GetNargsOpt());
791  }
792  return (TCppIndex_t)0;
793 }
794 
795 std::string Cppyy::GetMethodArgName( TCppMethod_t method, int iarg )
796 {
797  if ( method ) {
798  TFunction* f = (TFunction*)method;
799  TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At( iarg );
800  return arg->GetName();
801  }
802  return "<unknown>";
803 }
804 
805 std::string Cppyy::GetMethodArgType( TCppMethod_t method, int iarg )
806 {
807  if ( method ) {
808  TFunction* f = (TFunction*)method;
809  TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At( iarg );
810  return arg->GetTypeNormalizedName();
811  }
812  return "<unknown>";
813 }
814 
815 std::string Cppyy::GetMethodArgDefault( TCppMethod_t method, int iarg )
816 {
817  if ( method ) {
818  TFunction* f = (TFunction*)method;
819  TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At( iarg );
820  const char* def = arg->GetDefault();
821  if ( def )
822  return def;
823  }
824 
825  return "";
826 }
827 
828 std::string Cppyy::GetMethodSignature( TCppScope_t /* scope */, TCppIndex_t /* imeth */ )
829 {
830  return "<unknown>";
831 }
832 
833 Bool_t Cppyy::IsConstMethod( TCppMethod_t method )
834 {
835  if ( method ) {
836  TFunction* f = (TFunction*)method;
837  return f->Property() & kIsConstMethod;
838  }
839  return kFALSE;
840 }
841 
842 
843 bool Cppyy::ExistsMethodTemplate(TCppScope_t scope, const std::string& name)
844 {
845  if (scope == (TCppScope_t)GLOBAL_HANDLE) {
846  return (bool)gROOT->GetFunctionTemplate(name.c_str());
847  } else {
848  TClassRef& cr = type_from_handle(scope);
849  if (cr.GetClass())
850  return (bool)cr->GetFunctionTemplate(name.c_str());
851  }
852 
853  return false;
854 }
855 
856 Cppyy::TCppMethod_t Cppyy::GetMethodTemplate(
857  TCppScope_t scope, const std::string& name, const std::string& proto)
858 {
859  if (scope == (TCppScope_t)GLOBAL_HANDLE) {
860  return (TCppMethod_t)gROOT->GetGlobalFunctionWithPrototype(name.c_str(), proto.c_str());
861  } else {
862  TClassRef& cr = type_from_handle(scope);
863  if (cr.GetClass())
864  return (TCppMethod_t)cr->GetMethodWithPrototype(name.c_str(), proto.c_str());
865  }
866 
867  return (TCppMethod_t)nullptr;
868 }
869 
870 Bool_t Cppyy::IsMethodTemplate( TCppMethod_t method )
871 {
872  if ( method ) {
873  TFunction* f = (TFunction*)method;
874  std::string name = f->GetName();
875  return (name[name.size()-1] == '>') && (name.find('<') != std::string::npos);
876  }
877  return kFALSE;
878 }
879 
880 Cppyy::TCppIndex_t Cppyy::GetMethodNumTemplateArgs(
881  TCppScope_t /* scope */, TCppIndex_t /* imeth */ )
882 {
883  return (TCppIndex_t)0;
884 }
885 
886 std::string Cppyy::GetMethodTemplateArgName(
887  TCppScope_t /* scope */, TCppIndex_t /* imeth */, TCppIndex_t /* iarg */ )
888 {
889  return "<unknown>";
890 }
891 
892 Cppyy::TCppIndex_t Cppyy::GetGlobalOperator(
893  TCppScope_t /* scope */, TCppType_t /* lc */, TCppType_t /* rc */, const std::string& /* op */ )
894 {
895  return (TCppIndex_t)0;
896 }
897 
898 // method properties ---------------------------------------------------------
899 Bool_t Cppyy::IsConstructor( TCppMethod_t method )
900 {
901  if ( method ) {
902  TFunction* f = (TFunction*)method;
903  return f->ExtraProperty() & kIsConstructor;
904  }
905  return kFALSE;
906 }
907 
908 Bool_t Cppyy::IsPublicMethod( TCppMethod_t method )
909 {
910  if ( method ) {
911  TFunction* f = (TFunction*)method;
912  return f->Property() & kIsPublic;
913  }
914  return kFALSE;
915 }
916 
917 Bool_t Cppyy::IsStaticMethod( TCppMethod_t method )
918 {
919  if ( method ) {
920  TFunction* f = (TFunction*)method;
921  return f->Property() & kIsStatic;
922  }
923  return kFALSE;
924 }
925 
926 // data member reflection information ----------------------------------------
927 Cppyy::TCppIndex_t Cppyy::GetNumDatamembers( TCppScope_t scope )
928 {
929  TClassRef& cr = type_from_handle( scope );
930  if ( cr.GetClass() && cr->GetListOfDataMembers() )
931  return cr->GetListOfDataMembers()->GetSize();
932  else if ( scope == (TCppScope_t)GLOBAL_HANDLE ) {
933  std::cerr << " global data should be retrieved lazily " << std::endl;
934  TCollection* vars = gROOT->GetListOfGlobals( kTRUE );
935  if ( g_globalvars.size() != (GlobalVars_t::size_type)vars->GetSize() ) {
936  g_globalvars.clear();
937  g_globalvars.reserve(vars->GetSize());
938 
939  TIter ivar(vars);
940 
941  TGlobal* var = 0;
942  while ( (var = (TGlobal*)ivar.Next()) )
943  g_globalvars.push_back( var );
944  }
945  return (TCppIndex_t)g_globalvars.size();
946  }
947  return (TCppIndex_t)0;
948 }
949 
950 std::string Cppyy::GetDatamemberName( TCppScope_t scope, TCppIndex_t idata )
951 {
952  TClassRef& cr = type_from_handle( scope );
953  if (cr.GetClass()) {
954  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
955  return m->GetName();
956  }
957  assert( scope == (TCppScope_t)GLOBAL_HANDLE );
958  TGlobal* gbl = g_globalvars[ idata ];
959  return gbl->GetName();
960 }
961 
962 std::string Cppyy::GetDatamemberType( TCppScope_t scope, TCppIndex_t idata )
963 {
964  if ( scope == GLOBAL_HANDLE ) {
965  TGlobal* gbl = g_globalvars[ idata ];
966  std::string fullType = gbl->GetFullTypeName();
967  if ( fullType[fullType.size()-1] == '*' && \
968  fullType.find( "char", 0, 4 ) == std::string::npos )
969  fullType.append( "*" );
970  else if ( (int)gbl->GetArrayDim() > 1 )
971  fullType.append( "*" );
972  else if ( (int)gbl->GetArrayDim() == 1 ) {
973  std::ostringstream s;
974  s << '[' << gbl->GetMaxIndex( 0 ) << ']' << std::ends;
975  fullType.append( s.str() );
976  }
977  return fullType;
978  }
979 
980  TClassRef& cr = type_from_handle( scope );
981  if ( cr.GetClass() ) {
982  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
983  std::string fullType = m->GetTrueTypeName();
984  if ( (int)m->GetArrayDim() > 1 || (!m->IsBasic() && m->IsaPointer()) )
985  fullType.append( "*" );
986  else if ( (int)m->GetArrayDim() == 1 ) {
987  std::ostringstream s;
988  s << '[' << m->GetMaxIndex( 0 ) << ']' << std::ends;
989  fullType.append( s.str() );
990  }
991  return fullType;
992  }
993 
994  return "<unknown>";
995 }
996 
997 ptrdiff_t Cppyy::GetDatamemberOffset( TCppScope_t scope, TCppIndex_t idata )
998 {
999  if ( scope == GLOBAL_HANDLE ) {
1000  TGlobal* gbl = g_globalvars[ idata ];
1001  return (ptrdiff_t)gbl->GetAddress();
1002  }
1003 
1004  TClassRef& cr = type_from_handle( scope );
1005  if ( cr.GetClass() ) {
1006  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1007  return (ptrdiff_t)m->GetOffsetCint(); // yes, CINT ...
1008  }
1009 
1010  return (ptrdiff_t)0;
1011 }
1012 
1013 Cppyy::TCppIndex_t Cppyy::GetDatamemberIndex( TCppScope_t scope, const std::string& name )
1014 {
1015  if ( scope == GLOBAL_HANDLE ) {
1016  TGlobal* gb = (TGlobal*)gROOT->GetListOfGlobals( kTRUE )->FindObject( name.c_str() );
1017  if ( gb && gb->GetAddress() && gb->GetAddress() != (void*)-1 ) {
1018  g_globalvars.push_back( gb );
1019  return g_globalvars.size() - 1;
1020  }
1021 
1022  } else {
1023  TClassRef& cr = type_from_handle( scope );
1024  if ( cr.GetClass() ) {
1025  TDataMember* dm =
1026  (TDataMember*)cr->GetListOfDataMembers()->FindObject( name.c_str() );
1027  // TODO: turning this into an index is silly ...
1028  if ( dm ) return (TCppIndex_t)cr->GetListOfDataMembers()->IndexOf( dm );
1029  }
1030  }
1031 
1032  return (TCppIndex_t)-1;
1033 }
1034 
1035 
1036 // data member properties ----------------------------------------------------
1037 Bool_t Cppyy::IsPublicData( TCppScope_t scope, TCppIndex_t idata )
1038 {
1039  if ( scope == GLOBAL_HANDLE )
1040  return kTRUE;
1041  TClassRef& cr = type_from_handle( scope );
1042  if ( cr->Property() & kIsNamespace )
1043  return kTRUE;
1044  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1045  return m->Property() & kIsPublic;
1046 }
1047 
1048 Bool_t Cppyy::IsStaticData( TCppScope_t scope, TCppIndex_t idata )
1049 {
1050  if ( scope == GLOBAL_HANDLE )
1051  return kTRUE;
1052  TClassRef& cr = type_from_handle( scope );
1053  if ( cr->Property() & kIsNamespace )
1054  return kTRUE;
1055  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1056  return m->Property() & kIsStatic;
1057 }
1058 
1059 Bool_t Cppyy::IsConstData( TCppScope_t scope, TCppIndex_t idata )
1060 {
1061  if ( scope == GLOBAL_HANDLE ) {
1062  TGlobal* gbl = g_globalvars[ idata ];
1063  return gbl->Property() & kIsConstant;
1064  }
1065  TClassRef& cr = type_from_handle( scope );
1066  if ( cr.GetClass() ) {
1067  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1068  return m->Property() & kIsConstant;
1069  }
1070  return kFALSE;
1071 }
1072 
1073 Bool_t Cppyy::IsEnumData( TCppScope_t scope, TCppIndex_t idata )
1074 {
1075  if ( scope == GLOBAL_HANDLE ) {
1076  TGlobal* gbl = g_globalvars[ idata ];
1077  return gbl->Property() & kIsEnum;
1078  }
1079  TClassRef& cr = type_from_handle( scope );
1080  if ( cr.GetClass() ) {
1081  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1082  return m->Property() & kIsEnum;
1083  }
1084  return kFALSE;
1085 }
1086 
1087 Int_t Cppyy::GetDimensionSize( TCppScope_t scope, TCppIndex_t idata, int dimension )
1088 {
1089  if ( scope == GLOBAL_HANDLE ) {
1090  TGlobal* gbl = g_globalvars[ idata ];
1091  return gbl->GetMaxIndex( dimension );
1092  }
1093  TClassRef& cr = type_from_handle( scope );
1094  if ( cr.GetClass() ) {
1095  TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At( idata );
1096  return m->GetMaxIndex( dimension );
1097  }
1098  return (Int_t)-1;
1099 }
1100 
1101 // enum properties -----------------------------------------------------------
1102 Cppyy::TCppEnum_t Cppyy::GetEnum(TCppScope_t scope, const std::string& enum_name)
1103 {
1104  if (scope == GLOBAL_HANDLE)
1105  return (TCppEnum_t)gROOT->GetListOfEnums(kTRUE)->FindObject(enum_name.c_str());
1106 
1107  TClassRef& cr = type_from_handle(scope);
1108  if (cr.GetClass())
1109  return (TCppEnum_t)cr->GetListOfEnums(kTRUE)->FindObject(enum_name.c_str());
1110 
1111  return (TCppEnum_t)0;
1112 }
1113 
1114 Cppyy::TCppIndex_t Cppyy::GetNumEnumData(TCppEnum_t etype)
1115 {
1116  return (TCppIndex_t)((TEnum*)etype)->GetConstants()->GetSize();
1117 }
1118 
1119 std::string Cppyy::GetEnumDataName(TCppEnum_t etype, TCppIndex_t idata)
1120 {
1121  return ((TEnumConstant*)((TEnum*)etype)->GetConstants()->At(idata))->GetName();
1122 }
1123 
1124 long long Cppyy::GetEnumDataValue(TCppEnum_t etype, TCppIndex_t idata)
1125 {
1126  TEnumConstant* ecst = (TEnumConstant*)((TEnum*)etype)->GetConstants()->At(idata);
1127  return (long long)ecst->GetValue();
1128 }