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