Logo ROOT  
Reference Guide
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"
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
46std::map<std::string, LinkdefReader::EPragmaNames> LinkdefReader::fgMapPragmaNames;
47std::map<std::string, LinkdefReader::ECppNames> LinkdefReader::fgMapCppNames;
48
49struct 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 */
65{
66 if (!(fgMapPragmaNames.empty())) return; // if the map has already been populated, return, else populate it
67
85 // The following are listed here so we can officially ignore them
86 LinkdefReader::fgMapPragmaNames["nestedtypedefs"] = kIgnore;
88}
89
91{
92 if (!(fgMapCppNames.empty())) return; // if the map has already been populated, return, else populate it
93
99}
100
101LinkdefReader::LinkdefReader(cling::Interpreter &interp,
102 ROOT::TMetaUtils::RConstructorTypes &IOConstructorTypes):
103 fLine(1), fCount(0), fSelectionRules(nullptr), fIOConstructorTypesPtr(&IOConstructorTypes), fInterp(interp)
104{
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 */
113bool 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 */
127bool 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
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") {
145 if (linkOn) {
146 vsr.SetAttributeValue("pattern", "*");
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", "*");
157 }
158 }
159 //else vsr.SetSelected(BaseSelectionRule::kNo);
160 //fSelectionRules->AddVariableSelectionRule(vsr);
161
163 if (linkOn) {
165 esr.SetAttributeValue("pattern", "*");
167
168 //EnumSelectionRule esr2; //Problem wih the enums - if I deselect them here
171 esr2.SetAttributeValue("pattern", "*::*");
173 } else {
175 esr.SetAttributeValue("pattern", "*");
178 }
179 }
180 } else if (identifier == "functions" || identifier == "function") {
182 fsr.SetAttributeValue("pattern", "*");
183 if (linkOn) {
186 } else {
190 }
191 }
192 } else if (identifier == "classes" || identifier == "namespaces" ||
193 identifier == "class" || identifier == "namespace") {
194 if (linkOn) {
195
198 csr3.SetAttributeValue("pattern", "__va_*"); // don't generate for the built-in classes/structs
200
202 csr.SetAttributeValue("pattern", "*");
203 csr2.SetAttributeValue("pattern", "*::*");
204 csr.SetSelected(BaseSelectionRule::kYes);
206
209 } else {
212 csr.SetAttributeValue("pattern", "*");
213 csr2.SetAttributeValue("pattern", "*::*");
214
215 csr.SetSelected(BaseSelectionRule::kNo);
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: {
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
243 vsr.SetAttributeValue("pattern", "*");
244 vsr.SetAttributeValue("file_name", localIdentifier);
245 if (linkOn) vsr.SetSelected(BaseSelectionRule::kYes);
248
250 esr.SetAttributeValue("pattern", "*");
251 esr.SetAttributeValue("file_name", localIdentifier);
252 if (linkOn) esr.SetSelected(BaseSelectionRule::kYes);
255
257 fsr.SetAttributeValue("pattern", "*");
258 fsr.SetAttributeValue("file_name", localIdentifier);
259 if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
262
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);
272 } else {
273 csr.SetSelected(BaseSelectionRule::kNo);
275 }
276 csr.SetRequestStreamerInfo(true);
277 csr2.SetRequestStreamerInfo(true);
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 }
291 if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
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 }
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
327 if (linkOn) fsr.SetSelected(BaseSelectionRule::kYes);
329 fsr.SetAttributeValue("proto_pattern", localIdentifier);
331 }
332 break;
333 case kGlobal: {
335 if (linkOn) vsr.SetSelected(BaseSelectionRule::kYes);
337 if (IsPatternRule(identifier)) vsr.SetAttributeValue("pattern", identifier);
338 else vsr.SetAttributeValue("name", identifier);
340 }
341 break;
342 case kEnum: {
343
345 if (linkOn) esr.SetSelected(BaseSelectionRule::kYes);
347 if (IsPatternRule(identifier)) esr.SetAttributeValue("pattern", identifier);
348 else esr.SetAttributeValue("name", identifier);
350 }
351 break;
352 case kClass:
353 case kTypeDef:
354 case kNamespace:
355 case kUnion:
356 case kStruct: {
357 std::string localIdentifier(identifier);
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 '!':
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) {
423
424 if (localIdentifier == "*") { // rootcint generates error here, but I decided to implement it
427 csr2.SetAttributeValue("pattern", "*::*");
429
432 csr3.SetAttributeValue("pattern", "__va_*");
434 }
435 } else {
437 if (localIdentifier == "*") { // rootcint generates error here, but I decided to implement it
440 csr2.SetAttributeValue("pattern", "*::*");
442
443 EnumSelectionRule esr(fCount++, fInterp); // we need this because of implicit/explicit rules - check my notes on rootcint
445 esr.SetAttributeValue("pattern", "*::*");
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
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
497bool 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 */
508bool LinkdefReader::LoadIncludes(std::string &extraIncludes)
509{
510 extraIncludes += fIncludes;
511 return cling::Interpreter::kSuccess == fInterp.declare(fIncludes);
512}
513
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.
569bool 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
631class LinkdefReaderPragmaHandler : public clang::PragmaHandler {
632protected:
633 LinkdefReader &fOwner;
634 clang::SourceManager &fSourceManager;
635public:
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
731class PragmaExtraInclude : public LinkdefReaderPragmaHandler {
732public:
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
780class PragmaIoReadInclude : public LinkdefReaderPragmaHandler {
781public:
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
836class PragmaLinkCollector : public LinkdefReaderPragmaHandler {
837 // Handles:
838 // #pragma link [spec] options=... class classname[+-!]
839 //
840public:
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
944class PragmaCreateCollector : public LinkdefReaderPragmaHandler {
945public:
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.
1005bool LinkdefReader::Parse(SelectionRules &sr, llvm::StringRef code, const std::vector<std::string> &parserArgs, const char *llvmdir)
1006{
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}
void Info(const char *location, const char *msgfmt,...)
void Error(const char *location, const char *msgfmt,...)
void Warning(const char *location, const char *msgfmt,...)
char name[80]
Definition: TGX11.cxx:109
int type
Definition: TGX11.cxx:120
const char * proto
Definition: civetweb.c:16604
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
void SetSelected(ESelect sel)
bool RequestNoStreamer() const
void SetRequestNoInputOperator(bool excl)
bool RequestStreamerInfo() const
void SetRequestProtected(bool val)
void SetRequestedVersionNumber(int version)
void SetRequestPrivate(bool val)
void SetRequestNoStreamer(bool noStreamer)
void SetRequestOnlyTClass(bool val)
void SetRequestStreamerInfo(bool needStreamerInfo)
bool ProcessOperators(std::string &pattern)
bool LoadIncludes(std::string &extraInclude)
SelectionRules * fSelectionRules
Definition: LinkdefReader.h:60
friend class PragmaLinkCollector
Definition: LinkdefReader.h:54
LinkdefReader(cling::Interpreter &interp, ROOT::TMetaUtils::RConstructorTypes &IOConstructorTypes)
bool ProcessFunctionPrototype(std::string &proto, bool &name)
static std::map< std::string, ECppNames > fgMapCppNames
Definition: LinkdefReader.h:94
cling::Interpreter & fInterp
Definition: LinkdefReader.h:63
bool IsPatternRule(const std::string &rule_token)
bool AddInclude(const std::string &include)
std::string fIncludes
Definition: LinkdefReader.h:61
friend class PragmaCreateCollector
Definition: LinkdefReader.h:53
bool Parse(SelectionRules &sr, llvm::StringRef code, const std::vector< std::string > &parserArgs, const char *llvmdir)
bool AddRule(const std::string &ruletype, const std::string &identifier, bool linkOn, bool requestOnlyTClass, Options *option=0)
friend class PragmaExtraInclude
Definition: LinkdefReader.h:56
static std::map< std::string, EPragmaNames > fgMapPragmaNames
Definition: LinkdefReader.h:93
static void PopulatePragmaMap()
static void PopulateCppMap()
ROOT::TMetaUtils::RConstructorTypes * fIOConstructorTypesPtr
Definition: LinkdefReader.h:62
The class representing the collection of selection rules.
void AddVariableSelectionRule(const VariableSelectionRule &varSel)
void AddClassSelectionRule(const ClassSelectionRule &classSel)
bool GetHasFileNameRule() const
void AddEnumSelectionRule(const EnumSelectionRule &enumSel)
void AddFunctionSelectionRule(const FunctionSelectionRule &funcSel)
void SetHasFileNameRule(bool file_rule)
const Int_t n
Definition: legend1.C:16
void ProcessReadPragma(const char *args, std::string &error_string)
I am being called when a read pragma is encountered.
static constexpr double sr