]> rtime.felk.cvut.cz Git - boost-statechart-viewer.git/blob - src/visualizer.cpp
Change commentary style to be accepted by doxygen. Add generated configuration file...
[boost-statechart-viewer.git] / src / visualizer.cpp
1 /** @file */
2 ////////////////////////////////////////////////////////////////////////////////////////  
3 //    
4 //    This file is part of Boost Statechart Viewer.
5 //
6 //    Boost Statechart Viewer is free software: you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation, either version 3 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Boost Statechart Viewer is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License
17 //    along with Boost Statechart Viewer.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 ////////////////////////////////////////////////////////////////////////////////////////
20
21 //standard header files
22 #include <iostream>
23
24 //LLVM Header files
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/Support/Host.h"
27 #include "llvm/Config/config.h"
28
29 //clang header files
30 #include "clang/Frontend/TextDiagnosticPrinter.h"
31 #include "clang/Lex/HeaderSearch.h"
32 #include "clang/Basic/FileManager.h"
33 #include "clang/Frontend/Utils.h"
34 #include "clang/Basic/TargetInfo.h"
35 #include "clang/Lex/Preprocessor.h"
36 #include "clang/Frontend/CompilerInstance.h"
37 #include "clang/AST/ASTConsumer.h"
38 #include "clang/Sema/Lookup.h"
39 #include "clang/Parse/ParseAST.h"
40 #include "clang/Basic/Version.h"
41 #include "clang/Driver/Driver.h"
42 #include "clang/Driver/Compilation.h"
43
44 //my own header files
45 #include "iooper.h"
46
47 using namespace clang;
48 using namespace clang::driver;
49 using namespace std;
50
51 /**
52  * This class provides Simple diagnostic Client. It uses implementation in library for printing diagnostci information.
53  * Also it counts number of warnings, errors, ... When an error occurs the program is stopped.
54  */
55 class MyDiagnosticClient : public TextDiagnosticPrinter
56 {
57         /**
58           * Variables for saving numbers of warnings, errors, ...
59           */
60         int nwarnings;
61         int nnotes;
62         int nignored;
63         int nerrors;
64         public:
65         /**
66          * Initialize number of warnings, errors, ...
67          */
68         MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false)
69         {
70                 nwarnings=0;
71                 nnotes=0;
72                 nignored=0;
73                 nerrors = 0;
74         }
75         /**
76          * This method prints diagnostic and counts diagnostic types.
77          */
78         virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info)
79         {
80                 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information using library implementation
81                 switch (DiagLevel) // count number of all diagnostic information
82                 {
83                         case 0 : nignored+=1; break;
84                         case 1 : nnotes+=1; break;
85                         case 2 : nwarnings+=1; break;
86                         default : nerrors+=1; 
87                                                  print_stats(); 
88                                                  exit(1);
89                 }
90         }
91         /**
92          * Print statistics about diagnostic
93          */
94         void print_stats()
95         {
96                 cout<<"\n--Diagnostic Info--\n";
97                 cout<<"Number of ignored: "<<nignored<<"\n";
98                 cout<<"Number of notes: "<<nnotes<<"\n";
99                 cout<<"Number of warnings: "<<nwarnings<<"\n";
100                 cout<<"Number of errors and fatal errors: "<<nerrors<<"\n";
101         }
102         
103         int getNbrOfWarnings() /** Return number of warnings */
104         {
105                 return nwarnings;               
106         }
107         
108         int getNbrOfNotes() /** Return number of notes */
109         {
110                 return nnotes;          
111         }
112
113         int getNbrOfIgnored() /** Return number of ignored */
114         {
115                 return nignored;                
116         }
117 };
118
119 /**
120  * My ASTConsumer provides interface for traversing AST. It uses recursive traversing in namespaces.
121  */
122 class FindStates : public ASTConsumer
123 {
124         list<string> transitions;
125         list<string> cReactions;
126         list<string> events;
127         list<string> states;
128         string name_of_machine;
129         string name_of_start;
130         FullSourceLoc *fsloc;
131         public:
132
133         list<string> getStates() /** Return list of states. */
134         {
135                 return states;
136         }
137         
138         list<string> getTransitions() /** Return list of transitions. */
139         {
140                 return transitions;
141         }
142                 
143         list<string> getEvents() /** Return list of events. */
144         {
145                 return events;
146         }
147
148         string getStateMachine() /** Return name of the state machine. */
149         {
150                 return name_of_machine;
151         }
152
153         string getNameOfFirstState() /** Return name of start state. */
154         {
155                 return name_of_start;
156         }
157         
158         virtual void Initialize(ASTContext &ctx)/** Run after the AST is constructed before the consumer starts to work. So this function works like constructor. */
159         {       
160                 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
161                 name_of_start = "";
162                 name_of_machine = "";
163         }
164
165 /**
166 *   Traverse global decls using DeclGroupRef for handling all global decls. But only interesting decls are processed. Interesting decls are Struct, Class, Method and Namespace.
167 *   When Namespace is found it recursively traverse all decls inside this Namespace using method recursive_visit.
168 */
169         virtual void HandleTopLevelDecl(DeclGroupRef DGR)
170         {
171                 SourceLocation loc;
172                 string line, output, event;
173                 llvm::raw_string_ostream x(output);
174                 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) 
175                 {
176                         const Decl *decl = *i;
177                         loc = decl->getLocation();
178                         if(loc.isValid())
179                         {
180                                 if(decl->getKind()==35)
181                                 {                                       
182                                         method_decl(decl);
183                                 }
184                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
185                                 {
186                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class 
187                                         {
188                                                 struct_class(decl);
189                                         }
190                                 }       
191                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
192                                 {
193                                         
194                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
195                                         recursive_visit(declCont);
196                                 
197                                 }
198                         }
199                         output = "";
200                 }
201         }
202
203 /**
204 *   It is used to recursive traverse decls in Namespaces. This method do the same as HandleTopLevelDecl.
205 */
206         void recursive_visit(const DeclContext *declCont)
207         {
208                 string line, output, event;
209                 llvm::raw_string_ostream x(output);
210                 SourceLocation loc;
211                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
212                 {
213                         const Decl *decl = *i;
214                         loc = decl->getLocation();
215                         if(loc.isValid())
216                         {       
217                                 if(decl->getKind()==35)
218                                 {
219                                         method_decl(decl);
220                                 }
221                                 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
222                                 {
223                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
224                                         {
225                                                 struct_class(decl);
226                                         }       
227                                 }
228                                 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
229                                 {
230                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);                        
231                                         recursive_visit(declCont);
232                                 }
233                         }
234                         output = "";
235                 } 
236         }
237                 
238 /**
239 *       This function works with class or struct. It splits the decl into 3 interesting parts.
240 *       The state machine decl, state decl and event decl.
241 */
242         void struct_class(const Decl *decl)
243         {
244                 string output, line, ret, trans, event; 
245                 llvm::raw_string_ostream x(output);
246                 decl->print(x);
247                 line = get_line_of_code(x.str());
248                 output = "";
249                 int pos;
250                 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
251                 if(is_derived(line))
252                 {
253                         const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
254                                         
255                         if(find_events(cRecDecl, line))
256                         {
257                                 events.push_back(namedDecl->getNameAsString());
258                         }
259                         else if(name_of_machine == "")
260                         {
261                                 ret = find_name_of_machine(cRecDecl, line);
262                                 if(!ret.empty())
263                                 {
264                                         pos = ret.find(",");
265                                         name_of_machine = ret.substr(0,pos);
266                                         name_of_start = ret.substr(pos+1);
267                                 }
268                         }
269                         else
270                         {
271                                 ret = find_states(cRecDecl, line); 
272                                 if(!ret.empty())
273                                 {
274                                         states.push_back(ret);                  
275                                         methods_in_class(decl,namedDecl->getNameAsString());
276                                 }
277                         }
278                 }
279         }
280
281 /**
282 *       This function provides traversing all methods and other context indide class. If
283 *       typedef or classic method decl is found. Transitions inside it are beiing founded.
284 */
285         void methods_in_class(const Decl *decl, const string state)
286         {
287                 string output, line, ret, trans, event; 
288                 llvm::raw_string_ostream x(output);
289                 int pos, num;
290                 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
291                 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);                      
292                 output="";
293                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) 
294                 {
295                         if (i->getKind()==26) // typedefs
296                         {
297                                 i->print(x);
298                                 output = x.str();
299                                 line = clean_spaces(cut_type(output));          
300                                 ret = find_transitions(state,line);
301                                 if(!ret.empty()) 
302                                 {
303                                         num = count(ret,';')+1;
304                                         for(int i = 0;i<num;i++)
305                                         {
306                                                 pos = ret.find(";");
307                                                 if(pos == 0)
308                                                 {
309                                                         ret = ret.substr(1);
310                                                         pos = ret.find(";");
311                                                         if(pos==-1) cReactions.push_back(ret);
312                                                         else cReactions.push_back(ret.substr(0,pos));   
313                                                         num-=1;
314                                                 }
315                                                 else 
316                                                 {
317                                                         if(pos==-1) transitions.push_back(ret);
318                                                         else transitions.push_back(ret.substr(0,pos));
319                                                 }
320                                                 if(i!=num-1) ret = ret.substr(pos+1);
321                                         }
322                                         output="";
323                                 }
324                         }
325                         if(i->getKind()==35) method_decl(decl);// C++ method
326                 }
327         }
328
329         /**
330          * Traverse Method declaration using classes Stmt.
331          */
332         void method_decl(const Decl *decl)
333         {
334                 string output, line, event;     
335                 llvm::raw_string_ostream x(output);
336                 if(decl->hasBody())
337                 {
338                         decl->print(x);
339                         line = get_return(x.str());
340                         if(test_model(line,"result"))
341                         {
342                                 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
343                                 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
344                                 QualType qt = pvd->getOriginalType();
345                                 event = qt.getAsString();
346                                 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
347                                 event = event.substr(event.rfind(" ")+1);
348                                 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
349                                 line = cut_namespaces(line.substr(0,line.rfind("::")));
350                                 line.append(",");
351                                 line.append(event);
352                                 find_return_stmt(decl->getBody(),line); 
353                                 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++) // erase info about it from list of custom reactions
354                                 {
355                                         event = *i;
356                                         if(line.compare(event)==0) 
357                                         {
358                                                 cReactions.erase(i);
359                                                 break;
360                                         }
361                                 }
362                         }
363                 }
364         }
365
366         void find_return_stmt(Stmt *statemt,string event) /** Traverse all statements in function for finding return Statement*/
367         {
368                 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
369                 else
370                 {
371                         for (Stmt::child_range range = statemt->children(); range; ++range)    
372                         {
373                                 test_stmt(*range, event);
374                         }
375                 }
376         }
377         
378         void test_stmt(Stmt *stmt, string event) /** test statement for its kind*/
379         {
380                 const SourceManager &sman = fsloc->getManager();
381                 int type;
382                 string line, param;
383                 type = stmt->getStmtClass();
384                 switch(type)
385                 {       
386                         case 8 :                find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
387                                                         break;
388                         case 86 :       find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
389                                                         break;
390                         case 88 :   find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
391                                                         find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
392                                                         break;
393                         case 90 :       find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
394                                                         break;
395                         case 98 :       line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc()); 
396                                                         line = get_line_of_code(line).substr(6);
397                                                         line = line.substr(0,line.find("("));
398                                                         if(test_model(line,"transit"))
399                                                         {
400                                                                 param = get_params(line);
401                                                                 transitions.push_back(event.append(",").append(param));
402                                                         }
403                                                         break;
404                         case 99 :       find_return_stmt(stmt, event);
405                                                         break;
406                         case 101 :      find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
407                                                         break;
408                         case 102 :      find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
409                                                         break;
410                 }
411         }
412
413 };
414 /**
415   * Main function provides all initialization before starting analysis of AST. Diagnostic Client is initialized,
416   * Command line options are processed.
417   */
418 int main(int argc, char **argv)
419
420         string inputFilename = "";
421         string outputFilename = "graph.dot"; // initialize output Filename
422         DiagnosticOptions dopts;
423         dopts.ShowColors=1;
424         MyDiagnosticClient *mdc = new MyDiagnosticClient(llvm::errs(), dopts);
425         llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());       
426         Diagnostic diag(dis,mdc);
427         FileManager fm( * new FileSystemOptions());
428         SourceManager sm (diag, fm);
429         HeaderSearch *headers = new HeaderSearch(fm);
430         
431         Driver TheDriver(LLVM_BINDIR, llvm::sys::getHostTriple(), "", false, false, diag);
432         TheDriver.setCheckInputsExist(true);
433         TheDriver.CCCIsCXX = 1; 
434         TheDriver.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
435
436         CompilerInvocation compInv;
437         llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
438         llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
439                                                             Args.data()));
440         const driver::JobList &Jobs = C->getJobs();
441         const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
442         const driver::ArgStringList &CCArgs = Cmd->getArguments();
443         for(unsigned i = 0; i<Args.size();i++) // find -o in ArgStringList
444         {       
445                 if(strncmp(Args[i],"-o",2)==0) 
446                 {
447                         if(strlen(Args[i])>2)
448                         {
449                                 string str = Args[i];
450                                 outputFilename = str.substr(2);
451                         }
452                         else outputFilename = Args[i+1];
453                         break;
454                 }
455         }
456                 
457         CompilerInvocation::CreateFromArgs(compInv,
458                                           const_cast<const char **>(CCArgs.data()),
459                                           const_cast<const char **>(CCArgs.data())+CCArgs.size(),
460                                           diag);
461
462         HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts();
463         LangOptions lang = compInv.getLangOpts();
464         CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
465         TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts());
466         FrontendOptions f = compInv.getFrontendOpts();
467         inputFilename = f.Inputs[0].second;
468
469         cout<<"Input filename: "<<inputFilename<<"\n"; // print Input filename
470         cout<<"Output filename: "<<outputFilename<<"\n"; // print Output filename
471
472
473         Preprocessor pp(diag, lang, *ti, sm, *headers);
474         pp.getBuiltinInfo().InitializeBuiltins(pp.getIdentifierTable(), lang);
475                 
476         InitializePreprocessor(pp, compInv.getPreprocessorOpts(),hsopts,f);
477         
478         const FileEntry *file = fm.getFile(inputFilename);
479         sm.createMainFileID(file);
480         IdentifierTable tab(lang);
481         Builtin::Context builtins(*ti);
482         FindStates c;
483         ASTContext ctx(lang, sm, *ti, tab, * new SelectorTable(), builtins,0);
484         mdc->BeginSourceFile(lang, &pp);//start using diagnostic
485         ParseAST(pp, &c, ctx, false, false);
486         mdc->EndSourceFile(); //end using diagnostic
487         IO_operations *io = new IO_operations(outputFilename, c.getStateMachine(), c.getNameOfFirstState(), c.getTransitions(), c.getStates(), c.getEvents());
488         io->save_to_file();
489         io->print_stats();
490         mdc->print_stats();
491         return 0;
492 }