]> rtime.felk.cvut.cz Git - boost-statechart-viewer.git/blob - src/visualizer.cpp
Updated source files. Creation of new function for better looking code.
[boost-statechart-viewer.git] / src / visualizer.cpp
1 //standard header files
2 #include <iostream>
3 #include <string>
4 #include <fstream>
5 #include <list>
6
7 //LLVM Header files
8 #include "llvm/Support/raw_ostream.h"
9 #include "llvm/Support/Host.h"
10 #include "llvm/Config/config.h"
11
12 //clang header files
13 #include "clang/Frontend/TextDiagnosticPrinter.h"
14 #include "clang/Lex/HeaderSearch.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Frontend/Utils.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Lex/Preprocessor.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/AST/ASTConsumer.h"
21 #include "clang/Sema/Lookup.h"
22 #include "clang/Parse/ParseAST.h"
23 #include "clang/Basic/Version.h"
24 #include "clang/Driver/Driver.h"
25 #include "clang/Driver/Compilation.h"
26
27 //my own header files
28 #include "stringoper.h"
29
30 using namespace clang;
31 using namespace clang::driver;
32 using namespace std;
33
34 class MyDiagnosticClient : public TextDiagnosticPrinter // My diagnostic Client
35 {
36         public:
37         MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false){}
38         virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info)
39         {
40                 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information
41                 if(DiagLevel > 2) // if error/fatal error stop the program
42                 {               
43                         exit(1);
44                 }       
45         }
46 };
47
48 class FindStates : public ASTConsumer
49 {
50         list<string> transitions;
51         list<string> cReactions;
52         list<string> events;
53         string name_of_machine;
54         string name_of_start;
55         int nbrStates;
56         FullSourceLoc *fsloc;
57         public:
58         list<string> states;
59         
60         virtual void Initialize(ASTContext &ctx)//run after the AST is constructed before the consumer starts to work
61         {       
62                 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
63                 name_of_start = "";
64                 name_of_machine = "";
65                 nbrStates = 0;
66         }
67
68         virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations
69         {
70                 SourceLocation loc;
71       string line, output, event;
72                 llvm::raw_string_ostream x(output);
73                 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) 
74                 {
75                         const Decl *decl = *i;
76                         loc = decl->getLocation();
77                         if(loc.isValid())
78                         {
79                                 //cout<<decl->getKind()<<"ss\n";
80                                 if(decl->getKind()==35)
81                                 {                                       
82                                         method_decl(decl);
83                                 }
84                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
85                                 {
86                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class 
87                                         {
88                                                 struct_class(decl);
89                                         }
90                                 }       
91                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
92                                 {
93                                         
94                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
95                                         //cout<<namedDecl->getNameAsString()<<"   sss\n";
96                                         recursive_visit(declCont);
97                                 
98                                 }
99                         }
100                         output = "";
101                 }
102         }
103         void recursive_visit(const DeclContext *declCont) //recursively visit all decls hidden inside namespaces
104         {
105       string line, output, event;
106                 llvm::raw_string_ostream x(output);
107                 SourceLocation loc;
108                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
109                 {
110                         const Decl *decl = *i;
111                         //std::cout<<"a "<<decl->getDeclKindName()<<"\n";
112                         loc = decl->getLocation();
113                         if(loc.isValid())
114                         {       
115                                 if(decl->getKind()==35)
116                                 {
117                                         method_decl(decl);
118                                 }
119                                 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
120                                 {
121                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
122                                         {
123                                                 struct_class(decl);
124                                         }       
125                                 }
126                                 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
127                                 {
128                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
129                                         //cout<<namedDecl->getNameAsString()<<"  sss\n";                        
130                                         recursive_visit(declCont);
131                                 }
132                         }
133                         output = "";
134                 } 
135         }
136                 
137         void struct_class(const Decl *decl) // works with struct or class decl
138         {
139                 string output, line, ret, trans, event; 
140                 llvm::raw_string_ostream x(output);
141                 decl->print(x);
142                 line = get_line_of_code(x.str());
143                 output = "";
144                 int pos;
145                 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);         
146                 if(is_derived(line))
147                 {
148                         const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
149                                         
150                         if(find_events(cRecDecl, line))
151                         {
152                                 events.push_back(namedDecl->getNameAsString());
153                                 cout<<"New event: "<<namedDecl->getNameAsString()<<"\n";
154                         }
155                         else if(name_of_machine == "")
156                         {
157                                 ret = find_name_of_machine(cRecDecl, line);
158                                 if(!ret.empty())
159                                 {
160                                         pos = ret.find(",");
161                                         name_of_machine = ret.substr(0,pos);
162                                         name_of_start = ret.substr(pos+1);
163                                         cout<<"Name of the state machine: "<<name_of_machine<<"\n";
164                                         cout<<"Name of the first state: "<<name_of_start<<"\n";
165                                 }
166                         }
167                         else
168                         {
169                                 ret = find_states(cRecDecl, line);      
170                                 if(!ret.empty())
171                                 {       
172                                         cout << "New state: " << namedDecl->getNameAsString() << "\n";
173                                         states.push_back(ret);                  
174                                         methods_in_class(decl,namedDecl->getNameAsString());
175                                 }
176                         }
177                 }
178         }
179
180         void methods_in_class(const Decl *decl, const string state)
181         {
182                 string output, line, ret, trans, event; 
183                 llvm::raw_string_ostream x(output);
184                 int pos, num;
185                 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
186                 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);              
187                 //states.push_back(namedDecl->getNameAsString());
188                 
189                 output="";
190                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) 
191                 {
192                         if (i->getKind()==26) 
193                         {
194                                 i->print(x);
195                                 output = x.str();
196                                 line = clean_spaces(cut_type(output));          
197                                 ret = find_transitions(state,line);
198                                 if(!ret.empty()) 
199                                 {
200                                         num = count(ret,';')+1;
201                                         for(int i = 0;i<num;i++)
202                                         {
203                                                 pos = ret.find(";");
204                                                 if(pos == 0)
205                                                 {
206                                                         ret = ret.substr(1);
207                                                         pos = ret.find(";");
208                                                         if(pos==-1) cReactions.push_back(ret);
209                                                         else cReactions.push_back(ret.substr(0,pos));   
210                                                         num-=1;
211                                                 }
212                                                 else 
213                                                 {
214                                                         if(pos==-1) transitions.push_back(ret);
215                                                         else transitions.push_back(ret.substr(0,pos));
216                                                 }
217                                                 //cout<<ret<<"\n";
218                                                 if(i!=num-1) ret = ret.substr(pos+1);
219                                         }
220                                         output="";
221                                 }
222                         }
223                         if(i->getKind()==35) method_decl(decl);
224                 }
225         }
226
227         void method_decl(const Decl *decl)
228         {
229                 string output, line, event;     
230                 llvm::raw_string_ostream x(output);
231                 if(decl->hasBody())
232                 {
233                         decl->print(x);
234                         line = get_return(x.str());
235                         if(test_model(line,"result"))
236                         {
237                                 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
238                                 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
239                                 QualType qt = pvd->getOriginalType();                           
240                                 event = qt.getAsString();
241                                 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
242                                 event = event.substr(event.rfind(" ")+1);
243                                 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
244                                 line = cut_namespaces(line.substr(0,line.rfind("::")));
245                                 line.append(",");
246                                 line.append(event);
247                                 find_return_stmt(decl->getBody(),line); 
248                                 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++)
249                                 {
250                                         event = *i;
251                                         if(line.compare(event)==0) 
252                                         {
253                                                 cReactions.erase(i);
254                                                 break;
255                                         }
256                                 }
257                         }
258                 }
259         }
260
261         void find_return_stmt(Stmt *statemt,string event)
262         {
263                 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
264                 else
265                 {
266                         for (Stmt::child_range range = statemt->children(); range; ++range)    
267                         {
268                                 test_stmt(*range, event);
269                         }
270                 }
271         }
272         
273         void test_stmt(Stmt *stmt, string event)
274         {
275                 const SourceManager &sman = fsloc->getManager();
276                 int type;
277                 string line, param;
278                 type = stmt->getStmtClass();
279                 switch(type)
280                 {       
281                         case 8 :                find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
282                                                         break;
283                         case 86 :       find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
284                                                         break;
285                         case 88 :   find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
286                                                         find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
287                                                         break;
288                         case 90 :       find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
289                                                         break;
290                         case 98 :       line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc()); 
291                                                         line = get_line_of_code(line).substr(6);
292                                                         line = line.substr(0,line.find("("));
293                                                         if(test_model(line,"transit"))
294                                                         {
295                                                                 param = get_params(line);
296                                                                 transitions.push_back(event.append(",").append(param));
297                                                         }
298                                                         break;
299                         case 99 :       find_return_stmt(stmt, event);
300                                                         break;
301                         case 101 :      find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
302                                                         break;
303                         case 102 :      find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
304                                                         break;
305                         }
306         }
307
308         void save_to_file(string output) // save all to the output file
309         {
310                 nbrStates = states.size();
311                 string state, str, context, ctx;
312                 int pos1, pos2, cnt, subs;
313                 ofstream filestr(output.c_str());
314                 //std::cout<<output<<"\n";
315                 filestr<<"digraph "<< name_of_machine<< " {\n";
316                 context = name_of_machine;
317                 for(list<string>::iterator i = states.begin();i!=states.end();i++) // write all states in the context of the automaton
318                 {
319                         state = *i;
320                         cnt = count(state,',');
321                         if(cnt==1)
322                         {
323                                 pos1 = state.find(",");
324                                 ctx = cut_namespaces(state.substr(pos1+1));
325                                 //std::cout<<name_of_machine.length();                          
326                                 if(ctx.compare(0,context.length(),context)==0)
327                                 {
328                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
329                                         states.erase(i);
330                                         i--;
331                                 }
332                         }
333                         if(cnt==2)
334                         {
335                                 pos1 = state.find(",");
336                                 pos2 = state.rfind(",");
337                                 ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
338                                 //std::cout<<ctx<<" "<<context<<"\n";
339                                 if(ctx.compare(0,context.length(),context)==0)
340                                 {                               
341                                         filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
342                                 }
343                         }
344                 }
345                 filestr<<name_of_start<<" [peripheries=2] ;\n";
346                 subs = 0;
347                 while(!states.empty()) // substates ?
348                 {
349                         state = states.front();
350                         filestr<<"subgraph cluster"<<subs<<" {\n";                      
351                         pos1 = state.find(",");
352                         pos2 = state.rfind(",");
353                         context = cut_namespaces(state.substr(0,pos1));
354                         filestr<<"label=\""<<context<<"\";\n";
355                         filestr<<cut_namespaces(state.substr(pos2+1))<<" [peripheries=2] ;\n";  
356                         states.pop_front();     
357                         //std::cout<<states.size();     
358                         for(list<string>::iterator i = states.begin();i!=states.end();i++)
359                         {
360                                 state = *i;
361                                 cnt = count(state,',');
362                                 //std::cout<<state<<" \n";
363                                 if(cnt==1)
364                                 {
365                                         pos1 = state.find(",");
366                                         ctx = cut_namespaces(state.substr(pos1+1));
367                                         if(ctx.compare(0,context.length(),context)==0)
368                                         {
369                                                 filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
370                                                 states.erase(i);
371                                                 i--;
372                                         }
373                                 }
374                                 if(cnt==2)
375                                 {
376                                         pos1 = state.find(",");
377                                         pos2 = state.rfind(",");
378                                         ctx = cut_namespaces(state.substr(pos1+1,pos2-pos1-1));
379                                         if(ctx.compare(0,context.length(),context)==0) filestr<<cut_namespaces(state.substr(0,pos1))<<";\n";
380                                 }
381                         }
382                         filestr<<"}\n";
383                         subs+=1;        
384                 }               
385                 for(list<string>::iterator i = transitions.begin();i!=transitions.end();i++) // write all transitions
386                 {
387                         state = *i;
388                         pos1 = state.find(",");
389                         filestr<<cut_namespaces(state.substr(0,pos1))<<"->";
390                         pos2 = state.rfind(",");
391                         filestr<<cut_namespaces(state.substr(pos2+1));
392                         filestr<<"[label=\""<<cut_namespaces(state.substr(pos1+1,pos2-pos1-1))<<"\"];\n";
393                 }               
394                 filestr<<"}";
395                 filestr.close();
396         }
397         void print_stats() // print statistics
398         {
399                 cout<<"\n"<<"Statistics: \n";
400                 cout<<"Number of states: "<<nbrStates<<"\n";
401                 cout<<"Number of events: "<<events.size()<<"\n";
402                 cout<<"Number of transitions: "<<transitions.size()<<"\n";
403                 return;
404         }
405
406 };
407
408 int main(int argc, char **argv)
409
410         string inputFilename = "";
411         string outputFilename = "graph.dot"; // initialize output Filename
412         MyDiagnosticClient *mdc = new MyDiagnosticClient(llvm::errs(), * new DiagnosticOptions());
413         llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());       
414         Diagnostic diag(dis,mdc);
415         FileManager fm( * new FileSystemOptions());
416         SourceManager sm (diag, fm);
417         HeaderSearch *headers = new HeaderSearch(fm);
418         
419         Driver TheDriver(LLVM_PREFIX "/bin", llvm::sys::getHostTriple(), "", false, false, diag);
420         TheDriver.setCheckInputsExist(true);
421         TheDriver.CCCIsCXX = 1; 
422         CompilerInvocation compInv;
423         llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
424         llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
425                                                             Args.data()));
426         const driver::JobList &Jobs = C->getJobs();
427         const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
428         const driver::ArgStringList &CCArgs = Cmd->getArguments();
429         for(unsigned i = 0; i<Args.size();i++) // find -o in ArgStringList
430         {       
431                 if(strncmp(Args[i],"-o",2)==0) 
432                 {
433                         if(strlen(Args[i])>2)
434                         {
435                                 string str = Args[i];
436                                 outputFilename = str.substr(2);
437                         }
438                         else outputFilename = Args[i+1];
439                         break;
440                 }
441         }
442                 
443         CompilerInvocation::CreateFromArgs(compInv,
444                                           const_cast<const char **>(CCArgs.data()),
445                                           const_cast<const char **>(CCArgs.data())+CCArgs.size(),
446                                           diag);
447
448         HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts();
449         hsopts.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
450         LangOptions lang = compInv.getLangOpts();
451         CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
452         TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts());
453         ApplyHeaderSearchOptions(*headers, hsopts, lang, ti->getTriple());
454         FrontendOptions f = compInv.getFrontendOpts();
455         inputFilename = f.Inputs[0].second;
456
457         cout<<"Input filename: "<<inputFilename<<"\n"; // print Input filename
458         cout<<"Output filename: "<<outputFilename<<"\n"; // print Output filename
459
460
461         Preprocessor pp(diag, lang, *ti, sm, *headers);
462         pp.getBuiltinInfo().InitializeBuiltins(pp.getIdentifierTable(), lang);
463                 
464         InitializePreprocessor(pp, compInv.getPreprocessorOpts(),hsopts,f);
465         
466         const FileEntry *file = fm.getFile(inputFilename);
467         sm.createMainFileID(file);
468         IdentifierTable tab(lang);
469         Builtin::Context builtins(*ti);
470         FindStates c;
471         ASTContext ctx(lang, sm, *ti, tab, * new SelectorTable(), builtins,0);
472         mdc->BeginSourceFile(lang, &pp);//start using diagnostic
473         ParseAST(pp, &c, ctx, false, false);
474         mdc->EndSourceFile(); //end using diagnostic
475         if(c.states.size()>0) c.save_to_file(outputFilename);
476         else cout<<"No state machine was found\n";
477         c.print_stats();
478         return 0;
479 }