Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
LinkdefReader.cxx
Go to the documentation of this file.
1 // @(#)root/core/utils:$Id: LinkdefReader.cxx 41697 2011-11-01 21:03:41Z pcanal $
2 // Author: Velislava Spasova September 2010
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2011, Rene Brun, Fons Rademakers and al. *
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 // LinkdefReader //
15 // //
16 // //
17 // Note: some inconsistency in the way CINT parsed the #pragma: //
18 // "#pragma link C++ class" is terminated by either a ';' or a newline//
19 // which ever come first and does NOT support line continuation. //
20 // "#pragma read ..." is terminated by newline but support line //
21 // continuation (i.e. '\' followed by newline means to also use the//
22 // next line. //
23 // This was change in CINT to consistently ignore the continuation //
24 // //
25 // //
26 //////////////////////////////////////////////////////////////////////////
27 
28 #include <iostream>
29 #include <memory>
30 #include "LinkdefReader.h"
31 #include "SelectionRules.h"
32 #include "RConversionRuleParser.h"
33 
34 #include "llvm/Support/raw_ostream.h"
35 
36 #include "clang/AST/ASTContext.h"
37 
38 #include "clang/Frontend/CompilerInstance.h"
39 
40 #include "clang/Lex/Preprocessor.h"
41 #include "clang/Lex/Pragma.h"
42 
43 #include "cling/Interpreter/CIFactory.h"
44 #include "cling/Interpreter/Interpreter.h"
45 
46 std::map<std::string, LinkdefReader::EPragmaNames> LinkdefReader::fgMapPragmaNames;
47 std::map<std::string, LinkdefReader::ECppNames> LinkdefReader::fgMapCppNames;
48 
49 struct LinkdefReader::Options {
50  Options() : fNoStreamer(0), fNoInputOper(0), fUseByteCount(0), fVersionNumber(-1) {}
51 
52  int fNoStreamer;
53  int fNoInputOper;
54  union {
55  int fUseByteCount;
56  int fRequestStreamerInfo;
57  };
58  int fVersionNumber;
59 };
60 
61 /*
62  This is a static function - which in our context means it is populated only ones
63  */
64 void LinkdefReader::PopulatePragmaMap()
65 {
66  if (!(fgMapPragmaNames.empty())) return; // if the map has already been populated, return, else populate it
67 
68  LinkdefReader::fgMapPragmaNames["TClass"] = kClass;
69  LinkdefReader::fgMapPragmaNames["class"] = kClass;
70  LinkdefReader::fgMapPragmaNames["typedef"] = kTypeDef;
71  LinkdefReader::fgMapPragmaNames["namespace"] = kNamespace;
72  LinkdefReader::fgMapPragmaNames["function"] = kFunction;
73  LinkdefReader::fgMapPragmaNames["global"] = kGlobal;
74  LinkdefReader::fgMapPragmaNames["enum"] = kEnum;
75  LinkdefReader::fgMapPragmaNames["union"] = kUnion;
76  LinkdefReader::fgMapPragmaNames["struct"] = kStruct;
77  LinkdefReader::fgMapPragmaNames["all"] = kAll;
78  LinkdefReader::fgMapPragmaNames["defined_in"] = kDefinedIn;
79  LinkdefReader::fgMapPragmaNames["ioctortype"] = kIOCtorType;
80  LinkdefReader::fgMapPragmaNames["nestedclass"] = kNestedclasses;
81  LinkdefReader::fgMapPragmaNames["nestedclasses"] = kNestedclasses;
82  LinkdefReader::fgMapPragmaNames["nestedclasses;"] = kNestedclasses;
83  LinkdefReader::fgMapPragmaNames["operators"] = kOperators;
84  LinkdefReader::fgMapPragmaNames["operator"] = kOperators;
85  // The following are listed here so we can officially ignore them
86  LinkdefReader::fgMapPragmaNames["nestedtypedefs"] = kIgnore;
87  LinkdefReader::fgMapPragmaNames["nestedtypedef"] = kIgnore;
88 }
89 
90 void LinkdefReader::PopulateCppMap()
91 {
92  if (!(fgMapCppNames.empty())) return; // if the map has already been populated, return, else populate it
93 
94  LinkdefReader::fgMapCppNames["#pragma"] = kPragma;
95  LinkdefReader::fgMapCppNames["#ifdef"] = kIfdef;
96  LinkdefReader::fgMapCppNames["#endif"] = kEndif;
97  LinkdefReader::fgMapCppNames["#if"] = kIf;
98  LinkdefReader::fgMapCppNames["#else"] = kElse;
99 }
100 
101 LinkdefReader::LinkdefReader(cling::Interpreter &interp,
102  ROOT::TMetaUtils::RConstructorTypes &IOConstructorTypes):
103  fLine(1), fCount(0), fSelectionRules(nullptr), fIOConstructorTypesPtr(&IOConstructorTypes), fInterp(interp)
104 {
105  PopulatePragmaMap();
106  PopulateCppMap();
107 }
108 
109 /*
110  * The method records that 'include' has been explicitly requested in the linkdef file
111  * to be added to the dictionary and interpreter.
112  */
113 bool LinkdefReader::AddInclude(const std::string& include)
114 {
115  fIncludes += "#include ";
116  fIncludes += include;
117  fIncludes += "\n";
118 
119  return true;
120 }
121 
122 
123 /*
124  * The method that processes the pragma statement.
125  * Sometimes I had to do strange things to reflect the strange behavior of rootcint
126  */
127 bool LinkdefReader::AddRule(const std::string& ruletype,
128  const std::string& identifier,
129  bool linkOn,
130  bool request_only_tclass,
131  LinkdefReader::Options *options /* = 0 */)
132 {
133 
134  EPragmaNames name = kUnknown;
135  ROOT::TMetaUtils::Info("LinkdefReader::AddRule", "Ruletype is %s with the identifier %s\n", ruletype.c_str(), identifier.c_str());
136  auto it = fgMapPragmaNames.find(ruletype);
137  if (it != fgMapPragmaNames.end()) {
138  name = it->second;
139  }
140 
141  switch (name) {
142  case kAll:
143  if (identifier == "globals" || identifier == "global") {
144  VariableSelectionRule vsr(fCount++, fInterp);
145  if (linkOn) {
146  vsr.SetAttributeValue("pattern", "*");
147  vsr.SetSelected(BaseSelectionRule::kYes);
148  fSelectionRules->AddVariableSelectionRule(vsr);
149  } else {
150  if (fSelectionRules->GetHasFileNameRule()) { // only if we had previous defined_in -> create that
151  // we don't create anything which is OK - if I don't have a selection rule for something
152  // this something will not be generated
153  // This is valid also for the other all ... cases
154  vsr.SetAttributeValue("pattern", "*");
155  vsr.SetSelected(BaseSelectionRule::kNo);
156  fSelectionRules->AddVariableSelectionRule(vsr);
157  }
158  }
159  //else vsr.SetSelected(BaseSelectionRule::kNo);
160  //fSelectionRules->AddVariableSelectionRule(vsr);
161 
162  EnumSelectionRule esr(fCount++, fInterp);
163  if (linkOn) {
164  esr.SetSelected(BaseSelectionRule::kYes);
165  esr.SetAttributeValue("pattern", "*");
166  fSelectionRules->AddEnumSelectionRule(esr);
167 
168  //EnumSelectionRule esr2; //Problem wih the enums - if I deselect them here
169  EnumSelectionRule esr2(fCount++, fInterp);
170  esr2.SetSelected(BaseSelectionRule::kNo);
171  esr2.SetAttributeValue("pattern", "*::*");
172  fSelectionRules->AddEnumSelectionRule(esr2);
173  } else {
174  if (fSelectionRules->GetHasFileNameRule()) {
175  esr.SetAttributeValue("pattern", "*");
176  esr.SetSelected(BaseSelectionRule::kNo);
177  fSelectionRules->AddEnumSelectionRule(esr);
178  }
179  }
180  } else if (identifier == "functions" || identifier == "function") {
181  FunctionSelectionRule fsr(fCount++, fInterp);
182  fsr.SetAttributeValue("pattern", "*");
183  if (linkOn) {
184  fsr.SetSelected(BaseSelectionRule::kYes);
185  fSelectionRules->AddFunctionSelectionRule(fsr);
186  } else {
187  if (fSelectionRules->GetHasFileNameRule()) {
188  fsr.SetSelected(BaseSelectionRule::kNo);
189  fSelectionRules->AddFunctionSelectionRule(fsr);
190  }
191  }
192  } else if (identifier == "classes" || identifier == "namespaces" ||
193  identifier == "class" || identifier == "namespace") {
194  if (linkOn) {
195 
196  ClassSelectionRule csr3(fCount++, fInterp);
197  csr3.SetSelected(BaseSelectionRule::kNo);
198  csr3.SetAttributeValue("pattern", "__va_*"); // don't generate for the built-in classes/structs
199  fSelectionRules->AddClassSelectionRule(csr3);
200 
201  ClassSelectionRule csr(fCount++, fInterp), csr2(fCount++, fInterp);
202  csr.SetAttributeValue("pattern", "*");
203  csr2.SetAttributeValue("pattern", "*::*");
204  csr.SetSelected(BaseSelectionRule::kYes);
205  csr2.SetSelected(BaseSelectionRule::kYes);
206 
207  fSelectionRules->AddClassSelectionRule(csr);
208  fSelectionRules->AddClassSelectionRule(csr2);
209  } else {
210  if (fSelectionRules->GetHasFileNameRule()) {
211  ClassSelectionRule csr(fCount++, fInterp), csr2(fCount++, fInterp);
212  csr.SetAttributeValue("pattern", "*");
213  csr2.SetAttributeValue("pattern", "*::*");
214 
215  csr.SetSelected(BaseSelectionRule::kNo);
216  csr2.SetSelected(BaseSelectionRule::kNo);
217  fSelectionRules->AddClassSelectionRule(csr);
218  fSelectionRules->AddClassSelectionRule(csr2);
219  }
220  }
221  } else if (identifier == "typedefs" || identifier == "typedef") {
222  // Silently ignore
223  } else {
224  ROOT::TMetaUtils::Warning("Unimplemented pragma statement: %s\n",identifier.c_str());
225  return false;
226  }
227 
228  break;
229  case kNestedclasses: {
230  // we don't really process that one
231  }
232  break;
233  case kDefinedIn: {
234  fSelectionRules->SetHasFileNameRule(true);
235 
236  // add selection rules for everything
237  std::string localIdentifier(identifier);
238  if (localIdentifier.length() && localIdentifier[0] == '"' && localIdentifier[localIdentifier.length() - 1] == '"') {
239  localIdentifier = localIdentifier.substr(1, localIdentifier.length() - 2);
240  }
241 
242  VariableSelectionRule vsr(fCount++, fInterp);
243  vsr.SetAttributeValue("pattern", "*");
244  vsr.SetAttributeValue("file_name", localIdentifier);
245  if (linkOn) vsr.SetSelected(BaseSelectionRule::kYes);
246  else vsr.SetSelected(BaseSelectionRule::kNo);
247  fSelectionRules->AddVariableSelectionRule(vsr);
248 
249  EnumSelectionRule esr(fCount++, fInterp);
250  esr.SetAttributeValue("pattern", "*");
251  esr.SetAttributeValue("file_name", localIdentifier);
252  if (linkOn) esr.SetSelected(BaseSelectionRule::kYes);
253  else esr.SetSelected(BaseSelectionRule::kNo);
254  fSelectionRules->AddEnumSelectionRule(esr);
255 
256  FunctionSelectionRule fsr(fCount++, fInterp);
257  fsr.SetAttributeValue("pattern", "*");
258  fsr.SetAttributeValue("file_name", localIdentifier);
259  if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
260  else fsr.SetSelected(BaseSelectionRule::kNo);
261  fSelectionRules->AddFunctionSelectionRule(fsr);
262 
263  ClassSelectionRule csr(fCount++, fInterp), csr2(fCount++, fInterp);
264  csr.SetAttributeValue("pattern", "*");
265  csr2.SetAttributeValue("pattern", "*::*");
266 
267  csr.SetAttributeValue("file_name", localIdentifier);
268  csr2.SetAttributeValue("file_name", localIdentifier);
269  if (linkOn) {
270  csr.SetSelected(BaseSelectionRule::kYes);
271  csr2.SetSelected(BaseSelectionRule::kYes);
272  } else {
273  csr.SetSelected(BaseSelectionRule::kNo);
274  csr2.SetSelected(BaseSelectionRule::kNo);
275  }
276  csr.SetRequestStreamerInfo(true);
277  csr2.SetRequestStreamerInfo(true);
278  fSelectionRules->AddClassSelectionRule(csr);
279  fSelectionRules->AddClassSelectionRule(csr2);
280 
281  }
282  break;
283 
284  case kFunction: {
285  std::string localIdentifier(identifier);
286  bool name_or_proto = false; // if true = name, if flase = proto_name
287  if (!ProcessFunctionPrototype(localIdentifier, name_or_proto)) {
288  return false;
289  }
290  FunctionSelectionRule fsr(fCount++, fInterp);
291  if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
292  else fsr.SetSelected(BaseSelectionRule::kNo);
293  if (localIdentifier.at(localIdentifier.length() - 1) == '*') fsr.SetAttributeValue("pattern", localIdentifier);
294  else if (name_or_proto) fsr.SetAttributeValue("name", localIdentifier);
295  else {
296  int pos = localIdentifier.find("(*)"); //rootcint generates error here but I decided to implement that pattern
297  if (pos > -1) fsr.SetAttributeValue("proto_pattern", localIdentifier);
298  else {
299  // No multiline
300  ROOT::TMetaUtils::ReplaceAll(localIdentifier, "\\\n", "", true);
301  // Types: We do not do IO of functions, so it is safe to
302  // put in some heuristics
303  ROOT::TMetaUtils::ReplaceAll(localIdentifier, "ULong_t", "unsigned long");
304  ROOT::TMetaUtils::ReplaceAll(localIdentifier, "Long_t", "long");
305  ROOT::TMetaUtils::ReplaceAll(localIdentifier, "Int_t", "int");
306  // Remove space after/before the commas if any
307  ROOT::TMetaUtils::ReplaceAll(localIdentifier, ", ", ",", true);
308  ROOT::TMetaUtils::ReplaceAll(localIdentifier, " ,", ",", true);
309  // Remove any space before/after the ( as well
310  ROOT::TMetaUtils::ReplaceAll(localIdentifier, " (", "(", true);
311  ROOT::TMetaUtils::ReplaceAll(localIdentifier, "( ", "(", true);
312  ROOT::TMetaUtils::ReplaceAll(localIdentifier, " )", ")", true);
313  fsr.SetAttributeValue("proto_name", localIdentifier);
314  }
315  }
316  fSelectionRules->AddFunctionSelectionRule(fsr);
317 
318  }
319  break;
320 
321  case kOperators: {
322  std::string localIdentifier(identifier);
323  if (!ProcessOperators(localIdentifier)) // this creates the proto_pattern
324  return false;
325 
326  FunctionSelectionRule fsr(fCount++, fInterp);
327  if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
328  else fsr.SetSelected(BaseSelectionRule::kNo);
329  fsr.SetAttributeValue("proto_pattern", localIdentifier);
330  fSelectionRules->AddFunctionSelectionRule(fsr);
331  }
332  break;
333  case kGlobal: {
334  VariableSelectionRule vsr(fCount++, fInterp);
335  if (linkOn) vsr.SetSelected(BaseSelectionRule::kYes);
336  else vsr.SetSelected(BaseSelectionRule::kNo);
337  if (IsPatternRule(identifier)) vsr.SetAttributeValue("pattern", identifier);
338  else vsr.SetAttributeValue("name", identifier);
339  fSelectionRules->AddVariableSelectionRule(vsr);
340  }
341  break;
342  case kEnum: {
343 
344  EnumSelectionRule esr(fCount++, fInterp);
345  if (linkOn) esr.SetSelected(BaseSelectionRule::kYes);
346  else esr.SetSelected(BaseSelectionRule::kNo);
347  if (IsPatternRule(identifier)) esr.SetAttributeValue("pattern", identifier);
348  else esr.SetAttributeValue("name", identifier);
349  fSelectionRules->AddEnumSelectionRule(esr);
350  }
351  break;
352  case kClass:
353  case kTypeDef:
354  case kNamespace:
355  case kUnion:
356  case kStruct: {
357  std::string localIdentifier(identifier);
358  ClassSelectionRule csr(fCount++, fInterp);
359 
360  if (request_only_tclass) {
361  csr.SetRequestOnlyTClass(true);
362  }
363  int len = localIdentifier.length();
364  if (len > 8) { // process class+protected and class+private
365  const std::string protStr("+protected");
366  const std::string privStr("+private");
367 
368  if (localIdentifier.compare(0, protStr.length(), protStr) == 0) {
369  csr.SetRequestProtected(true);
370  localIdentifier.erase(0, protStr.length() + 1);
371  len = localIdentifier.length();
372  } else if (localIdentifier.compare(0, privStr.length(), privStr) == 0) {
373  csr.SetRequestPrivate(true);
374  localIdentifier.erase(0, privStr.length() + 1);
375  len = localIdentifier.length();
376  }
377  }
378  if (len > 1) { // process the +, -, -! endings of the classes
379 
380  bool ending = false;
381  int where = 1;
382  while (!ending && where < len) {
383  char last = localIdentifier.at(len - where);
384  switch (last) {
385  case ';':
386  break;
387  case '+':
388  csr.SetRequestStreamerInfo(true);
389  break;
390  case '!':
391  csr.SetRequestNoInputOperator(true);
392  break;
393  case '-':
394  csr.SetRequestNoStreamer(true);
395  break;
396  case ' ':
397  case '\t':
398  break;
399  default:
400  ending = true;
401  }
402  ++where;
403  }
404  if (options) {
405  if (options->fNoStreamer) csr.SetRequestNoStreamer(true);
406  if (options->fNoInputOper) csr.SetRequestNoInputOperator(true);
407  if (options->fRequestStreamerInfo) csr.SetRequestStreamerInfo(true);
408  if (options->fVersionNumber >= 0) csr.SetRequestedVersionNumber(options->fVersionNumber);
409  }
410  if (csr.RequestStreamerInfo() && csr.RequestNoStreamer()) {
411  std::cerr << "Warning: " << localIdentifier << " option + mutual exclusive with -, + prevails\n";
412  csr.SetRequestNoStreamer(false);
413  }
414  if (ending) {
415  localIdentifier.erase(len - (where - 2)); // We 'consumed' one of the class token
416  } else {
417  localIdentifier.erase(len - (where - 1));
418  }
419  }
420 
421  if (linkOn) {
422  csr.SetSelected(BaseSelectionRule::kYes);
423 
424  if (localIdentifier == "*") { // rootcint generates error here, but I decided to implement it
425  ClassSelectionRule csr2(fCount++, fInterp);
426  csr2.SetSelected(BaseSelectionRule::kYes);
427  csr2.SetAttributeValue("pattern", "*::*");
428  fSelectionRules->AddClassSelectionRule(csr2);
429 
430  ClassSelectionRule csr3(fCount++, fInterp);
431  csr3.SetSelected(BaseSelectionRule::kNo);
432  csr3.SetAttributeValue("pattern", "__va_*");
433  fSelectionRules->AddClassSelectionRule(csr3);
434  }
435  } else {
436  csr.SetSelected(BaseSelectionRule::kNo);
437  if (localIdentifier == "*") { // rootcint generates error here, but I decided to implement it
438  ClassSelectionRule csr2(fCount++, fInterp);
439  csr2.SetSelected(BaseSelectionRule::kNo);
440  csr2.SetAttributeValue("pattern", "*::*");
441  fSelectionRules->AddClassSelectionRule(csr2);
442 
443  EnumSelectionRule esr(fCount++, fInterp); // we need this because of implicit/explicit rules - check my notes on rootcint
444  esr.SetSelected(BaseSelectionRule::kNo);
445  esr.SetAttributeValue("pattern", "*::*");
446  fSelectionRules->AddEnumSelectionRule(esr);
447 
448  }
449  // Since the rootcling default is 'off' (we need to explicilty annotate to turn it on), the nested type and function
450  // should be off by default. Note that anyway, this is not yet relevant since the pcm actually ignore the on/off
451  // request and contains everything (for now).
452  // else {
453  // EnumSelectionRule esr(fCount++); // we need this because of implicit/explicit rules - check my notes on rootcint
454  // esr.SetSelected(BaseSelectionRule::kNo);
455  // esr.SetAttributeValue("pattern", localIdentifier+"::*");
456  // fSelectionRules->AddEnumSelectionRule(esr);
457 
458  // if (fSelectionRules->GetHasFileNameRule()) {
459  // FunctionSelectionRule fsr(fCount++); // we need this because of implicit/explicit rules - check my notes on rootcint
460  // fsr.SetSelected(BaseSelectionRule::kNo);
461  // std::string value = localIdentifier + "::*";
462  // fsr.SetAttributeValue("pattern", value);
463  // fSelectionRules->AddFunctionSelectionRule(fsr);
464  // }
465  // }
466  }
467  if (IsPatternRule(localIdentifier)) {
468  csr.SetAttributeValue("pattern", localIdentifier);
469  }
470  csr.SetAttributeValue("name", localIdentifier);
471 
472  if (name == kTypeDef) {
473  csr.SetAttributeValue("fromTypedef", "true");
474  }
475 
476  fSelectionRules->AddClassSelectionRule(csr);
477  //csr.PrintAttributes(std::cout,3);
478  }
479  break;
480  case kIOCtorType:
481  // #pragma link C++ IOCtorType typename;
482  fIOConstructorTypesPtr->push_back(ROOT::TMetaUtils::RConstructorType(identifier.c_str(), fInterp));
483  break;
484  case kIgnore:
485  // All the pragma that were supported in CINT but are currently not relevant for CLING
486  // (mostly because we do not yet filter the dictionary/pcm).
487  break;
488  case kUnknown:
489  ROOT::TMetaUtils::Warning("Unimplemented pragma statement - it has no effect: %s\n", identifier.c_str());
490  return false;
491  break;
492  }
493 
494  return true;
495 }
496 
497 bool LinkdefReader::IsPatternRule(const std::string &rule_token)
498 {
499  int pos = rule_token.find("*");
500  if (pos > -1) return true;
501  else return false;
502 }
503 
504 /*
505  * The method records that 'include' has been explicitly requested in the linkdef file
506  * to be added to the dictionary and interpreter.
507  */
508 bool LinkdefReader::LoadIncludes(std::string &extraIncludes)
509 {
510  extraIncludes += fIncludes;
511  return cling::Interpreter::kSuccess == fInterp.declare(fIncludes);
512 }
513 
514 bool LinkdefReader::ProcessFunctionPrototype(std::string &proto, bool &name)
515 {
516  int pos1, pos1_1, pos2, pos2_1;
517 
518  pos1 = proto.find_first_of("(");
519  pos1_1 = proto.find_last_of("(");
520 
521  if (pos1 != pos1_1) {
522  std::cout << "Error at line " << fLine << " - too many ( in function prototype!" << std::endl;
523  return false;
524  }
525 
526  pos2 = proto.find_first_of(")");
527  pos2_1 = proto.find_last_of(")");
528 
529  if (pos2 != pos2_1) {
530  std::cout << "Error at line " << fLine << " - too many ) in function prototype!" << std::endl;
531  return false;
532  }
533 
534  if (pos1 > -1) {
535  if (pos2 < 0) {
536  std::cout << "Error at line " << fLine << " - missing ) in function prototype" << std::endl;
537  return false;
538  }
539  if (pos2 < pos1) {
540  std::cout << "Error at line " << fLine << " - wrong order of ( and ) in function prototype" << std::endl;
541  return false;
542  }
543 
544  // I don't have to escape the *-s because in rootcint there is no pattern recognition
545  int pos3 = pos1;
546  while (true) {
547  pos3 = proto.find(" ", pos3);
548  if (pos3 > -1) {
549  proto.erase(pos3, 1);
550  }
551  if (pos3 < 0) break;
552  }
553  name = false;
554  } else {
555  if (pos2 > -1) {
556  std::cout << "Error at line " << fLine << " - missing ( in function prototype" << std::endl;
557  return false;
558  } else {
559  //std::cout<<"Debug - no prototype, name = true"<<std::endl;
560  name = true;
561  }
562  }
563  return true;
564 }
565 
566 // This function is really very basic - it just checks whether everything is OK with the
567 // spaces and if the number of opening < matches the number of >.
568 // But it doesn't catch situations like vector>int<, etc.
569 bool LinkdefReader::ProcessOperators(std::string &pattern)
570 {
571  int pos = -1;
572  int pos1 = -1, pos2 = -1;
573  int open_br = 0, close_br = 0;
574  int i = 0;
575  while (true) {
576  i++;
577  pos = pattern.find(" ", pos + 1);
578  pos1 = pattern.find("<", pos1 + 1);
579  pos2 = pattern.find(">", pos2 + 1);
580 
581  if ((pos < 0) && (pos1 < 0) && (pos2 < 0)) break;
582 
583  if (pos1 > -1) ++open_br;
584  if (pos2 > -1) ++close_br;
585 
586  if (pos < 0) continue;
587  char before = '$';
588  char after = '$';
589  bool ok1 = false;
590  bool ok2 = false;
591 
592  if (pos > 0) before = pattern.at(pos - 1);
593  if (pos < (int)(pattern.length() - 1)) after = pattern.at(pos + 1);
594 
595  //std::cout<<"before: "<<before<<", after: "<<after<<", pos: "<<pos<<std::endl;
596  switch (before) {
597  case '<':
598  case ',':
599  case ' ':
600  ok1 = true;
601  break;
602  default:
603  ok1 = false;
604  }
605  switch (after) {
606  case '>':
607  case '<':
608  case ',':
609  case ' ':
610  ok2 = true;
611  break;
612  default:
613  ok2 = false;
614  }
615  //std::cout<<"ok1: "<<ok1<<", ok2: "<<ok2<<std::endl;
616  if (!ok1 && !ok2) {
617  std::cout << "Error at line " << fLine - 1 << " - extra space" << std::endl;
618  return false;
619  }
620  pattern.erase(pos, 1);
621  }
622 
623  if (open_br != close_br) {
624  std::cout << "Error at line " << fLine << " - number of < doesn't match number of >" << std::endl;
625  return false;
626  }
627  pattern = "operator*(*" + pattern + "*)";
628  return true;
629 }
630 
631 class LinkdefReaderPragmaHandler : public clang::PragmaHandler {
632 protected:
633  LinkdefReader &fOwner;
634  clang::SourceManager &fSourceManager;
635 public:
636  LinkdefReaderPragmaHandler(const char *which, LinkdefReader &owner, clang::SourceManager &sm) :
637  // This handler only cares about "#pragma link"
638  clang::PragmaHandler(which), fOwner(owner), fSourceManager(sm) {
639  }
640 
641  void Error(const char *message, const clang::Token &tok, bool source = true) {
642 
643  std::cerr << message << " at ";
644  tok.getLocation().dump(fSourceManager);
645  if (source) {
646  std::cerr << ":";
647  std::cerr << fSourceManager.getCharacterData(tok.getLocation());
648  }
649  std::cerr << '\n';
650  }
651 
652  bool ProcessOptions(LinkdefReader::Options &options,
653  clang::Preprocessor &PP,
654  clang::Token &tok) {
655  // Constructor parsing:
656  /* options=...
657  * possible options:
658  * nostreamer: set G__NOSTREAMER flag
659  * noinputoper: set G__NOINPUTOPERATOR flag
660  * evolution: set G__USEBYTECOUNT flag
661  * nomap: (ignored by roocling; prevents entry in ROOT's rootmap file)
662  * stub: (ignored by rootcling was a directly for CINT code generation)
663  * version(x): sets the version number of the class to x
664  */
665 
666  // We assume that the first toke in option or options
667  // assert( tok.getIdentifierInfo()->getName() != "option" or "options")
668 
669  PP.Lex(tok);
670  if (tok.is(clang::tok::eod) || tok.isNot(clang::tok::equal)) {
671  Error("Error: the 'options' keyword must be followed by an '='", tok);
672  return false;
673  }
674 
675  PP.Lex(tok);
676  while (tok.isNot(clang::tok::eod) && tok.isNot(clang::tok::semi)) {
677  if (!tok.getIdentifierInfo()) {
678  Error("Error: Malformed version option.", tok);
679  } else if (tok.getIdentifierInfo()->getName() == "nomap") {
680  // For rlibmap rather than rootcling
681  // so ignore
682  } else if (tok.getIdentifierInfo()->getName() == "nostreamer") options.fNoStreamer = 1;
683  else if (tok.getIdentifierInfo()->getName() == "noinputoper") options.fNoInputOper = 1;
684  else if (tok.getIdentifierInfo()->getName() == "evolution") options.fRequestStreamerInfo = 1;
685  else if (tok.getIdentifierInfo()->getName() == "stub") {
686  // This was solely for CINT dictionary, ignore for now.
687  // options.fUseStubs = 1;
688  } else if (tok.getIdentifierInfo()->getName() == "version") {
689  clang::Token start = tok;
690  PP.Lex(tok);
691  if (tok.is(clang::tok::eod) || tok.isNot(clang::tok::l_paren)) {
692  Error("Error: missing left parenthesis after version.", start);
693  return false;
694  }
695  PP.Lex(tok);
696  clang::Token number = tok;
697  if (tok.isNot(clang::tok::eod)) PP.Lex(tok);
698  if (tok.is(clang::tok::eod) || tok.isNot(clang::tok::r_paren)) {
699  Error("Error: missing right parenthesis after version.", start);
700  return false;
701  }
702  if (!number.isLiteral()) {
703  std::cerr << "Error: Malformed version option, the value is not a non-negative number!";
704  Error("", tok);
705  }
706  std::string verStr(number.getLiteralData(), number.getLength());
707  bool noDigit = false;
708  for (std::string::size_type i = 0; i < verStr.size(); ++i)
709  if (!isdigit(verStr[i])) noDigit = true;
710 
711  if (noDigit) {
712  std::cerr << "Error: Malformed version option! \"" << verStr << "\" is not a non-negative number!";
713  Error("", start);
714  } else
715  options.fVersionNumber = atoi(verStr.c_str());
716  } else {
717  Error("Warning: ignoring unknown #pragma link option=", tok);
718  }
719  PP.Lex(tok);
720  if (tok.is(clang::tok::eod) || tok.isNot(clang::tok::comma)) {
721  // no more options, we are done.
722  break;
723  }
724  PP.Lex(tok);
725  }
726  return true;
727  }
728 
729 };
730 
731 class PragmaExtraInclude : public LinkdefReaderPragmaHandler {
732 public:
733  PragmaExtraInclude(LinkdefReader &owner, clang::SourceManager &sm) :
734  // This handler only cares about "#pragma link"
735  LinkdefReaderPragmaHandler("extra_include", owner, sm) {
736  }
737 
738  void HandlePragma(clang::Preprocessor &PP,
739  clang::PragmaIntroducerKind Introducer,
740  clang::Token &tok) {
741  // Handle a #pragma found by the Preprocessor.
742 
743  // check whether we care about the pragma - we are a named handler,
744  // thus this could actually be transformed into an assert:
745  if (Introducer != clang::PIK_HashPragma) return; // only #pragma, not C-style.
746  if (!tok.getIdentifierInfo()) return; // must be "link"
747  if (tok.getIdentifierInfo()->getName() != "extra_include") return;
748 
749  PP.Lex(tok);
750  // if (DClient.hasErrorOccured()) {
751  // return;
752  // }
753  if (tok.is(clang::tok::eod)) {
754  Error("Warning - lonely pragma statement: ", tok);
755  return;
756  }
757  const char *start = fSourceManager.getCharacterData(tok.getLocation());
758  clang::Token end;
759  end.startToken(); // Initialize token.
760  while (tok.isNot(clang::tok::eod) && tok.isNot(clang::tok::semi)) {
761  end = tok;
762  PP.Lex(tok);
763  }
764  if (tok.isNot(clang::tok::semi)) {
765  Error("Error: missing ; at end of rule", tok, false);
766  return;
767  }
768  if (end.is(clang::tok::unknown)) {
769  Error("Error: Unknown token!", tok);
770  } else {
771  llvm::StringRef include(start, fSourceManager.getCharacterData(end.getLocation()) - start + end.getLength());
772 
773  if (!fOwner.AddInclude(include)) {
774  Error("", tok);
775  }
776  }
777  }
778 };
779 
780 class PragmaIoReadInclude : public LinkdefReaderPragmaHandler {
781 public:
782  PragmaIoReadInclude(LinkdefReader &owner, clang::SourceManager &sm) :
783  // This handler only cares about "#pragma link"
784  LinkdefReaderPragmaHandler("read", owner, sm) {
785  }
786 
787  void HandlePragma(clang::Preprocessor &PP,
788  clang::PragmaIntroducerKind Introducer,
789  clang::Token &tok) {
790  // Handle a #pragma found by the Preprocessor.
791 
792  // check whether we care about the pragma - we are a named handler,
793  // thus this could actually be transformed into an assert:
794  if (Introducer != clang::PIK_HashPragma) return; // only #pragma, not C-style.
795  if (!tok.getIdentifierInfo()) return; // must be "link"
796  if (tok.getIdentifierInfo()->getName() != "read") return;
797 
798  PP.Lex(tok);
799  // if (DClient.hasErrorOccured()) {
800  // return;
801  // }
802  if (tok.is(clang::tok::eod)) {
803  Error("Warning - lonely pragma statement: ", tok);
804  return;
805  }
806  const char *start = fSourceManager.getCharacterData(tok.getLocation());
807  clang::Token end;
808  end.startToken(); // Initialize token.
809  while (tok.isNot(clang::tok::eod) && tok.isNot(clang::tok::semi)) {
810  end = tok;
811  PP.Lex(tok);
812  }
813  // Pragma read rule do not need to end in a semi colon
814  // if (tok.isNot(clang::tok::semi)) {
815  // Error("Error: missing ; at end of rule",tok);
816  // return;
817  // }
818  if (end.is(clang::tok::unknown)) {
819  Error("Error: unknown token", tok);
820  } else {
821  llvm::StringRef rule_text(start, fSourceManager.getCharacterData(end.getLocation()) - start + end.getLength());
822 
823  std::string error_string;
824  ROOT::ProcessReadPragma(rule_text.str().c_str(), error_string);
825  if (!error_string.empty())
826  std::cerr << error_string;
827  //std::cerr << "Warning: #pragma read not yet handled: " << include.str() << "\n";
828  // if (!fOwner.AddInclude(include))
829  // {
830  // Error("",tok);
831  // }
832  }
833  }
834 };
835 
836 class PragmaLinkCollector : public LinkdefReaderPragmaHandler {
837  // Handles:
838  // #pragma link [spec] options=... class classname[+-!]
839  //
840 public:
841  PragmaLinkCollector(LinkdefReader &owner, clang::SourceManager &sm) :
842  // This handler only cares about "#pragma link"
843  LinkdefReaderPragmaHandler("link", owner, sm) {
844  }
845 
846  void HandlePragma(clang::Preprocessor &PP,
847  clang::PragmaIntroducerKind Introducer,
848  clang::Token &tok) {
849  // Handle a #pragma found by the Preprocessor.
850 
851  // check whether we care about the pragma - we are a named handler,
852  // thus this could actually be transformed into an assert:
853  if (Introducer != clang::PIK_HashPragma) return; // only #pragma, not C-style.
854  if (!tok.getIdentifierInfo()) return; // must be "link"
855  if (tok.getIdentifierInfo()->getName() != "link") return;
856 
857  PP.Lex(tok);
858 // if (DClient.hasErrorOccured()) {
859 // return;
860 // }
861  if (tok.is(clang::tok::eod)) {
862  Error("Warning - lonely pragma statement: ", tok);
863  return;
864  }
865  bool linkOn;
866  if (tok.isAnyIdentifier()) {
867  if ((tok.getIdentifierInfo()->getName() == "off")) {
868  linkOn = false;
869  } else if ((tok.getIdentifierInfo()->getName() == "C")) {
870  linkOn = true;
871  PP.Lex(tok);
872  if (tok.is(clang::tok::eod) || tok.isNot(clang::tok::plusplus)) {
873  Error("Error ++ expected after '#pragma link C' at ", tok);
874  return;
875  }
876  } else {
877  Error("Error #pragma link should be followed by off or C", tok);
878  return;
879  }
880  } else {
881  Error("Error bad #pragma format. ", tok);
882  return;
883  }
884 
885  PP.Lex(tok);
886  if (tok.is(clang::tok::eod)) {
887  Error("Error no arguments after #pragma link C++/off: ", tok);
888  return;
889  }
890  auto identifier = tok.getIdentifierInfo();
891  if (identifier == nullptr) {
892  if (linkOn) Error("Error #pragma link C++ should be followed by identifier", tok);
893  else Error("Error #pragma link off should be followed by identifier", tok);
894  return;
895  }
896 
897  llvm::StringRef type = identifier->getName();
898 
899  std::unique_ptr<LinkdefReader::Options> options;
900  if (type == "options" || type == "option") {
901  options.reset(new LinkdefReader::Options());
902  if (!ProcessOptions(*options, PP, tok)) {
903  return;
904  }
905  if (tok.getIdentifierInfo()) type = tok.getIdentifierInfo()->getName();
906  }
907 
908  PP.LexUnexpandedToken(tok);
909  const char *start = fSourceManager.getCharacterData(tok.getLocation());
910  clang::Token end;
911  end.startToken(); // Initialize token.
912  while (tok.isNot(clang::tok::eod) && tok.isNot(clang::tok::semi)) {
913  // PP.DumpToken(tok, true);
914  // llvm::errs() << "\n";
915  end = tok;
916  PP.LexUnexpandedToken(tok);
917  }
918 
919  if (tok.isNot(clang::tok::semi)) {
920  Error("Error: missing ; at end of rule", tok, false);
921  return;
922  }
923 
924  if (end.is(clang::tok::unknown)) {
925  if (!fOwner.AddRule(type.data(), "", linkOn, false, options.get())) {
926  Error(type.data(), tok, false);
927  }
928  } else {
929  llvm::StringRef identifier(start, fSourceManager.getCharacterData(end.getLocation()) - start + end.getLength());
930 
931  if (!fOwner.AddRule(type, identifier, linkOn, false, options.get())) {
932  Error(type.data(), tok, false);
933  }
934  }
935 // do {
936 // PP.Lex(tok);
937 // PP.DumpToken(tok, true);
938 // llvm::errs() << "\n";
939 // } while (tok.isNot(clang::tok::eod));
940  }
941 
942 };
943 
944 class PragmaCreateCollector : public LinkdefReaderPragmaHandler {
945 public:
946  PragmaCreateCollector(LinkdefReader &owner, clang::SourceManager &sm) :
947  // This handler only cares about "#pragma create"
948  LinkdefReaderPragmaHandler("create", owner, sm) {
949  }
950 
951  void HandlePragma(clang::Preprocessor &PP,
952  clang::PragmaIntroducerKind Introducer,
953  clang::Token &tok) {
954  // Handle a #pragma found by the Preprocessor.
955 
956  // check whether we care about the pragma - we are a named handler,
957  // thus this could actually be transformed into an assert:
958  if (Introducer != clang::PIK_HashPragma) return; // only #pragma, not C-style.
959  if (!tok.getIdentifierInfo()) return; // must be "link"
960  if (tok.getIdentifierInfo()->getName() != "create") return;
961 
962  PP.Lex(tok);
963  // if (DClient.hasErrorOccured()) {
964  // return;
965  // }
966  if (tok.is(clang::tok::eod)) {
967  Error("Warning - lonely pragma statement: ", tok);
968  return;
969  }
970  if ((tok.getIdentifierInfo()->getName() != "TClass")) {
971  Error("Error: currently only supporting TClass after '#pragma create':", tok);
972  return;
973  }
974 
975  PP.Lex(tok);
976  const char *start = fSourceManager.getCharacterData(tok.getLocation());
977  clang::Token end = tok;
978  while (tok.isNot(clang::tok::eod) && tok.isNot(clang::tok::semi)) {
979  end = tok;
980  PP.Lex(tok);
981  }
982 
983  if (tok.isNot(clang::tok::semi)) {
984  Error("Error: missing ; at end of rule", tok, false);
985  return;
986  }
987 
988  llvm::StringRef identifier(start, fSourceManager.getCharacterData(end.getLocation()) - start + end.getLength());
989 
990  if (!fOwner.AddRule("class", identifier, true, true)) {
991  Error("", tok);
992  }
993 
994 // do {
995 // PP.Lex(tok);
996 // PP.DumpToken(tok, true);
997 // llvm::errs() << "\n";
998 // } while (tok.isNot(clang::tok::eod));
999  };
1000 
1001 };
1002 
1003 
1004 // Parse using clang and its pragma handlers callbacks.
1005 bool LinkdefReader::Parse(SelectionRules &sr, llvm::StringRef code, const std::vector<std::string> &parserArgs, const char *llvmdir)
1006 {
1007  fSelectionRules = &sr;
1008 
1009  std::vector<const char *> parserArgsC;
1010  for (size_t i = 0, n = parserArgs.size(); i < n; ++i) {
1011  parserArgsC.push_back(parserArgs[i].c_str());
1012  }
1013 
1014  // Extract all #pragmas
1015  std::unique_ptr<llvm::MemoryBuffer> memBuf = llvm::MemoryBuffer::getMemBuffer(code, "CLING #pragma extraction");
1016  clang::CompilerInstance *pragmaCI = cling::CIFactory::createCI(std::move(memBuf), parserArgsC.size(),
1017  &parserArgsC[0], llvmdir, nullptr /*Consumer*/,
1018  {} /*ModuleFileExtension*/, true /*OnlyLex*/);
1019 
1020  clang::Preprocessor &PP = pragmaCI->getPreprocessor();
1021  clang::DiagnosticConsumer &DClient = pragmaCI->getDiagnosticClient();
1022  DClient.BeginSourceFile(pragmaCI->getLangOpts(), &PP);
1023 
1024  PragmaLinkCollector pragmaLinkCollector(*this, pragmaCI->getASTContext().getSourceManager());
1025  PragmaCreateCollector pragmaCreateCollector(*this, pragmaCI->getASTContext().getSourceManager());
1026  PragmaExtraInclude pragmaExtraInclude(*this, pragmaCI->getASTContext().getSourceManager());
1027  PragmaIoReadInclude pragmaIoReadInclude(*this, pragmaCI->getASTContext().getSourceManager());
1028 
1029  PP.AddPragmaHandler(&pragmaLinkCollector);
1030  PP.AddPragmaHandler(&pragmaCreateCollector);
1031  PP.AddPragmaHandler(&pragmaExtraInclude);
1032  PP.AddPragmaHandler(&pragmaIoReadInclude);
1033 
1034  // Start parsing the specified input file.
1035  PP.EnterMainSourceFile();
1036  clang::Token tok;
1037  do {
1038  PP.Lex(tok);
1039  } while (tok.isNot(clang::tok::eof));
1040 
1041  fSelectionRules = 0;
1042  return 0 == DClient.getNumErrors();
1043 }