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