Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
BaseSelectionRule.cxx
Go to the documentation of this file.
1 // @(#)root/core/utils:$Id: BaseSelectionRule.cxx 41697 2011-11-01 21:03:41Z pcanal $
2 // Author: Velislava Spasova September 2010
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2011, 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 //////////////////////////////////////////////////////////////////////////
13 // //
14 // BaseSelectionRule //
15 // //
16 // Base selection class from which all //
17 // selection classes should be derived //
18 // //
19 //////////////////////////////////////////////////////////////////////////
20 
21 #include "BaseSelectionRule.h"
22 
23 #include "TClassEdit.h"
24 #include "TClingUtils.h"
25 
26 #include <iostream>
27 #include <string.h>
28 #include <cctype>
29 
30 #include "clang/Basic/SourceLocation.h"
31 #include "clang/Basic/SourceManager.h"
32 #include "clang/AST/DeclCXX.h"
33 #include "clang/AST/ASTContext.h"
34 #include "clang/AST/DeclTemplate.h"
35 
36 #ifdef _WIN32
37 #include "process.h"
38 #endif
39 #include <sys/stat.h>
40 
41 static const char *R__GetDeclSourceFileName(const clang::Decl* D)
42 {
43  clang::ASTContext& ctx = D->getASTContext();
44  clang::SourceManager& SM = ctx.getSourceManager();
45  clang::SourceLocation SL = D->getLocation();
46  // If the class decl is the result of a macpo expansion, take the location
47  // where the macro is "invoked" i.e. expanded at (ExpansionLoc), not the
48  // spelling location (where the delc's tokens come from).
49  if (SL.isMacroID())
50  SL = SM.getExpansionLoc(SL);
51 
52  if (SL.isValid() && SL.isFileID()) {
53  clang::PresumedLoc PLoc = SM.getPresumedLoc(SL);
54  return PLoc.getFilename();
55  }
56  else {
57  return "invalid";
58  }
59 }
60 
61 static bool R__match_filename(const char *srcname,const char *filename)
62 {
63  if (srcname==0) {
64  return false;
65  }
66  if((strcmp(srcname,filename)==0)) {
67  return true;
68  }
69 
70 #ifdef G__WIN32
71  char i1name[_MAX_PATH];
72  char fullfile[_MAX_PATH];
73  _fullpath( i1name, srcname, _MAX_PATH );
74  _fullpath( fullfile, filename, _MAX_PATH );
75  if((stricmp(i1name, fullfile)==0)) return 1;
76 #else
77  struct stat statBufItem;
78  struct stat statBuf;
79  if ( ( 0 == stat( filename, & statBufItem ) )
80  && ( 0 == stat( srcname, & statBuf ) )
81  && ( statBufItem.st_dev == statBuf.st_dev ) // Files on same device
82  && ( statBufItem.st_ino == statBuf.st_ino ) // Files on same inode (but this is not unique on AFS so we need the next 2 test
83  && ( statBufItem.st_size == statBuf.st_size ) // Files of same size
84  && ( statBufItem.st_mtime == statBuf.st_mtime ) // Files modified at the same time
85  ) {
86  return true;
87  }
88 #endif
89  return false;
90 }
91 
92 BaseSelectionRule::BaseSelectionRule(long index, BaseSelectionRule::ESelect sel, const std::string& attributeName, const std::string& attributeValue, cling::Interpreter &interp, const char* selFileName, long lineno)
93  : fIndex(index),fLineNumber(lineno),fSelFileName(selFileName),fIsSelected(sel),fMatchFound(false),fCXXRecordDecl(0),fRequestedType(0),fInterp(&interp)
94 {
95  fAttributes.insert(AttributesMap_t::value_type(attributeName, attributeValue));
96 }
97 
98 void BaseSelectionRule::SetSelected(BaseSelectionRule::ESelect sel)
99 {
100  fIsSelected = sel;
101 }
102 
103 BaseSelectionRule::ESelect BaseSelectionRule::GetSelected() const
104 {
105  return fIsSelected;
106 }
107 
108 bool BaseSelectionRule::HasAttributeWithName(const std::string& attributeName) const
109 {
110  AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
111 
112  if(iter!=fAttributes.end()) return true;
113  else return false;
114 }
115 
116 bool BaseSelectionRule::GetAttributeValue(const std::string& attributeName, std::string& returnValue) const
117 {
118  AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
119 
120  bool retVal = iter!=fAttributes.end();
121  returnValue = retVal ? iter->second : "";
122  return retVal;
123 }
124 
125 void BaseSelectionRule::SetAttributeValue(const std::string& attributeName, const std::string& attributeValue)
126 {
127 
128  std::string localAttributeValue(attributeValue);
129 
130  int pos = attributeName.find("pattern");
131  int pos_file = attributeName.find("file_pattern");
132 
133  // Strip trailing spaces from the name or pattern
134  if (attributeName == "name" || pos> -1){
135  while(std::isspace(*localAttributeValue.begin())) localAttributeValue.erase(localAttributeValue.begin());
136  while(std::isspace(*localAttributeValue.rbegin()))localAttributeValue.erase(localAttributeValue.length()-1);
137  }
138  fAttributes.insert(AttributesMap_t::value_type(attributeName, localAttributeValue));
139 
140  if (pos > -1) {
141  if (pos_file > -1) // if we have file_pattern
142  ProcessPattern(localAttributeValue, fFileSubPatterns);
143  else ProcessPattern(localAttributeValue, fSubPatterns); // if we have pattern and proto_pattern
144  }
145 
146 
147 
148 }
149 
150 const BaseSelectionRule::AttributesMap_t& BaseSelectionRule::GetAttributes() const
151 {
152  return fAttributes;
153 }
154 
155 void BaseSelectionRule::DebugPrint() const
156 {
157  Print(std::cout);
158 }
159 
160 void BaseSelectionRule::PrintAttributes(std::ostream &out, int level) const
161 {
162  std::string tabs;
163  for (int i = 0; i < level; ++i) {
164  tabs+='\t';
165  }
166 
167  if (!fAttributes.empty()) {
168  std::map<std::string,std::string> orderedAttributes(fAttributes.begin(),fAttributes.end());
169  for (auto&& attr : orderedAttributes) {
170  out<<tabs<<attr.first<<" = "<<attr.second<<std::endl;
171  }
172  }
173  else {
174  out<<tabs<<"No attributes"<<std::endl;
175  }
176 }
177 
178 void BaseSelectionRule::PrintAttributes(int level) const
179 {
180  PrintAttributes(std::cout, level);
181 }
182 #ifndef G__WIN32
183 #include <unistd.h>
184 #endif
185 BaseSelectionRule::EMatchType BaseSelectionRule::Match(const clang::NamedDecl *decl,
186  const std::string& name,
187  const std::string& prototype,
188  bool isLinkdef) const
189 {
190  /* This method returns whether and how the declaration is matching the rule.
191  * It returns one of:
192  * kNoMatch : the rule does match the declaration
193  * kName : the rule match the declaration by name
194  * kPattern : the rule match the declaration via a pattern
195  * kFile : the declaration's file name is match by the rule (either by name or pattern).
196  * To check whether the rule is accepting or vetoing the declaration see the result of
197  * GetSelected().
198  (
199  * We pass as arguments of the method:
200  * name - the name of the Decl
201  * prototype - the prototype of the Decl (if it is function or method, otherwise "")
202  * file_name - name of the source file
203  * isLinkdef - if the selection rules were generating from a linkdef.h file
204  */
205 
206 
207  const std::string& name_value = fName;
208  const std::string& pattern_value = fPattern;
209 
210  // Check if we have in hands a typedef to a RecordDecl
211  const clang::CXXRecordDecl *D = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
212  bool isTypedefNametoRecordDecl = false;
213 
214  if (!D){
215  //Either it's a CXXRecordDecl ot a TypedefNameDecl
216  const clang::TypedefNameDecl* typedefNameDecl = llvm::dyn_cast<clang::TypedefNameDecl> (decl);
217  isTypedefNametoRecordDecl = typedefNameDecl &&
218  ROOT::TMetaUtils::GetUnderlyingRecordDecl(typedefNameDecl->getUnderlyingType());
219  }
220 
221  if (! isTypedefNametoRecordDecl && fCXXRecordDecl !=0 && fCXXRecordDecl != (void*)-1) {
222  const clang::CXXRecordDecl *target = fCXXRecordDecl;
223  if ( target && D && target == D ) {
224  // fprintf(stderr,"DECL MATCH: %s %s\n",name_value.c_str(),name.c_str());
225  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
226  return kName;
227  }
228  } else if (fHasNameAttribute) {
229  if (name_value == name) {
230  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
231  return kName;
232  } else if (fCXXRecordDecl == nullptr ||
233  (fCXXRecordDecl != (void*)-1 && isTypedefNametoRecordDecl && !decl->hasOwningModule())){
234  // Possibly take the most expensive path if the fCXXRecordDecl is not
235  // set or we already took the expensive path and found nothing (-1).
236  const clang::CXXRecordDecl *target
237  = fHasFromTypedefAttribute ? nullptr : ROOT::TMetaUtils::ScopeSearch(name_value.c_str(), *fInterp,
238  true /*diagnose*/, 0);
239 
240  if ( target ) {
241  const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = target;
242  } else {
243  // If the lookup failed, let's not try it again, so mark the value has invalid.
244  const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = (clang::CXXRecordDecl*)-1;
245  }
246  if ( target && D && target == D ) {
247  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
248  return kName;
249  }
250  }
251  }
252 
253  // do we have matching against the file_name (or file_pattern) attribute and if yes - select or veto
254  const std::string& file_name_value = fFileName;
255  const std::string& file_pattern_value = fFilePattern;
256 
257  if ((fHasFileNameAttribute||fHasFilePatternAttribute)) {
258  const char *file_name = R__GetDeclSourceFileName(decl);
259  bool hasFileMatch = ((fHasFileNameAttribute &&
260  //FIXME It would be much better to cache the rule stat result and compare to the clang::FileEntry
261  (R__match_filename(file_name_value.c_str(),file_name))) ||
262  (fHasFilePatternAttribute && CheckPattern(file_name, file_pattern_value, fFileSubPatterns, isLinkdef)));
263 
264  if (hasFileMatch) {
265  // Reject utility classes defined in ClassImp
266  // when using a file based rule
267  if (!strncmp(name.c_str(), "R__Init", 7) ||
268  strstr(name.c_str(), "::R__Init")) {
269  return kNoMatch;
270  }
271  if (!name.compare(0, 24, "ROOT::R__dummyintdefault")) {
272  return kNoMatch;
273  }
274  if (!name.compare(0, 27, "ROOT::R__dummyVersionNumber")) {
275  return kNoMatch;
276  }
277  if (!name.compare(0, 22, "ROOT::R__dummyStreamer")) {
278  return kNoMatch;
279  }
280  if (name.find("(anonymous namespace)") != std::string::npos) {
281  // Reject items declared in anonymous namespace
282  return kNoMatch;
283  }
284  if (fHasPatternAttribute) {
285  if (CheckPattern(name, pattern_value, fSubPatterns, isLinkdef)) {
286  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
287  return kPattern;
288  }
289  } else {
290  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
291  return kName;
292  }
293  }
294 
295  // We have file_name or file_pattern attribute but the
296  // passed file_name is different than that in the selection rule then return no match
297  return kNoMatch;
298  }
299 
300  if (fHasPatternAttribute)
301  {
302  bool patternMatched = CheckPattern(name, pattern_value, fSubPatterns, isLinkdef);
303  if (!patternMatched && !isLinkdef) {
304  std::string auxName(name);
305  std::string &nameNoSpaces = auxName;
306  nameNoSpaces.erase(std::remove_if(nameNoSpaces.begin(), nameNoSpaces.end(), isspace),
307  nameNoSpaces.end());
308  if (name.size() != nameNoSpaces.size()) {
309  patternMatched = CheckPattern(nameNoSpaces, pattern_value, fSubPatterns, isLinkdef);
310  }
311 
312  // For ROOT-6704: use normalised name for matching if the class is in stl
313  // The reason for this check is that we have rules like std::map<*, int>
314  // We do not know how the internal representation of the innocuous "map"
315  // is. We therefore have to act on a nicer name, obtained with TClassEdit
316  // The check ROOT::TMetaUtils::IsStdDropDefaultClass is there to call
317  // TClassEdit only when necessary as it can be expensive, a performance
318  // optimisation.
319  if (!patternMatched &&
320  D &&
321  //ROOT::TMetaUtils::IsStdDropDefaultClass(*D)) {
322  0 != TClassEdit::IsSTLCont(name)) {
323  TClassEdit::GetNormalizedName(auxName, name);
324  if (name.size() != auxName.size()) {
325  auxName = TClassEdit::InsertStd(auxName.c_str());
326  patternMatched = CheckPattern(auxName, pattern_value, fSubPatterns, isLinkdef);
327  }
328  }
329 
330  }
331  if (patternMatched) {
332  const_cast<BaseSelectionRule *>(this)->SetMatchFound(true);
333  return kPattern;
334  }
335  }
336 
337 
338  // do we have matching against the proto_name (or proto_pattern) attribute and if yes - select or veto
339  // The following selects functions on whether the requested prototype exactly matches the
340  // prototype issued by SelectionRules::GetFunctionPrototype which relies on
341  // ParmVarDecl::getType()->getAsString()
342  // to get the type names. Currently, this does not print the prototype in the usual
343  // human (written) forms. For example:
344  // For Hash have prototype: '(const class TString &)'
345  // For Hash have prototype: '(const class TString*)'
346  // For Hash have prototype: '(const char*)'
347  // In addition, the const can legally be in various place in the type name and thus
348  // a string based match will be hard to work out (it would need to normalize both
349  // the user input string and the clang provided string).
350  // Using lookup form cling would be probably be a better choice.
351  if (!prototype.empty()) {
352  if (fHasProtoNameAttribute && fProtoName==prototype) {
353  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
354  return kName;
355  }
356  if (fHasProtoPatternAttribute && CheckPattern(prototype, fProtoPattern, fSubPatterns, isLinkdef)) {
357  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
358  return kPattern;
359  }
360  }
361 
362  return kNoMatch;
363 }
364 
365 
366 /*
367  * This method processes the pattern - which means that it splits it in a list of fSubPatterns.
368  * The idea is the following - if we have a pattern = "this*pat*rn", it will be split in the
369  * following list of subpatterns: "this", "pat", "rn". If we have "this*pat\*rn", it will be
370  * split in "this", "pat*rn", i.e. the star could be escaped.
371  */
372 
373 void BaseSelectionRule::ProcessPattern(const std::string& pattern, std::list<std::string>& out) const
374 {
375  std::string temp = pattern;
376  std::string split;
377  int pos;
378  bool escape = false;
379 
380  if (pattern.size()==1 && pattern == "*"){
381  out.push_back("");
382  return;
383  }
384 
385  while (!temp.empty()){
386  pos = temp.find("*");
387  if (pos == -1) {
388  if (!escape){ // if we don't find a '*', push_back temp (contains the last sub-pattern)
389  out.push_back(temp);
390  // std::cout<<"1. pushed = "<<temp<<std::endl;
391  }
392  else { // if we don't find a star - add temp to split (in split we keep the previous sub-pattern + the last escaped '*')
393  split += temp;
394  out.push_back(split);
395  // std::cout<<"1. pushed = "<<split<<std::endl;
396  }
397  return;
398  }
399  else if (pos == 0) { // we have '*' at the beginning of the pattern; can't have '\' before the '*'
400  temp = temp.substr(1); // remove the '*'
401  }
402  else if (pos == (int)(temp.length()-1)) { // we have '*' at the end of the pattern
403  if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
404  split += temp.substr(0, temp.length()-2); // add evrything from the beginning of temp till the '\' to split (where we keep the last sub-pattern)
405  split += temp.at(pos); // add the '*'
406  out.push_back(split); // push_back() split
407  // std::cout<<"3. pushed = "<<split<<std::endl;
408  temp.clear(); // empty temp (the '*' was at the last position of temp, so we don't have anything else to process)
409  }
410  temp = temp.substr(0, (temp.length()-1));
411  }
412  else { // the '*' is at a random position in the pattern
413  if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
414  split += temp.substr(0, pos-1); // remove the '\' and add the star to split
415  split += temp.at(pos);
416  escape = true; // escape = true which means that we will add the next sub-pattern to that one
417 
418  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
419  temp = temp.substr(pos);
420  // DEBUG std::cout<<"temp = "<<temp<<", split = "<<split<<std::endl;
421  }
422  else { // if we don't have '\' before the '*'
423  if (escape) {
424  split += temp.substr(0, pos);
425  }
426  else {
427  split = temp.substr(0, pos);
428  }
429  escape = false;
430  temp = temp.substr(pos);
431  out.push_back(split);
432  // std::cout<<"2. pushed = "<<split<<std::endl;
433  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
434  split = "";
435  }
436  }
437  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
438  }
439 }
440 
441 /*
442  * This method checks if the given test string is matched against the pattern
443  */
444 
445 bool BaseSelectionRule::CheckPattern(const std::string& test, const std::string& pattern, const std::list<std::string>& patterns_list, bool isLinkdef) const
446 {
447  bool begin = pattern.front() == '*';
448  if (pattern.size() == 1 && begin) {
449  // We have the simple pattern '*', it matches everything by definition!
450  return true;
451  }
452 
453  std::list<std::string>::const_iterator it = patterns_list.begin();
454  size_t pos1, pos2, pos3;
455  pos1= pos2= pos3= std::string::npos;
456  bool end = pattern.back() == '*';
457 
458  // we first check if the last sub-pattern is contained in the test string
459  const std::string& last = patterns_list.back();
460  size_t pos_end = test.rfind(last);
461 
462  if (pos_end == std::string::npos) { // the last sub-pattern isn't conatained in the test string
463  return false;
464  }
465  if (!end) { // if the pattern doesn't end with '*', the match has to be complete
466  // i.e. if the last sub-pattern is "sub" the test string should end in "sub" ("1111sub" is OK, "1111sub1" is not OK)
467 
468  int len = last.length(); // length of last sub-pattern
469  if ((pos_end+len) < test.length()) {
470  return false;
471  }
472  }
473 
474  // position of the first sub-pattern
475  pos1 = test.find(*it);
476 
477 
478  if (pos1 == std::string::npos || (!begin && pos1 != 0)) { // if the first sub-pattern isn't found in test or if it is found but the
479  // pattern doesn't start with '*' and the sub-pattern is not at the first position
480  //std::cout<<"\tNo match!"<<std::endl;
481  return false;
482  }
483 
484  if (isLinkdef) { // A* selects all global classes, unions, structs but not the nested, i.e. not A::B
485  // A::* selects the nested classes
486  int len = (*it).length();
487  int pos_colon = test.find("::", pos1+len);
488 
489  if (pos_colon > -1) {
490  return false;
491  }
492 
493  }
494 
495  if (patterns_list.size() > 1) {
496  if (((*it).length())+pos1 > pos_end) {
497  // std::cout<<"\tNo match";
498  return false; // end is contained in begin -> test = "A::B" sub-patterns = "A::", "::" will return false
499  }
500  }
501 
502 
503  ++it;
504 
505  for (; it != patterns_list.end(); ++it) {
506  // std::cout<<"sub-pattern = "<<*it<<std::endl;
507  pos2 = test.find(*it);
508  if (pos2 <= pos1) {
509  return false;
510  }
511  pos1 = pos2;
512  }
513 
514  return true;
515 }
516 
517 
518 void BaseSelectionRule::SetMatchFound(bool match)
519 {
520  fMatchFound = match;
521 }
522 
523 bool BaseSelectionRule::GetMatchFound() const
524 {
525  return fMatchFound;
526 }
527 
528 const clang::Type *BaseSelectionRule::GetRequestedType() const
529 {
530  return fRequestedType;
531 }
532 
533 void BaseSelectionRule::SetCXXRecordDecl(const clang::CXXRecordDecl *decl, const clang::Type *typeptr)
534 {
535  fCXXRecordDecl = decl;
536  fRequestedType = typeptr;
537 }
538 
539 void BaseSelectionRule::FillCache()
540 {
541  std::string value;
542  fHasNameAttribute = GetAttributeValue("name",fName);
543  fHasProtoNameAttribute = GetAttributeValue("proto_name",fProtoName);
544  fHasPatternAttribute = GetAttributeValue("pattern",fPattern);
545  fHasProtoPatternAttribute = GetAttributeValue("proto_pattern",fProtoPattern);
546  fHasFileNameAttribute = GetAttributeValue("file_name",fFileName);
547  fHasFilePatternAttribute = GetAttributeValue("file_pattern",fFilePattern);
548  fHasFromTypedefAttribute = GetAttributeValue("fromTypedef",value);
549  fIsFromTypedef = (value == "true");
550 
551  GetAttributeValue(ROOT::TMetaUtils::propNames::nArgsToKeep,fNArgsToKeep);
552 
553 
554  if (fHasPatternAttribute || fHasProtoPatternAttribute) {
555  if (fSubPatterns.empty()) {
556  std::cout<<"Error - A pattern selection without sub patterns." <<std::endl;
557  }
558  }
559 
560 }
561 
562